snoutdata 0.10.0 → 0.10.2

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,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var ii=Object.defineProperty;var M=(e,t)=>()=>(e&&(t=e(e=0)),t);var fr=(e,t)=>{for(var n in t)ii(e,n,{get:t[n],enumerable:!0})};function pr(e){let t=[],n={};for(let r=0;r<e.length;r++){let o=e[r];if(!o.startsWith("-")){t.push(o);continue}if(o==="--"){t.push(...e.slice(r+1));break}let[s,a]=ui(o);if(ci.has(s)){let u=a??e[++r];if(u===void 0)throw new y(`--${s} needs a value`);n[s]=u;continue}if(a!==void 0)throw new y(`--${s} does not take a value`);if(!li.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function ui(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function O(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function F(e,t){return e.flags[t]===!0}function _e(e,t){let n=O(e,t);if(n===void 0)return;let r=Number(n);if(!Number.isInteger(r))throw new y(`--${t} must be a whole number, not "${n}"`);return r}var y,ci,li,ne=M(()=>{"use strict";y=class extends Error{},ci=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","memory","concurrency","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment"]);li=new Set(["json","window","help","version","force","no-verify-jwt","reset","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history"])});import{chmodSync as di,existsSync as gr,mkdirSync as hr,readFileSync as yr,rmSync as mi,writeFileSync as wr}from"node:fs";import{homedir as mt}from"node:os";import{dirname as Xt,join as br,parse as fi,resolve as pi}from"node:path";function Zt(e=mt()){return br(e,".snoutdata","auth.json")}function Sr(e=mt()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=Zt(e);if(!gr(n))return null;try{let r=JSON.parse(yr(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function ae(e,t=mt()){let n=Zt(t);hr(Xt(n),{recursive:!0}),wr(n,`${JSON.stringify(e,null,2)}
3
- `,{mode:384}),di(n,384)}function xr(e=mt()){mi(Zt(e),{force:!0})}function _r(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function kr(e){return br(e,".snoutdata","project.json")}function qe(e){let t=pi(e),n=fi(t).root;for(;;){let r=kr(t);if(gr(r))try{let o=JSON.parse(yr(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return typeof o.local=="string"&&o.local?{ref:o.ref,directory:t,local:o.local}:{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=Xt(t)}}function Ge(e,t){let n=kr(e);return hr(Xt(n),{recursive:!0}),wr(n,`${JSON.stringify(t,null,2)}
4
- `),n}function Ke(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:qe(e.cwd??process.cwd())?.ref??null}var ke=M(()=>{"use strict"});import{existsSync as Qt,readFileSync as vr}from"node:fs";import{homedir as gi}from"node:os";import{isAbsolute as hi,join as me,resolve as Te}from"node:path";function yi(e=process.env,t=process.platform,n=gi()){let r;return t==="win32"?r=e.APPDATA??me(n,"AppData","Roaming"):t==="darwin"?r=me(n,"Library","Application Support"):r=e.XDG_CONFIG_HOME??me(n,".config"),["snoutdata","snoutdata-dev"].map(o=>me(r,o,"localProjects.json"))}function ft(e=yi()){let t=[];for(let n of e)if(Qt(n))try{let r=JSON.parse(vr(n,"utf8"));for(let o of r.projects??[])typeof o.folder=="string"&&o.folder&&!t.some(s=>s.folder===o.folder)&&t.push({name:o.name??null,folder:o.folder,ref:o.ref??null})}catch{}return t}function pt(e){let t=new Map;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim(),a=r.slice(o+1).trim();a.length>=2&&(a[0]==='"'||a[0]==="'")&&a.endsWith(a[0])&&(a=a.slice(1,-1)),t.set(s,a)}return t}function We(e){return Qt(me(e,"compose.yaml"))&&Qt(me(e,".env"))}function fe(e,t=null){let n=Te(e);if(!We(n))throw new Error(`${n} is not a SnoutData stack folder: it needs compose.yaml and .env (see github.com/snoutdata/snout-stack)`);let r=pt(vr(me(n,".env"),"utf8")),o=r.get("SNOUT_REF"),s=r.get("POSTGRES_OWNER_PASSWORD");if(!o||!s)throw new Error(`${me(n,".env")} has no ${o?"POSTGRES_OWNER_PASSWORD":"SNOUT_REF"}, so this stack was not set up with \`snout-stack init\``);let a=Number(r.get("API_PORT")??8e3)||8e3,u=Number(r.get("DB_PORT")??5432)||5432;return{folder:n,ref:o,name:t,apiUrl:(r.get("API_EXTERNAL_URL")||`http://127.0.0.1:${a}`).replace(/\/$/,""),apiPort:a,dbPort:u,anonKey:r.get("ANON_KEY")||null,serviceRoleKey:r.get("SERVICE_ROLE_KEY")||null,ownerPassword:s}}function Er(e,t=process.cwd(),n=ft()){if(!e){if(We(t))return fe(t);let[i]=n;if(n.length===1&&i)return fe(i.folder,i.name);throw n.length===0?new Error("there is no local project to link: set one up in Studio (Projects, Local), or pass the folder of a stack you made with snout-stack"):new Error(`there are ${n.length} local projects; name one: ${n.map(m=>m.name??m.folder).join(", ")}`)}let r=hi(e)?e:Te(t,e);if(We(r)){let i=n.find(m=>Te(m.folder)===Te(r));return fe(r,i?.name??null)}let o=e.toLowerCase(),s=n.filter(i=>i.name?.toLowerCase()===o||i.ref===e),[a]=s;if(s.length===1&&a)return fe(a.folder,a.name);let u=n.map(i=>i.name??i.folder);throw new Error(s.length>1?`more than one local project is called ${e}; link it by its folder`:`no local project called ${e}, and it is not a stack folder${u.length?`. The local projects are ${u.join(", ")}`:""}`)}function Pr(e){let t=`${e.ref}_owner`;return{ref:e.ref,host:"127.0.0.1",port:e.dbPort,database:e.ref,user:t,password:e.ownerPassword,ssl:"disable",uri:`postgresql://${t}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.dbPort}/${e.ref}`,anonKey:e.anonKey,serviceRoleKey:e.serviceRoleKey,keysIssuedAt:null,keysExpireAt:null,wakesInstantly:!0,state:"local"}}function $r(e,t,n=ft()){if(t&&We(t))try{let r=n.find(s=>Te(s.folder)===Te(t)),o=fe(t,r?.name??null);if(o.ref===e)return o}catch{}for(let r of n)if(r.ref===e&&We(r.folder))return fe(r.folder,r.name);return null}var Je=M(()=>{"use strict"});function A(e,t,n){throw new L(e,t,n)}function gt(e){return e===403?"forbidden":e===404?"not-found":e===409?"conflict":e===402||e===429?"quota":e===503?"not-ready":e===504?"timeout":e>=500?"server":"failed"}function Tr(e){let t=e;return!t||typeof t!="object"||t.code!=="ENOENT"||typeof t.path!="string"||t.syscall==="spawn"?null:`no file at ${t.path}`}function Ar(e){let t=[e],n=e;for(let r=0;r<4&&n instanceof Error&&n.cause;r+=1)n=n.cause,t.push(n);for(let r of t){let o=typeof r=="object"&&r&&"code"in r?String(r.code):"";if(wi.has(o))return"network";if(o==="ABORT_ERR"||o==="ETIMEDOUT")return"timeout"}return e instanceof Error&&/fetch failed|network|socket hang up/i.test(e.message)?"network":"failed"}var ie,L,wi,Y=M(()=>{"use strict";ie={ok:0,failed:1,usage:2,"not-signed-in":3,"not-ready":4,forbidden:5,"not-found":6,conflict:7,quota:8,network:9,timeout:10,"tool-missing":127,server:1},L=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return ie[this.code]}};wi=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});function ht(e){let t=e.env.SNOUTDATA_NO_INTERACTIVE;if(t!==void 0&&t!==""&&t!=="0"&&t!=="false")return{canAsk:!1,reason:"opted-out"};if(e.json)return{canAsk:!1,reason:"json"};for(let n of bi){let r=e.env[n];if(r!==void 0&&r!==""&&r!=="0"&&r!=="false")return{canAsk:!1,reason:"ci"}}return e.isTty?{canAsk:!0}:{canAsk:!1,reason:"not-a-tty"}}function Ae(e){switch(e){case"json":return"running with --json, so nothing was asked";case"opted-out":return"SNOUTDATA_NO_INTERACTIVE is set, so nothing was asked";case"ci":return"this looks like CI, so nothing was asked";case"not-a-tty":return"there is no terminal to ask at"}}function Rr(e){en=e}function pe(){return(en??ht({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function Re(){return(en??ht({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var bi,en,Oe=M(()=>{"use strict";bi=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];en=null});import{existsSync as Si,readFileSync as xi}from"node:fs";import{homedir as tn,hostname as _i}from"node:os";import{join as Ne}from"node:path";function ki(e,t,n){if(t==="win32"){let r=n.APPDATA??Ne(tn(),"AppData","Roaming");return Ne(r,e)}return t==="darwin"?Ne(tn(),"Library","Application Support",e):Ne(n.XDG_CONFIG_HOME??Ne(tn(),".config"),e)}function Nr(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>Ne(ki(n,e,t),"mcp.json"))}function vi(e=process.platform,t=process.env){return Nr(e,t)[0]}function Ei(e=vi()){if(!Si(e))return null;try{let t=JSON.parse(xi(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function Pi(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function yt(e){if(e!==void 0)return Or(e);let t="no-config";for(let n of Nr()){let r=Ei(n);if(!r)continue;let o=await Or(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function Or(e){let t=Pi(e);if(t)return{available:!1,skip:t};let n=e,r;try{let o=await fetch(`http://127.0.0.1:${n.port}/cli/whoami`,{headers:{Authorization:`Bearer ${n.token}`},signal:AbortSignal.timeout($i)});if(!o.ok)return{available:!1,skip:"not-running"};r=await o.json()}catch{return{available:!1,skip:"not-running"}}return r.signedIn?r.canMintToken?{available:!0,who:r,config:n}:{available:!1,skip:"cannot-mint"}:{available:!1,skip:"signed-out"}}async function Dr(e,t){let n=await fetch(`http://127.0.0.1:${e.port}/cli/token`,{method:"POST",headers:{Authorization:`Bearer ${e.token}`,"Content-Type":"application/json"},body:JSON.stringify({client:"snoutdata-cli",command:t}),signal:AbortSignal.timeout(6e4)}),r=await n.json().catch(()=>({}));return n.ok&&r.ok&&r.token?{token:r.token,name:r.name??"SnoutData CLI"}:{declined:!0,reason:r.error??`SnoutData Studio refused (${n.status})`}}function wt(){return _i()}async function nn(e,t,n,r){let o=await fetch(`http://127.0.0.1:${e.port}/mcp`,{method:"POST",headers:{Authorization:`Bearer ${e.token}`,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:t,params:n}),signal:AbortSignal.timeout(r)});if(!o.ok)throw new Error(`the app answered ${o.status}`);let s=await o.json();if(s.error)throw new Error(s.error.message??"SnoutData Studio refused");return s.result}async function Mr(e){return await nn(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await nn(e,"tools/list",{},3e3)).tools??[]}async function Ir(e,t,n){return nn(e,"tools/call",{name:t,arguments:n},12e4)}var $i,bt=M(()=>{"use strict";$i=1500});async function jr(e,t){let n=await fetch(`${re}/functions/v1/${e}`,{method:"POST",headers:{apikey:ce,"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return{status:n.status,body:r}}async function St(e={}){let{status:t,body:n}=await jr("cloud-device-start",{client:e});return t===429&&A("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&A("not-found","this SnoutData does not have the pairing flow (cloud-device-start is not deployed). Use `snoutdata login` to sign in with a browser, or set SNOUTDATA_ACCESS_TOKEN."),(t!==200||!n.code||!n.secret)&&A("server",n.error??`could not start a pairing (${t})`),n}async function xt(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&A("timeout","that pairing code expired before it was approved. Run the command again for a new one."),await new Promise(a=>setTimeout(a,n));let{status:o,body:s}=await jr("cloud-device-poll",{secret:e});switch(o===404&&A("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&A("usage",s.error??"that is not a pairing secret"),o!==200&&A("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":A("forbidden",s.error??"that pairing was refused in the browser");case"used":A("conflict",s.error??"that pairing has already been completed");case"expired":A("timeout",s.error??"that pairing code expired before it was approved");case"slow-down":n=Math.max(n*2,(s.interval??2)*1e3);break;default:s.interval&&(n=Math.max(n,s.interval*1e3));break}t.onWait?.(Math.max(0,Math.round((r-Date.now())/1e3)))}}var rn=M(()=>{"use strict";H();Y()});function ze(e){_t=e}function Ur(e){Lr=e}function b(e){return Fr?`\x1B[2m${e}\x1B[0m`:e}function P(e){return Fr?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(_t){process.stdout.write(`${JSON.stringify(e,null,2)}
5
- `);return}t()}function c(e){!_t&&!Lr&&process.stderr.write(`${e}
6
- `)}function ge(e,t,n){process.stderr.write(`${t}
7
- `),_t&&process.stdout.write(`${JSON.stringify({ok:!1,code:e,error:t,...n},null,2)}
8
- `)}function z(e){process.stderr.write(`${e}
2
+ var qi=Object.defineProperty;var M=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ir=(e,t)=>{for(var n in t)qi(e,n,{get:t[n],enumerable:!0})};function jr(e){let t=[],n={};for(let r=0;r<e.length;r++){let o=e[r];if(!o.startsWith("-")){t.push(o);continue}if(o==="--"){t.push(...e.slice(r+1));break}let[s,a]=Wi(o);if(Gi.has(s)){let u=a??e[++r];if(u===void 0)throw new y(`--${s} needs a value`);n[s]=u;continue}if(a!==void 0)throw new y(`--${s} does not take a value`);if(!Ki.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function Wi(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function R(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function B(e,t){return e.flags[t]===!0}function $e(e,t){let n=R(e,t);if(n===void 0)return;let r=Number(n);if(!Number.isInteger(r))throw new y(`--${t} must be a whole number, not "${n}"`);return r}var y,Gi,Ki,re=M(()=>{"use strict";y=class extends Error{},Gi=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","memory","concurrency","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment","email","channel","since"]);Ki=new Set(["json","window","help","version","force","no-verify-jwt","reset","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history","show-url","check","watch"])});import{chmodSync as Ji,existsSync as Lr,mkdirSync as Ur,readFileSync as Fr,rmSync as zi,writeFileSync as Br}from"node:fs";import{homedir as bt}from"node:os";import{dirname as hn,join as Hr,parse as Vi,resolve as Yi}from"node:path";function yn(e=bt()){return Hr(e,".snoutdata","auth.json")}function St(e=bt()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=yn(e);if(!Lr(n))return null;try{let r=JSON.parse(Fr(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function se(e,t=bt()){let n=yn(t);Ur(hn(n),{recursive:!0}),Br(n,`${JSON.stringify(e,null,2)}
3
+ `,{mode:384}),Ji(n,384)}function qr(e=bt()){zi(yn(e),{force:!0})}function Gr(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function Kr(e){return Hr(e,".snoutdata","project.json")}function Ke(e){let t=Yi(e),n=Vi(t).root;for(;;){let r=Kr(t);if(Lr(r))try{let o=JSON.parse(Fr(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return typeof o.local=="string"&&o.local?{ref:o.ref,directory:t,local:o.local}:{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=hn(t)}}function We(e,t){let n=Kr(e);return Ur(hn(n),{recursive:!0}),Br(n,`${JSON.stringify(t,null,2)}
4
+ `),n}function Je(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:Ke(e.cwd??process.cwd())?.ref??null}var he=M(()=>{"use strict"});import{existsSync as wn,readFileSync as Wr}from"node:fs";import{homedir as Xi}from"node:os";import{isAbsolute as Zi,join as ye,resolve as Oe}from"node:path";function Qi(e=process.env,t=process.platform,n=Xi()){let r;return t==="win32"?r=e.APPDATA??ye(n,"AppData","Roaming"):t==="darwin"?r=ye(n,"Library","Application Support"):r=e.XDG_CONFIG_HOME??ye(n,".config"),["snoutdata","snoutdata-dev"].map(o=>ye(r,o,"localProjects.json"))}function xt(e=Qi()){let t=[];for(let n of e)if(wn(n))try{let r=JSON.parse(Wr(n,"utf8"));for(let o of r.projects??[])typeof o.folder=="string"&&o.folder&&!t.some(s=>s.folder===o.folder)&&t.push({name:o.name??null,folder:o.folder,ref:o.ref??null})}catch{}return t}function _t(e){let t=new Map;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim(),a=r.slice(o+1).trim();a.length>=2&&(a[0]==='"'||a[0]==="'")&&a.endsWith(a[0])&&(a=a.slice(1,-1)),t.set(s,a)}return t}function ze(e){return wn(ye(e,"compose.yaml"))&&wn(ye(e,".env"))}function we(e,t=null){let n=Oe(e);if(!ze(n))throw new Error(`${n} is not a SnoutData stack folder: it needs compose.yaml and .env (see github.com/snoutdata/snout-stack)`);let r=_t(Wr(ye(n,".env"),"utf8")),o=r.get("SNOUT_REF"),s=r.get("POSTGRES_OWNER_PASSWORD");if(!o||!s)throw new Error(`${ye(n,".env")} has no ${o?"POSTGRES_OWNER_PASSWORD":"SNOUT_REF"}, so this stack was not set up with \`snout-stack init\``);let a=Number(r.get("API_PORT")??8e3)||8e3,u=Number(r.get("DB_PORT")??5432)||5432;return{folder:n,ref:o,name:t,apiUrl:(r.get("API_EXTERNAL_URL")||`http://127.0.0.1:${a}`).replace(/\/$/,""),apiPort:a,dbPort:u,anonKey:r.get("ANON_KEY")||null,serviceRoleKey:r.get("SERVICE_ROLE_KEY")||null,ownerPassword:s}}function Jr(e,t=process.cwd(),n=xt()){if(!e){if(ze(t))return we(t);let[i]=n;if(n.length===1&&i)return we(i.folder,i.name);throw n.length===0?new Error("there is no local project to link: set one up in Studio (Projects, Local), or pass the folder of a stack you made with snout-stack"):new Error(`there are ${n.length} local projects; name one: ${n.map(d=>d.name??d.folder).join(", ")}`)}let r=Zi(e)?e:Oe(t,e);if(ze(r)){let i=n.find(d=>Oe(d.folder)===Oe(r));return we(r,i?.name??null)}let o=e.toLowerCase(),s=n.filter(i=>i.name?.toLowerCase()===o||i.ref===e),[a]=s;if(s.length===1&&a)return we(a.folder,a.name);let u=n.map(i=>i.name??i.folder);throw new Error(s.length>1?`more than one local project is called ${e}; link it by its folder`:`no local project called ${e}, and it is not a stack folder${u.length?`. The local projects are ${u.join(", ")}`:""}`)}function zr(e){let t=`${e.ref}_owner`;return{ref:e.ref,host:"127.0.0.1",port:e.dbPort,database:e.ref,user:t,password:e.ownerPassword,ssl:"disable",uri:`postgresql://${t}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.dbPort}/${e.ref}`,anonKey:e.anonKey,serviceRoleKey:e.serviceRoleKey,keysIssuedAt:null,keysExpireAt:null,wakesInstantly:!0,state:"local"}}function Vr(e,t,n=xt()){if(t&&ze(t))try{let r=n.find(s=>Oe(s.folder)===Oe(t)),o=we(t,r?.name??null);if(o.ref===e)return o}catch{}for(let r of n)if(r.ref===e&&ze(r.folder))return we(r.folder,r.name);return null}var Ve=M(()=>{"use strict"});function O(e,t,n){throw new N(e,t,n)}function kt(e){return e===403?"forbidden":e===404?"not-found":e===409?"conflict":e===402||e===429?"quota":e===503?"not-ready":e===504?"timeout":e===410?"outdated":e>=500?"server":"failed"}function Yr(e){let t=e;return!t||typeof t!="object"||t.code!=="ENOENT"||typeof t.path!="string"||t.syscall==="spawn"?null:`no file at ${t.path}`}function Xr(e){let t=[e],n=e;for(let r=0;r<4&&n instanceof Error&&n.cause;r+=1)n=n.cause,t.push(n);for(let r of t){let o=typeof r=="object"&&r&&"code"in r?String(r.code):"";if(ec.has(o))return"network";if(o==="ABORT_ERR"||o==="ETIMEDOUT")return"timeout"}return e instanceof Error&&/fetch failed|network|socket hang up/i.test(e.message)?"network":"failed"}var le,N,ec,X=M(()=>{"use strict";le={ok:0,failed:1,usage:2,"not-signed-in":3,"not-ready":4,forbidden:5,"not-found":6,conflict:7,quota:8,network:9,timeout:10,outdated:11,"tool-missing":127,server:1},N=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return le[this.code]}};ec=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});function vt(e){let t=e.env.SNOUTDATA_NO_INTERACTIVE;if(t!==void 0&&t!==""&&t!=="0"&&t!=="false")return{canAsk:!1,reason:"opted-out"};if(e.json)return{canAsk:!1,reason:"json"};for(let n of tc){let r=e.env[n];if(r!==void 0&&r!==""&&r!=="0"&&r!=="false")return{canAsk:!1,reason:"ci"}}return e.isTty?{canAsk:!0}:{canAsk:!1,reason:"not-a-tty"}}function Ne(e){switch(e){case"json":return"running with --json, so nothing was asked";case"opted-out":return"SNOUTDATA_NO_INTERACTIVE is set, so nothing was asked";case"ci":return"this looks like CI, so nothing was asked";case"not-a-tty":return"there is no terminal to ask at"}}function Zr(e){bn=e}function be(){return(bn??vt({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function De(){return(bn??vt({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var tc,bn,Me=M(()=>{"use strict";tc=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];bn=null});import{existsSync as nc,readFileSync as rc}from"node:fs";import{homedir as Sn,hostname as oc}from"node:os";import{join as Ie}from"node:path";function sc(e,t,n){if(t==="win32"){let r=n.APPDATA??Ie(Sn(),"AppData","Roaming");return Ie(r,e)}return t==="darwin"?Ie(Sn(),"Library","Application Support",e):Ie(n.XDG_CONFIG_HOME??Ie(Sn(),".config"),e)}function eo(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>Ie(sc(n,e,t),"mcp.json"))}function ac(e=process.platform,t=process.env){return eo(e,t)[0]}function ic(e=ac()){if(!nc(e))return null;try{let t=JSON.parse(rc(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function cc(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function Et(e){if(e!==void 0)return Qr(e);let t="no-config";for(let n of eo()){let r=ic(n);if(!r)continue;let o=await Qr(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function Qr(e){let t=cc(e);if(t)return{available:!1,skip:t};let n=e,r;try{let o=await fetch(`http://127.0.0.1:${n.port}/cli/whoami`,{headers:{Authorization:`Bearer ${n.token}`},signal:AbortSignal.timeout(lc)});if(!o.ok)return{available:!1,skip:"not-running"};r=await o.json()}catch{return{available:!1,skip:"not-running"}}return r.signedIn?r.canMintToken?{available:!0,who:r,config:n}:{available:!1,skip:"cannot-mint"}:{available:!1,skip:"signed-out"}}async function to(e,t){let n=await fetch(`http://127.0.0.1:${e.port}/cli/token`,{method:"POST",headers:{Authorization:`Bearer ${e.token}`,"Content-Type":"application/json"},body:JSON.stringify({client:"snoutdata-cli",command:t}),signal:AbortSignal.timeout(6e4)}),r=await n.json().catch(()=>({}));return n.ok&&r.ok&&r.token?{token:r.token,name:r.name??"SnoutData CLI"}:{declined:!0,reason:r.error??`SnoutData Studio refused (${n.status})`}}function $t(){return oc()}async function xn(e,t,n,r){let o=await fetch(`http://127.0.0.1:${e.port}/mcp`,{method:"POST",headers:{Authorization:`Bearer ${e.token}`,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:t,params:n}),signal:AbortSignal.timeout(r)});if(!o.ok)throw new Error(`the app answered ${o.status}`);let s=await o.json();if(s.error)throw new Error(s.error.message??"SnoutData Studio refused");return s.result}async function no(e){return await xn(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await xn(e,"tools/list",{},3e3)).tools??[]}async function ro(e,t,n){return xn(e,"tools/call",{name:t,arguments:n},12e4)}var lc,Pt=M(()=>{"use strict";lc=1500});import{basename as uc}from"node:path";function Tt(e={bun:typeof process.versions.bun=="string",execPath:process.execPath,script:process.argv[1]??""}){let t=uc(e.execPath.replace(/\\/g,"/")).toLowerCase();if(e.bun&&t.startsWith("snoutdata"))return"binary";let n=e.script.replace(/\\/g,"/");return/\/_npx\//.test(n)?"npx":/\/node_modules\/snoutdata\//.test(n)?"npm":"source"}function dc(e=Tt(),t=process.platform){switch(e){case"binary":case"npm":return"snoutdata upgrade";case"npx":return"npx snoutdata@latest";case"source":return t==="win32"?ue:Ye}}function _n(e={}){let t=e.kind??Tt(),n=e.platform??process.platform,r=e.minimum?`${e.minimum} or later is required`:"a newer version is required",o=n==="win32"?ue:`${Ye} (or ${ue})`;return`snoutdata ${J} is installed and ${r}. Run \`${dc(t,n)}\`, or install again: ${o}`}function so(e,t){if(e==="dev"||t==="dev")return!1;let n=e.split("-")[0].split(".").map(Number),r=t.split("-")[0].split(".").map(Number);for(let o=0;o<Math.max(n.length,r.length);o+=1){let s=n[o]??0,a=r[o]??0;if(Number.isNaN(s)||Number.isNaN(a))return!1;if(s!==a)return s<a}return!1}var J,Rt,Ye,ue,Xe=M(()=>{"use strict";J="0.10.2",Rt=`snoutdata-cli/${J}`,Ye="curl -fsSL https://snoutdata.com/install.sh | sh",ue="npm install -g snoutdata@latest"});async function ao(e,t){let n=await fetch(`${ae}/functions/v1/${e}`,{method:"POST",headers:{apikey:de,"Content-Type":"application/json","x-client-info":Rt},body:JSON.stringify(t)});Nt(n);let r=await n.json().catch(()=>({}));if(n.status===410){let o=r.error;throw Dt(n,typeof o=="string"?o:"This version of the snoutdata CLI is no longer supported.")}return{status:n.status,body:r}}async function At(e={}){let{status:t,body:n}=await ao("cloud-device-start",{client:e});return t===429&&O("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&O("not-found","this SnoutData does not have the pairing flow (cloud-device-start is not deployed). Use `snoutdata login` to sign in with a browser, or set SNOUTDATA_ACCESS_TOKEN."),(t!==200||!n.code||!n.secret)&&O("server",n.error??`could not start a pairing (${t})`),n}async function Ot(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&O("timeout","that pairing code expired before it was approved. Run the command again for a new one."),await new Promise(a=>setTimeout(a,n));let{status:o,body:s}=await ao("cloud-device-poll",{secret:e});switch(o===404&&O("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&O("usage",s.error??"that is not a pairing secret"),o!==200&&O("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":O("forbidden",s.error??"that pairing was refused in the browser");case"used":O("conflict",s.error??"that pairing has already been completed");case"expired":O("timeout",s.error??"that pairing code expired before it was approved");case"slow-down":n=Math.max(n*2,(s.interval??2)*1e3);break;default:s.interval&&(n=Math.max(n,s.interval*1e3));break}t.onWait?.(Math.max(0,Math.round((r-Date.now())/1e3)))}}var kn=M(()=>{"use strict";K();X();Xe()});function Qe(e){Ze=e}function co(e){io=e}function lo(){return Ze}function b(e){return uo?`\x1B[2m${e}\x1B[0m`:e}function v(e){return uo?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(Ze){process.stdout.write(`${JSON.stringify(e,null,2)}
5
+ `);return}t()}function c(e){!Ze&&!io&&process.stderr.write(`${e}
6
+ `)}function Se(e,t,n){process.stderr.write(`${t}
7
+ `),Ze&&process.stdout.write(`${JSON.stringify({ok:!1,code:e,error:t,...n},null,2)}
8
+ `)}function W(e){process.stderr.write(`${e}
9
9
  `)}function U(e){if(e.length===0)return"";let t=e[0].map((n,r)=>Math.max(...e.map(o=>(o[r]??"").length)));return e.map(n=>n.map((r,o)=>r.padEnd(t[o]??0)).join(" ").trimEnd()).join(`
10
- `)}function le(e){if(!e)return"never";let t=Math.round((Date.now()-Date.parse(e))/1e3),n=t<0,r=Math.abs(t);if(r<60)return n?"in a moment":"just now";let o=[[60,"minute"],[60,"hour"],[24,"day"],[30,"month"]],s=r,a="second";for(let[i,m]of o){if(s<i)break;s=Math.round(s/i),a=m}let u=`${s} ${a}${s===1?"":"s"}`;return n?`in ${u}`:`${u} ago`}var _t,Lr,Fr,q=M(()=>{"use strict";_t=!1,Lr=!1;Fr=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as qr,randomBytes as on}from"node:crypto";import{createServer as Ai}from"node:http";import{createInterface as Ri}from"node:readline/promises";import{spawn as Oi}from"node:child_process";function kt(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Br(e,t){let n=e==="ok"?"#e0b256":"#e0765a",r=e==="ok"?"Signed in":"Sign-in failed",o=e==="ok"?"You can close this tab and go back to your terminal.":Ji(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
10
+ `)}function me(e){if(!e)return"never";let t=Math.round((Date.now()-Date.parse(e))/1e3),n=t<0,r=Math.abs(t);if(r<60)return n?"in a moment":"just now";let o=[[60,"minute"],[60,"hour"],[24,"day"],[30,"month"]],s=r,a="second";for(let[i,d]of o){if(s<i)break;s=Math.round(s/i),a=d}let u=`${s} ${a}${s===1?"":"s"}`;return n?`in ${u}`:`${u} ago`}var Ze,io,uo,q=M(()=>{"use strict";Ze=!1,io=!1;uo=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as po,randomBytes as vn}from"node:crypto";import{createServer as fc}from"node:http";import{createInterface as pc}from"node:readline/promises";import{spawn as gc}from"node:child_process";function Mt(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function mo(e,t){let n=e==="ok"?"#e0b256":"#e0765a",r=e==="ok"?"Signed in":"Sign-in failed",o=e==="ok"?"You can close this tab and go back to your terminal.":Nc(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
11
11
  <meta name="viewport" content="width=device-width,initial-scale=1"></head>
12
12
  <body style="font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;background:#050403;color:#e9e4dc;display:grid;place-items:center;min-height:100vh;margin:0">
13
13
  <div style="text-align:center;max-width:460px;padding:40px 24px">
@@ -19,32 +19,32 @@ var ii=Object.defineProperty;var M=(e,t)=>()=>(e&&(t=e(e=0)),t);var fr=(e,t)=>{f
19
19
  <p style="color:#8f887e;margin:0;font-size:15px;line-height:1.5">${o}</p>
20
20
  ${e==="ok"?"":'<p style="color:#8f887e;margin:14px 0 0;font-size:14px">Run <code style="background:#1b1917;border:1px solid #262320;border-radius:6px;padding:2px 6px;font-size:13px">snoutdata login</code> to try again.</p>'}
21
21
  </div>
22
- </body></html>`}function sn(){let e=kt(on(32)),t=kt(qr("sha256").update(e).digest());return{verifier:e,challenge:t}}function Ni(e,t){return e==="darwin"?{command:"open",args:[t],verbatim:!1}:e==="win32"?{command:"cmd.exe",args:["/c","start",'""',`"${t}"`],verbatim:!0}:{command:"xdg-open",args:[t],verbatim:!1}}function Di(e){let{command:t,args:n,verbatim:r}=Ni(process.platform,e),o=Oi(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function Gr(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function Ci(){return"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"}function ji(e){let t=e??kt(on(32));return{raw:t,hashed:qr("sha256").update(t).digest("hex")}}function Li(e){return`${Mi}?${new URLSearchParams({client_id:Gr(),redirect_uri:e.redirectUri,response_type:"code",scope:"openid email profile",code_challenge:e.challenge,code_challenge_method:"S256",nonce:e.hashedNonce,state:e.state,prompt:"select_account"}).toString()}`}function Ui(e,t){let n=new URL(e,"http://127.0.0.1");if(!n.searchParams.has("code")&&!n.searchParams.has("error"))return{kind:"ignore"};let r=n.searchParams.get("error_description")??n.searchParams.get("error");if(r)return{kind:"error",message:r};let o=n.searchParams.get("code");return o?t!==void 0&&n.searchParams.get("state")!==t?{kind:"error",message:"the browser came back with a state that did not match"}:{kind:"ok",code:o}:{kind:"error",message:"the browser came back without a code"}}async function an(e){return new Promise((t,n)=>{let r="",o=Ai((a,u)=>{let i=Ui(a.url??"/",e.state);if(i.kind==="ignore"){u.writeHead(404).end();return}u.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),u.end(i.kind==="ok"?Br("ok"):Br("failed",i.message)),o.close(),i.kind==="ok"?t({code:i.code,redirectUri:r}):n(new Error(i.message))});o.listen(0,"127.0.0.1",()=>{let a=o.address();r=`http://127.0.0.1:${typeof a=="object"&&a?a.port:0}${e.callbackPath??""}`,Promise.resolve().then(()=>e.authorizeUrl(r)).then(i=>{process.stdout.write(`${i}
23
- `),e.noBrowser?c("Open that in a browser, on any machine, and sign in."):(c("Opening that in your browser. If nothing opens, paste it into one."),Di(i))},i=>{o.close(),n(i)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Kr(e,t){let n=await fetch(`${re}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:ce,"Content-Type":"application/json"},body:JSON.stringify({auth_code:e,code_verifier:t})});if(!n.ok)throw new Error(`could not exchange the code: ${(await n.text()).slice(0,200)}`);return Wr(await n.json())}async function Fi(e,t,n){let r=await fetch(Ii,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Gr(),client_secret:Ci(),code:e,code_verifier:t,grant_type:"authorization_code",redirect_uri:n})}),o=await r.json().catch(()=>({}));if(!r.ok||!o.id_token)throw new Error(`Google would not exchange the code: ${o.error_description??o.error??`HTTP ${r.status}`}`);return o.id_token}async function Bi(e){let{verifier:t,challenge:n}=sn(),{raw:r,hashed:o}=ji(),s=kt(on(16)),{code:a,redirectUri:u}=await an({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:l=>Li({redirectUri:l,challenge:n,hashedNonce:o,state:s})}),i=await Fi(a,t,u),m=await fetch(`${re}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:ce,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:i,nonce:r})});if(!m.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await m.text()).slice(0,200)}`);return Wr(await m.json())}function Wr(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return ae(n),n}function Hr(e){let t=e.trim().toLowerCase();if(!t||/\s/.test(t))return null;let n=t.indexOf("@");if(n===0||t.indexOf("@",n+1)!==-1)return null;let r=n===-1?t:t.slice(n+1);if(!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/.test(r))return null;let o=r.slice(r.lastIndexOf(".")+1);return/^[a-z]{2,}$/.test(o)?r:null}function Hi(e){try{let t=JSON.parse(e);for(let n of["error_description","msg","message","error"]){let r=t[n];if(typeof r=="string"&&r)return r}}catch{return""}return""}function qi(e,t,n){let r=Hi(t);return e===404||/no sso provider/i.test(r)?new L("not-found",`No single sign-on is set up for ${n}. Check the address, or ask whoever administers your team to connect your identity provider.`,{domain:n}):/saml.*(disabled|not enabled)/i.test(r)?new L("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new L(gt(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function Gi(e){let t=await fetch(`${re}/auth/v1/sso`,{method:"POST",headers:{apikey:ce,"Content-Type":"application/json"},body:JSON.stringify({domain:e.domain,redirect_to:e.redirectTo,skip_http_redirect:!0,code_challenge:e.challenge,code_challenge_method:"s256"})}),n=await t.text();if(!t.ok)throw qi(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new L("server",`Single sign-on for ${e.domain} was accepted but came back without somewhere to send the browser.`,{domain:e.domain});return r}async function Ki(e){if(e!==void 0){let r=Hr(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!pe()){let r=Re();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${Ae(r)}`:""}`)}let t=Ri({input:process.stdin,output:process.stderr}),n=new Promise((r,o)=>{t.once("close",()=>o(new y("nothing left to read: pass --domain <your work email or company domain>")))});n.catch(()=>{});try{for(let r=0;r<3;r+=1){let o=await Promise.race([t.question("Your work email address: "),n]),s=Hr(o);if(s)return s;c("That is not a work email or a company domain. Try something like you@yourcompany.com.")}}finally{t.close()}throw new y("no domain to sign in with: pass --domain <your work email or company domain>")}async function Wi(e){let t=await Ki(e.domain),{verifier:n,challenge:r}=sn();c(`Signing in with the identity provider for ${P(t)}.`);let{code:o}=await an({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>Gi({domain:t,redirectTo:s,challenge:r})});return Kr(o,n)}async function vt(e={}){if(e.sso)return Wi(e);let t=e.provider??"google";if(t==="google")return Bi(e);let{verifier:n,challenge:r}=sn(),{code:o}=await an({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${re}/auth/v1/authorize`);return a.searchParams.set("provider",t),a.searchParams.set("redirect_to",s),a.searchParams.set("code_challenge",r),a.searchParams.set("code_challenge_method","s256"),a.searchParams.set("prompt","select_account"),a.toString()}});return Kr(o,n)}function Ji(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var Mi,Ii,cn=M(()=>{"use strict";H();ne();ke();Y();Oe();q();Mi="https://accounts.google.com/o/oauth2/v2/auth",Ii="https://oauth2.googleapis.com/token"});import{createInterface as zi}from"node:readline/promises";async function ln(e){let t=zi({input:process.stdin,output:process.stderr});try{let n=(await t.question(`${e} [Y/n] `)).trim().toLowerCase();return n===""||n==="y"||n==="yes"}finally{t.close()}}async function Vi(){let e=await yt();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&c(b("SnoutData Studio is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(c(""),c(`SnoutData Studio is open here, signed in as ${P(t)}.`),c(b("It can create an access token for this terminal. Your sign-in itself is not handed over,")),c(b("and you can revoke the token later with `snoutdata tokens revoke`.")),!await ln("Use it?"))return null;c("Asking SnoutData Studio. Approve it there.");let n=await Dr(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return c(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return ae(r),c(`Signed in with "${n.name}".`),r}async function Yi(){if(!await ln("Sign in with a browser?"))return null;try{let e=await vt({});return c(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return c(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function Xi(){if(!await ln("Sign in by typing a code into a browser on another machine?"))return null;let e=await St({name:wt(),platform:process.platform});c(""),c(` Open ${P(e.verifyUrl)}`),c(` Type ${P(e.code)}`),c(""),c(b("The code is typed rather than in the link on purpose: it is what proves the person")),c(b("at the browser is the person at this terminal. Waiting\u2026"));let t=await xt(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return ae(n),c(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function Jr(){if(!pe())return null;let e=await Vi();if(e)return e;let t=await Yi();return t||Xi()}var zr=M(()=>{"use strict";ke();bt();rn();Oe();cn();q()});var we={};fr(we,{ACCOUNTS_URL:()=>re,ANON_KEY:()=>ce,ApiError:()=>oe,FUNCTIONS:()=>Vr,NotSignedIn:()=>he,call:()=>S,connection:()=>V,exportStatus:()=>Me,isSettled:()=>Yr,listProjects:()=>X,listTeams:()=>Et,localStack:()=>ye,requestExport:()=>Ve,waitForReady:()=>Xe,waitForSettled:()=>Ye,whoami:()=>De});function Qi(){let e=Re();return e?`not signed in, and ${Ae(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token made at https://dashboard.snoutdata.com/#/tokens, or by \`snoutdata tokens create\` on a machine that is signed in.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function ec(){let e=Sr();if(e||(e=await Jr()),!e)throw new he;if(!_r(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${re}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:ce,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new he;let n=await t.json(),r={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,email:e.email};return ae(r),r.accessToken}async function S(e,t={},n="POST"){let r=typeof t=="object"&&t?t.ref:void 0;if(typeof r=="string"){let m=ye(r);if(m)throw new L("usage",`${r} is a local project (${m.folder}), and this command is about SnoutData Cloud. Locally: db url, db psql, db push, gen types, keys, status, start, stop, functions, secrets.`,{ref:r,local:m.folder})}let o=await ec(),s=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",a=await fetch(`${Vr}/${e}${s}`,{method:n,headers:{Authorization:`Bearer ${o}`,apikey:ce,"Content-Type":"application/json","x-client-info":Zi},body:n==="GET"?void 0:JSON.stringify(t)}),u=await a.text(),i;try{i=u?JSON.parse(u):{}}catch{throw new oe(a.status,u.slice(0,300)||a.statusText)}if(!a.ok){let m=typeof i=="object"&&i&&"error"in i?String(i.error):`${a.status} ${a.statusText}`;throw a.status===401?new he(m):new oe(a.status,m)}return i}async function De(){return S("cloud-whoami")}async function X(){return S("cloud-project-list")}async function V(e){let t=ye(e);return t?Pr(t):S("cloud-project-connection",{ref:e})}function ye(e){return $r(e,qe(process.cwd())?.local??null)}async function Et(){return S("cloud-team-list")}async function Ve(e){return S("cloud-project-export",{ref:e})}async function Me(e){return S("cloud-project-export",{ref:e},"GET")}async function Ye(e,t,n={}){let r=Date.now()+(n.timeoutMs??3e5);for(;;){let{projects:o}=await X(),s=o.find(u=>u.ref===e)??null,a=s?.state??"deleted";if(n.onTick?.(a),Yr(t,a))return s;if(Date.now()>=r)throw new oe(504,`${e} is still ${a} after waiting`);await new Promise(u=>setTimeout(u,2e3))}}function Yr(e,t){let n={pause:"paused",resume:"ready",delete:"deleted"}[e];return t===n||t==="error"}async function Xe(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await X(),o=r.find(s=>s.ref===e);if(!o)throw new oe(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new oe(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var Zi,re,ce,Vr,oe,he,H=M(()=>{"use strict";ke();Je();Y();Oe();zr();Zi="snoutdata-cli/0.10.0",re=process.env.SNOUTDATA_ACCOUNTS_URL??"https://accounts.snoutdata.com",ce=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzbm91dGRhdGEiLCJyZWYiOiJ6ZjdnNjVtYjhmYXJhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTA4MDcwNDcsImV4cCI6MjEwNDg3NTUyNH0.M-Cb0wFEhJXU21cXtQxZAn-WnRcq4rK2uNSkeHdLucY",Vr=`${re}/functions/v1`,oe=class extends Error{status;constructor(t,n){super(n),this.status=t}},he=class extends Error{constructor(t){super(t??Qi())}}});function Bn(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function Ts(e,t,n){let r="";for(;r.length<n;)r+=Bn(e,t);return r}var Fn,$s,As=M(()=>{"use strict";Fn="0123456789abcdefghjkmnpqrstvwxyz",$s="abcdefghjkmnpqrstvwxyz"});function Ns(e){return Bn(e,Rs)+Ts(e,Fn,Os-1)}function Kl(e){return Gl.test(e)}function Q(e){if(!Kl(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function C(e,t=Wl){return Q(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var Rs,Os,Gl,Wl,be=M(()=>{"use strict";As();Rs=$s,Os=13,Gl=new RegExp(`^[${Rs}][${Fn}]{${Os-1}}$`);Wl="db.snoutdata.com"});var Ds=M(()=>{"use strict";be()});function Is(e,t){let n=Ms(e,7),r=Ms(t,24);return Math.max(2,Math.ceil(n*24/r))}function Ms(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var Hn=M(()=>{"use strict"});function qn(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var Gn=M(()=>{"use strict"});var Cs=M(()=>{"use strict"});var Vf,js=M(()=>{"use strict";be();Vf=7*24});var Xf,Ls=M(()=>{"use strict";Xf=7*24*60*60*1e3});var Jl,Us=M(()=>{"use strict";Ls();Jl=60*60*1e3});var Ue,rp,Fs=M(()=>{"use strict";Ue="snout_storage_admin",rp=`do $$ begin
22
+ </body></html>`}function En(){let e=Mt(vn(32)),t=Mt(po("sha256").update(e).digest());return{verifier:e,challenge:t}}function hc(e,t){return e==="darwin"?{command:"open",args:[t],verbatim:!1}:e==="win32"?{command:"cmd.exe",args:["/c","start",'""',`"${t}"`],verbatim:!0}:{command:"xdg-open",args:[t],verbatim:!1}}function yc(e){let{command:t,args:n,verbatim:r}=hc(process.platform,e),o=gc(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function go(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function Sc(){return"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"}function xc(e){let t=e??Mt(vn(32));return{raw:t,hashed:po("sha256").update(t).digest("hex")}}function _c(e){return`${wc}?${new URLSearchParams({...e.loginHint?{login_hint:e.loginHint}:{},client_id:go(),redirect_uri:e.redirectUri,response_type:"code",scope:"openid email profile",code_challenge:e.challenge,code_challenge_method:"S256",nonce:e.hashedNonce,state:e.state,prompt:"select_account"}).toString()}`}function kc(e,t){let n=new URL(e,"http://127.0.0.1");if(!n.searchParams.has("code")&&!n.searchParams.has("error"))return{kind:"ignore"};let r=n.searchParams.get("error_description")??n.searchParams.get("error");if(r)return{kind:"error",message:r};let o=n.searchParams.get("code");return o?t!==void 0&&n.searchParams.get("state")!==t?{kind:"error",message:"the browser came back with a state that did not match"}:{kind:"ok",code:o}:{kind:"error",message:"the browser came back without a code"}}async function $n(e){return new Promise((t,n)=>{let r="",o=fc((a,u)=>{let i=kc(a.url??"/",e.state);if(i.kind==="ignore"){u.writeHead(404).end();return}u.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),u.end(i.kind==="ok"?mo("ok"):mo("failed",i.message)),o.close(),i.kind==="ok"?t({code:i.code,redirectUri:r}):n(new Error(i.message))});o.listen(0,"127.0.0.1",()=>{let a=o.address();r=`http://127.0.0.1:${typeof a=="object"&&a?a.port:0}${e.callbackPath??""}`,Promise.resolve().then(()=>e.authorizeUrl(r)).then(i=>{process.stdout.write(`${i}
23
+ `),e.noBrowser?c("Open that in a browser, on any machine, and sign in."):(c("Opening that in your browser. If nothing opens, paste it into one."),yc(i))},i=>{o.close(),n(i)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function ho(e,t){let n=await fetch(`${ae}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:de,"Content-Type":"application/json"},body:JSON.stringify({auth_code:e,code_verifier:t})});if(!n.ok)throw new Error(`could not exchange the code: ${(await n.text()).slice(0,200)}`);return yo(await n.json())}async function vc(e,t,n){let r=await fetch(bc,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:go(),client_secret:Sc(),code:e,code_verifier:t,grant_type:"authorization_code",redirect_uri:n})}),o=await r.json().catch(()=>({}));if(!r.ok||!o.id_token)throw new Error(`Google would not exchange the code: ${o.error_description??o.error??`HTTP ${r.status}`}`);return o.id_token}async function Ec(e){let{verifier:t,challenge:n}=En(),{raw:r,hashed:o}=xc(),s=Mt(vn(16)),{code:a,redirectUri:u}=await $n({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:l=>_c({redirectUri:l,challenge:n,hashedNonce:o,state:s,loginHint:e.email})}),i=await vc(a,t,u),d=await fetch(`${ae}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:de,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:i,nonce:r})});if(!d.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await d.text()).slice(0,200)}`);return yo(await d.json())}function yo(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return se(n),n}function fo(e){let t=e.trim().toLowerCase();if(!t||/\s/.test(t))return null;let n=t.indexOf("@");if(n===0||t.indexOf("@",n+1)!==-1)return null;let r=n===-1?t:t.slice(n+1);if(!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/.test(r))return null;let o=r.slice(r.lastIndexOf(".")+1);return/^[a-z]{2,}$/.test(o)?r:null}function $c(e){try{let t=JSON.parse(e);for(let n of["error_description","msg","message","error"]){let r=t[n];if(typeof r=="string"&&r)return r}}catch{return""}return""}function Pc(e,t,n){let r=$c(t);return e===404||/no sso provider/i.test(r)?new N("not-found",`No single sign-on is set up for ${n}. Check the address, or ask whoever administers your team to connect your identity provider.`,{domain:n}):/saml.*(disabled|not enabled)/i.test(r)?new N("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new N(kt(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function Rc(e){let t=await fetch(`${ae}/auth/v1/sso`,{method:"POST",headers:{apikey:de,"Content-Type":"application/json"},body:JSON.stringify({domain:e.domain,redirect_to:e.redirectTo,skip_http_redirect:!0,code_challenge:e.challenge,code_challenge_method:"s256"})}),n=await t.text();if(!t.ok)throw Pc(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new N("server",`Single sign-on for ${e.domain} was accepted but came back without somewhere to send the browser.`,{domain:e.domain});return r}async function Tc(e){if(e!==void 0){let r=fo(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!be()){let r=De();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${Ne(r)}`:""}`)}let t=pc({input:process.stdin,output:process.stderr}),n=new Promise((r,o)=>{t.once("close",()=>o(new y("nothing left to read: pass --domain <your work email or company domain>")))});n.catch(()=>{});try{for(let r=0;r<3;r+=1){let o=await Promise.race([t.question("Your work email address: "),n]),s=fo(o);if(s)return s;c("That is not a work email or a company domain. Try something like you@yourcompany.com.")}}finally{t.close()}throw new y("no domain to sign in with: pass --domain <your work email or company domain>")}async function Ac(e){let t=await Tc(e.domain),{verifier:n,challenge:r}=En();c(`Signing in with the identity provider for ${v(t)}.`);let{code:o}=await $n({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>Rc({domain:t,redirectTo:s,challenge:r})});return ho(o,n)}async function It(e={}){return Pn(e.email,await Oc(e))}function Pn(e,t){if(!e)return t;let n={...t,expectedEmail:e};return se(n),t.email&&!Rn(t.email,e)&&W(`Signed in as ${t.email}, not ${e}. Run \`snoutdata login --email ${e}\` and pick that account.`),n}function Rn(e,t){return e.trim().toLowerCase()===t.trim().toLowerCase()}async function Oc(e){if(e.sso)return Ac({...e,domain:e.domain??e.email});let t=e.provider??"google";if(t==="google")return Ec(e);let{verifier:n,challenge:r}=En(),{code:o}=await $n({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${ae}/auth/v1/authorize`);return a.searchParams.set("provider",t),a.searchParams.set("redirect_to",s),a.searchParams.set("code_challenge",r),a.searchParams.set("code_challenge_method","s256"),a.searchParams.set("prompt","select_account"),a.toString()}});return ho(o,n)}function Nc(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var wc,bc,jt=M(()=>{"use strict";K();re();he();X();Me();q();wc="https://accounts.google.com/o/oauth2/v2/auth",bc="https://oauth2.googleapis.com/token"});import{createInterface as Dc}from"node:readline/promises";async function Tn(e){let t=Dc({input:process.stdin,output:process.stderr});try{let n=(await t.question(`${e} [Y/n] `)).trim().toLowerCase();return n===""||n==="y"||n==="yes"}finally{t.close()}}async function Mc(){let e=await Et();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&c(b("SnoutData Studio is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(c(""),c(`SnoutData Studio is open here, signed in as ${v(t)}.`),c(b("It can create an access token for this terminal. Your sign-in itself is not handed over,")),c(b("and you can revoke the token later with `snoutdata tokens revoke`.")),!await Tn("Use it?"))return null;c("Asking SnoutData Studio. Approve it there.");let n=await to(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return c(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return se(r),c(`Signed in with "${n.name}".`),r}async function Ic(){if(!await Tn("Sign in with a browser?"))return null;try{let e=await It({});return c(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return c(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function jc(){if(!await Tn("Sign in by typing a code into a browser on another machine?"))return null;let e=await At({name:$t(),platform:process.platform});c(""),c(` Open ${v(e.verifyUrl)}`),c(` Type ${v(e.code)}`),c(""),c(b("The code is typed rather than in the link on purpose: it is what proves the person")),c(b("at the browser is the person at this terminal. Waiting\u2026"));let t=await Ot(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return se(n),c(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function wo(){if(!be())return null;let e=await Mc();if(e)return e;let t=await Ic();return t||jc()}var bo=M(()=>{"use strict";he();Pt();kn();Me();jt();q()});var fe={};Ir(fe,{ACCOUNTS_URL:()=>ae,ANON_KEY:()=>de,ApiError:()=>ie,FUNCTIONS:()=>xo,NotSignedIn:()=>xe,call:()=>x,connection:()=>Z,exportStatus:()=>je,isSettled:()=>_o,listProjects:()=>ee,listTeams:()=>Ct,localStack:()=>_e,noticeFrom:()=>Nt,outdated:()=>Dt,requestExport:()=>et,waitForReady:()=>nt,waitForSettled:()=>tt,whoami:()=>Pe});function Cc(){let e=De();return e?`not signed in, and ${Ne(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token made at https://dashboard.snoutdata.com/#/tokens, or by \`snoutdata tokens create\` on a machine that is signed in.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function Lc(){let e=St();if(e||(e=await wo()),!e)throw new xe;if(!Gr(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${ae}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:de,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new xe;let n=await t.json(),r={...e,accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at};return se(r),r.accessToken}async function x(e,t={},n="POST"){let r=typeof t=="object"&&t?t.ref:void 0;if(typeof r=="string"){let d=_e(r);if(d)throw new N("usage",`${r} is a local project (${d.folder}), and this command is about SnoutData Cloud. Locally: db url, db psql, db push, gen types, keys, status, start, stop, functions, secrets.`,{ref:r,local:d.folder})}let o=await Lc(),s=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",a=await fetch(`${xo}/${e}${s}`,{method:n,headers:{Authorization:`Bearer ${o}`,apikey:de,"Content-Type":"application/json","x-client-info":Rt},body:n==="GET"?void 0:JSON.stringify(t)});Nt(a);let u=await a.text(),i;try{i=u?JSON.parse(u):{}}catch{throw new ie(a.status,u.slice(0,300)||a.statusText)}if(!a.ok){let d=typeof i=="object"&&i&&"error"in i?String(i.error):`${a.status} ${a.statusText}`;throw a.status===401?new xe(d):a.status===410?Dt(a,d):new ie(a.status,d)}return i}function Nt(e){let t=e.headers.get("x-snoutdata-cli-notice");!t||So||(So=!0,W(`${t} ${_n({minimum:e.headers.get("x-snoutdata-cli-minimum")})}`))}function Dt(e,t){let n=e.headers.get("x-snoutdata-cli-minimum");return new N("outdated",`${t.replace(/\s+$/,"")} ${_n({minimum:n})}`,{status:410,installed:J,minimum:n??null})}async function Pe(){return x("cloud-whoami")}async function ee(){return x("cloud-project-list")}async function Z(e){let t=_e(e);return t?zr(t):x("cloud-project-connection",{ref:e})}function _e(e){return Vr(e,Ke(process.cwd())?.local??null)}async function Ct(){return x("cloud-team-list")}async function et(e){return x("cloud-project-export",{ref:e})}async function je(e){return x("cloud-project-export",{ref:e},"GET")}async function tt(e,t,n={}){let r=Date.now()+(n.timeoutMs??3e5);for(;;){let{projects:o}=await ee(),s=o.find(u=>u.ref===e)??null,a=s?.state??"deleted";if(n.onTick?.(a),_o(t,a))return s;if(Date.now()>=r)throw new ie(504,`${e} is still ${a} after waiting`);await new Promise(u=>setTimeout(u,2e3))}}function _o(e,t){let n={pause:"paused",resume:"ready",delete:"deleted"}[e];return t===n||t==="error"}async function nt(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await ee(),o=r.find(s=>s.ref===e);if(!o)throw new ie(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new ie(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var ae,de,xo,ie,xe,So,K=M(()=>{"use strict";he();Ve();X();Me();bo();q();Xe();ae=process.env.SNOUTDATA_ACCOUNTS_URL??"https://accounts.snoutdata.com",de=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzbm91dGRhdGEiLCJyZWYiOiJ6ZjdnNjVtYjhmYXJhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTA4MDcwNDcsImV4cCI6MjEwNDg3NTUyNH0.M-Cb0wFEhJXU21cXtQxZAn-WnRcq4rK2uNSkeHdLucY",xo=`${ae}/functions/v1`,ie=class extends Error{status;constructor(t,n){super(n),this.status=t}},xe=class extends Error{constructor(t){super(t??Cc())}};So=!1});function ir(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function ra(e,t,n){let r="";for(;r.length<n;)r+=ir(e,t);return r}var ar,na,oa=M(()=>{"use strict";ar="0123456789abcdefghjkmnpqrstvwxyz",na="abcdefghjkmnpqrstvwxyz"});function ia(e){return ir(e,sa)+ra(e,ar,aa-1)}function Cu(e){return ju.test(e)}function Q(e){if(!Cu(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function C(e,t=Lu){return Q(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var sa,aa,ju,Lu,ke=M(()=>{"use strict";oa();sa=na,aa=13,ju=new RegExp(`^[${sa}][${ar}]{${aa-1}}$`);Lu="db.snoutdata.com"});var ca=M(()=>{"use strict";ke()});function ua(e,t){let n=la(e,7),r=la(t,24);return Math.max(2,Math.ceil(n*24/r))}function la(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var cr=M(()=>{"use strict"});function lr(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var ur=M(()=>{"use strict"});var da=M(()=>{"use strict"});var mg,ma=M(()=>{"use strict";ke();mg=7*24});var pg,fa=M(()=>{"use strict";pg=7*24*60*60*1e3});var Uu,pa=M(()=>{"use strict";fa();Uu=60*60*1e3});var Be,Sg,ga=M(()=>{"use strict";Be="snout_storage_admin",Sg=`do $$ begin
24
24
  if exists (select from pg_roles where rolname = 'supabase_storage_admin') then
25
- if exists (select from pg_roles where rolname = '${Ue}') then
26
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Ue}';
27
- reassign owned by ${Ue} to supabase_storage_admin;
28
- drop owned by ${Ue};
29
- drop role ${Ue};
25
+ if exists (select from pg_roles where rolname = '${Be}') then
26
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Be}';
27
+ reassign owned by ${Be} to supabase_storage_admin;
28
+ drop owned by ${Be};
29
+ drop role ${Be};
30
30
  end if;
31
- alter role supabase_storage_admin rename to ${Ue};
31
+ alter role supabase_storage_admin rename to ${Be};
32
32
  end if;
33
- end $$;`});var Se,sp,Bt,Bs,ap,Hs=M(()=>{"use strict";Se="snout_realtime_admin",sp=`do $snoutpod$
33
+ end $$;`});var ve,_g,en,ha,kg,ya=M(()=>{"use strict";ve="snout_realtime_admin",_g=`do $snoutpod$
34
34
  begin
35
35
  if exists (select from pg_roles where rolname = 'supabase_admin') then
36
- if exists (select from pg_roles where rolname = '${Se}') then
37
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Se}';
38
- reassign owned by ${Se} to supabase_admin;
39
- drop owned by ${Se};
40
- drop role ${Se};
36
+ if exists (select from pg_roles where rolname = '${ve}') then
37
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${ve}';
38
+ reassign owned by ${ve} to supabase_admin;
39
+ drop owned by ${ve};
40
+ drop role ${ve};
41
41
  end if;
42
- alter role supabase_admin rename to ${Se};
42
+ alter role supabase_admin rename to ${ve};
43
43
  end if;
44
44
  if exists (select from pg_roles where rolname = 'supabase_realtime_admin')
45
- and exists (select from pg_roles where rolname = '${Se}') then
45
+ and exists (select from pg_roles where rolname = '${ve}') then
46
46
  begin
47
- reassign owned by supabase_realtime_admin to ${Se};
47
+ reassign owned by supabase_realtime_admin to ${ve};
48
48
  drop owned by supabase_realtime_admin;
49
49
  drop role supabase_realtime_admin;
50
50
  exception when others then
@@ -53,26 +53,26 @@ begin
53
53
  end if;
54
54
  end
55
55
  $snoutpod$;
56
- `,Bt="snoutdata_realtime",Bs="supabase_realtime",ap=`do $snoutpod$
56
+ `,en="snoutdata_realtime",ha="supabase_realtime",kg=`do $snoutpod$
57
57
  begin
58
- if exists (select 1 from pg_publication where pubname = '${Bt}') then
58
+ if exists (select 1 from pg_publication where pubname = '${en}') then
59
59
  return;
60
60
  end if;
61
- if exists (select 1 from pg_publication where pubname = '${Bs}') then
62
- alter publication ${Bs} rename to ${Bt};
61
+ if exists (select 1 from pg_publication where pubname = '${ha}') then
62
+ alter publication ${ha} rename to ${en};
63
63
  return;
64
64
  end if;
65
- create publication ${Bt};
66
- execute format('alter publication ${Bt} owner to %I',
65
+ create publication ${en};
66
+ execute format('alter publication ${en} owner to %I',
67
67
  (select pg_get_userbyid(datdba) from pg_database where datname = current_database()));
68
68
  end
69
69
  $snoutpod$;
70
- `});var cp,lp,qs=M(()=>{"use strict";cp=2*1024*1024,lp=64*1024});var Gs,Ks,zl,Vl,Yl,Ws=M(()=>{"use strict";Gs="/snoutfn",Ks="/_snoutpod/health",zl="x-snoutdata-door",Vl="SNOUT_FUNCTIONS_DOOR_SECRET",Yl=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
70
+ `});var Eg,$g,wa=M(()=>{"use strict";Eg=2*1024*1024,$g=64*1024});var ba,Sa,Fu,Bu,Hu,xa=M(()=>{"use strict";ba="/snoutfn",Sa="/_snoutpod/health",Fu="x-snoutdata-door",Bu="SNOUT_FUNCTIONS_DOOR_SECRET",Hu=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
71
71
  // every time the agent starts, from packages/snoutpod/src/host/functionMain.ts.
72
- const MOUNT = ${JSON.stringify(Gs)};
73
- const HEALTH = ${JSON.stringify(Ks)};
74
- const DOOR_HEADER = ${JSON.stringify(zl)};
75
- const DOOR = new TextEncoder().encode(Deno.env.get(${JSON.stringify(Vl)}) ?? '');
72
+ const MOUNT = ${JSON.stringify(ba)};
73
+ const HEALTH = ${JSON.stringify(Sa)};
74
+ const DOOR_HEADER = ${JSON.stringify(Fu)};
75
+ const DOOR = new TextEncoder().encode(Deno.env.get(${JSON.stringify(Bu)}) ?? '');
76
76
  if (DOOR.length === 0) {
77
77
  console.error('WARNING: no door secret is set, so a request is not asked to prove it came through the front door');
78
78
  }
@@ -233,9 +233,9 @@ Deno.serve(async (req) => {
233
233
  return refuse(500, 'This function could not be run.', detail);
234
234
  }
235
235
  });
236
- `});function Kn(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function Ht(e){let t=["network","create"],n=e.allow,r=e.deny;if(n!=null&&r!=null)throw new Error("a network is policed by an allow-list or by a deny-list, not by both");if(r!=null)return Zl(e,r);if(n==null){if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no egress policy, which does nothing");return t.push(e.name),t}if(n.length>0)throw new Error("an egress allow-list is not expressible on this runtime: --internal blocks everything or nothing, and a route-based block does not block (measured on podman 5.8.6 / netavark 1.17.2)");if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no allowance to route through it");return t.push("--internal",e.name),t}function Zl(e,t){if(t.length===0)throw new Error("an empty deny-list is not a policy: leave it absent to create an ordinary network");let n=e.subnet,r=e.sink;if(!n||!r)throw new Error("a deny-list needs a pinned subnet and a sink address on it");if(Kn("the subnet",n,Js),Kn("the sink",r,Xl),!Vs(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!Vs(e.gateway,n))throw new Error(`the gateway ${e.gateway} is not inside ${n}`);if(e.gateway&&e.gateway===r)throw new Error("the sink is the gateway, which routes denied traffic at the host rather than nowhere");let o=["network","create","--subnet",n];e.gateway&&o.push("--gateway",e.gateway);for(let s of t)o.push("--route",`${Kn("a denied prefix",s,Js)},${r}`);return o.push(e.name),o}function zs(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function Vs(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(zs(e)&s)>>>0===(zs(n??"")&s)>>>0}function Ys(e){return e?.internal===!0}var Js,Xl,Wn=M(()=>{"use strict";Js=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\/(3[0-2]|[12]?[0-9])$/,Xl=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/});var Ql,qt=M(()=>{"use strict";Ql=7*24*60*60});var Xs=M(()=>{"use strict";be();qs();Ws();Wn();qt()});function Qs(e){let t=[];for(let n of e.split(`
237
- `)){let r=n.trim();if(!r)continue;let[o,s,a,u]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:u?Number(u):null})}return t}var Zs,Jn=M(()=>{"use strict";Zs="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var Op,Np,ea=M(()=>{"use strict";be();qt();Op=6*60*60,Np=5*60});var ta=M(()=>{"use strict"});var na,gg,hg,yg,wg,bg,Sg,ra=M(()=>{"use strict";Ds();Hn();Gn();Cs();js();Us();Fs();Hs();Xs();Jn();ea();ta();qt();na=120,gg=2*6e4,hg=15*60*1e3,yg=60*60*1e3,wg=60*60*1e3,bg=5*60*1e3,Sg=10*60*1e3});import{execFile as tu}from"node:child_process";function oa(){return(e,t,n={})=>new Promise((r,o)=>{let s=tu(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,u,i)=>{if(a&&typeof a.code=="string"){o(a);return}let m=a&&typeof a.code=="number"?a.code:0;r({code:m,stdout:String(u),stderr:String(i)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function I(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
238
- `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var Gt=M(()=>{"use strict"});import{readdir as nu,readFile as Eg}from"node:fs/promises";async function sa(e,t,n={}){let r=n.binary??"podman",o=C(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),i=(Array.isArray(a)?a[0]:a)?.Mountpoint;return i?await(n.look??ru)(i)!=="empty":!1}async function ru(e){try{return(await nu(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var aa=M(()=>{"use strict";be();Gt()});function ia(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can pass to a pod: ${JSON.stringify(t)}`);return t}async function ua(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function da(e){let t=!!(e?.key&&e?.secret),n=["--env",`SNOUTPOD_S3_KEY_TYPE=${t?"shared":"auto"}`];return e&&(t&&(n.push("--env",`SNOUTPOD_S3_KEY=${ia("the access key",e.key,su)}`),n.push("--env",ca),e.token&&n.push("--env",la)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${ia("the endpoint",e.endpoint,ou)}`),e.uriStyle&&n.push("--env",`SNOUTPOD_S3_URI_STYLE=${e.uriStyle==="path"?"path":"host"}`),e.verifyTls===!1&&n.push("--env","SNOUTPOD_S3_VERIFY_TLS=n")),n}function ma(e){if(!e?.key||!e?.secret)return{};if(/[\r\n]/.test(e.secret))throw new Error("the repository secret contains a newline, which would write a second setting");if(e.token&&/[\r\n]/.test(e.token))throw new Error("the session token contains a newline, which would write a second setting");return{[ca]:e.secret,...e.token?{[la]:e.token}:{}}}var ca,la,ou,su,fa=M(()=>{"use strict";ca="SNOUTPOD_S3_SECRET",la="SNOUTPOD_S3_TOKEN",ou=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,su=/^[A-Za-z0-9/+=_.-]{1,256}$/});function Kt(e){return au[e]??e}var pa,ga,de,au,ha,ya=M(()=>{"use strict";pa="authenticator",ga=`do $snoutpod$
236
+ `});function dr(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function tn(e){let t=["network","create"],n=e.allow,r=e.deny;if(n!=null&&r!=null)throw new Error("a network is policed by an allow-list or by a deny-list, not by both");if(r!=null)return Gu(e,r);if(n==null){if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no egress policy, which does nothing");return t.push(e.name),t}if(n.length>0)throw new Error("an egress allow-list is not expressible on this runtime: --internal blocks everything or nothing, and a route-based block does not block (measured on podman 5.8.6 / netavark 1.17.2)");if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no allowance to route through it");return t.push("--internal",e.name),t}function Gu(e,t){if(t.length===0)throw new Error("an empty deny-list is not a policy: leave it absent to create an ordinary network");let n=e.subnet,r=e.sink;if(!n||!r)throw new Error("a deny-list needs a pinned subnet and a sink address on it");if(dr("the subnet",n,_a),dr("the sink",r,qu),!va(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!va(e.gateway,n))throw new Error(`the gateway ${e.gateway} is not inside ${n}`);if(e.gateway&&e.gateway===r)throw new Error("the sink is the gateway, which routes denied traffic at the host rather than nowhere");let o=["network","create","--subnet",n];e.gateway&&o.push("--gateway",e.gateway);for(let s of t)o.push("--route",`${dr("a denied prefix",s,_a)},${r}`);return o.push(e.name),o}function ka(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function va(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(ka(e)&s)>>>0===(ka(n??"")&s)>>>0}function Ea(e){return e?.internal===!0}var _a,qu,mr=M(()=>{"use strict";_a=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\/(3[0-2]|[12]?[0-9])$/,qu=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/});var Ku,nn=M(()=>{"use strict";Ku=7*24*60*60});var $a=M(()=>{"use strict";ke();wa();xa();mr();nn()});function Ra(e){let t=[];for(let n of e.split(`
237
+ `)){let r=n.trim();if(!r)continue;let[o,s,a,u]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:u?Number(u):null})}return t}var Pa,fr=M(()=>{"use strict";Pa="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var zg,Vg,Ta=M(()=>{"use strict";ke();nn();zg=6*60*60,Vg=5*60});var Aa=M(()=>{"use strict"});var Oa,Nh,Dh,Mh,Ih,jh,Ch,Na=M(()=>{"use strict";ca();cr();ur();da();ma();pa();ga();ya();$a();fr();Ta();Aa();nn();Oa=120,Nh=2*6e4,Dh=15*60*1e3,Mh=60*60*1e3,Ih=60*60*1e3,jh=5*60*1e3,Ch=10*60*1e3});import{execFile as Ju}from"node:child_process";function Da(){return(e,t,n={})=>new Promise((r,o)=>{let s=Ju(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,u,i)=>{if(a&&typeof a.code=="string"){o(a);return}let d=a&&typeof a.code=="number"?a.code:0;r({code:d,stdout:String(u),stderr:String(i)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function L(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
238
+ `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var rn=M(()=>{"use strict"});import{readdir as zu,readFile as Hh}from"node:fs/promises";async function Ma(e,t,n={}){let r=n.binary??"podman",o=C(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),i=(Array.isArray(a)?a[0]:a)?.Mountpoint;return i?await(n.look??Vu)(i)!=="empty":!1}async function Vu(e){try{return(await zu(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var Ia=M(()=>{"use strict";ke();rn()});function ja(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can pass to a pod: ${JSON.stringify(t)}`);return t}async function Ua(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function Fa(e){let t=!!(e?.key&&e?.secret),n=["--env",`SNOUTPOD_S3_KEY_TYPE=${t?"shared":"auto"}`];return e&&(t&&(n.push("--env",`SNOUTPOD_S3_KEY=${ja("the access key",e.key,Xu)}`),n.push("--env",Ca),e.token&&n.push("--env",La)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${ja("the endpoint",e.endpoint,Yu)}`),e.uriStyle&&n.push("--env",`SNOUTPOD_S3_URI_STYLE=${e.uriStyle==="path"?"path":"host"}`),e.verifyTls===!1&&n.push("--env","SNOUTPOD_S3_VERIFY_TLS=n")),n}function Ba(e){if(!e?.key||!e?.secret)return{};if(/[\r\n]/.test(e.secret))throw new Error("the repository secret contains a newline, which would write a second setting");if(e.token&&/[\r\n]/.test(e.token))throw new Error("the session token contains a newline, which would write a second setting");return{[Ca]:e.secret,...e.token?{[La]:e.token}:{}}}var Ca,La,Yu,Xu,Ha=M(()=>{"use strict";Ca="SNOUTPOD_S3_SECRET",La="SNOUTPOD_S3_TOKEN",Yu=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,Xu=/^[A-Za-z0-9/+=_.-]{1,256}$/});function on(e){return Zu[e]??e}var qa,Ga,ge,Zu,Ka,Wa=M(()=>{"use strict";qa="authenticator",Ga=`do $snoutpod$
239
239
  declare
240
240
  wanted text[];
241
241
  begin
@@ -253,21 +253,21 @@ begin
253
253
  end loop;
254
254
  end
255
255
  $snoutpod$;
256
- `,de="snout_auth_admin",au={supabase_auth_admin:de};ha=`do $snoutpod$
256
+ `,ge="snout_auth_admin",Zu={supabase_auth_admin:ge};Ka=`do $snoutpod$
257
257
  begin
258
258
  if exists (select from pg_roles where rolname = 'supabase_auth_admin') then
259
- if exists (select from pg_roles where rolname = '${de}') then
260
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${de}';
261
- reassign owned by ${de} to supabase_auth_admin;
262
- drop owned by ${de};
263
- drop role ${de};
259
+ if exists (select from pg_roles where rolname = '${ge}') then
260
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${ge}';
261
+ reassign owned by ${ge} to supabase_auth_admin;
262
+ drop owned by ${ge};
263
+ drop role ${ge};
264
264
  end if;
265
- alter role supabase_auth_admin rename to ${de};
265
+ alter role supabase_auth_admin rename to ${ge};
266
266
  end if;
267
267
  end
268
268
  $snoutpod$;
269
- `});function ba(e){let t=[];for(let n of e.split(`
270
- `)){let r=n.split(Vn);if(r.length!==4||!zn.includes(r[0]))continue;let[o,s,a,u]=r;t.push(u?{name:o,from:s,error:u}:{name:o,from:s,to:a})}return t}var zn,Vn,wa,Sa=M(()=>{"use strict";zn=["snouttime"];for(let e of zn)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);Vn="",wa=`
269
+ `});function za(e){let t=[];for(let n of e.split(`
270
+ `)){let r=n.split(gr);if(r.length!==4||!pr.includes(r[0]))continue;let[o,s,a,u]=r;t.push(u?{name:o,from:s,error:u}:{name:o,from:s,to:a})}return t}var pr,gr,Ja,Va=M(()=>{"use strict";pr=["snouttime"];for(let e of pr)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);gr="",Ja=`
271
271
  CREATE TEMP TABLE snoutpod_extension_updates (name text, from_version text, to_version text, error text);
272
272
  DO $snoutpod$
273
273
  DECLARE
@@ -277,7 +277,7 @@ BEGIN
277
277
  SELECT e.extname, e.extversion
278
278
  FROM pg_extension e
279
279
  JOIN pg_available_extensions a ON a.name = e.extname
280
- WHERE e.extname = ANY (ARRAY[${zn.map(e=>`'${e}'`).join(", ")}])
280
+ WHERE e.extname = ANY (ARRAY[${pr.map(e=>`'${e}'`).join(", ")}])
281
281
  AND a.default_version IS DISTINCT FROM e.extversion
282
282
  LOOP
283
283
  BEGIN
@@ -292,86 +292,91 @@ BEGIN
292
292
  END
293
293
  $snoutpod$;
294
294
  SELECT name, from_version, coalesce(to_version, ''), coalesce(error, '') FROM snoutpod_extension_updates ORDER BY name;
295
- `});import{createHash as Zn,randomBytes as iu}from"node:crypto";import{access as cu,constants as lu}from"node:fs/promises";import{delimiter as uu,join as du}from"node:path";function mu(e,t){return Zn("sha256").update(Qn(e)).update(e.smtpSecretEnv&&t?Zn("sha256").update(t).digest("hex"):"").digest("hex").slice(0,32)}function Qn(e){if(Array.isArray(e))return`[${e.map(Qn).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().filter(r=>t[r]!==void 0).map(r=>`${JSON.stringify(r)}:${Qn(t[r])}`).join(",")}}`}return JSON.stringify(e??null)}function nr(e){if(!pu.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function it(e){if(!gu.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function hu(e,t,n){return`postgres://${it(t)}:${n}@127.0.0.1:5432/${Q(e)}`}function ka(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=Kt(t.dbUriRole),a=n.get(s);if(a===void 0)throw new Error(`no password was applied for role ${JSON.stringify(t.dbUriRole)}, which ${JSON.stringify(t.name)} needs to reach its database`);o[t.dbUriEnv]=hu(e.ref,s,a)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),yu(t.image,o)}function yu(e,t){if(!/(^|\/)snout-auth([:@]|$)/.test(e))return{...t};let n={};for(let[r,o]of Object.entries(t)){let s=r.startsWith("GOTRUE_")?`AUTH_${r.slice(7)}`:r==="API_EXTERNAL_URL"?"AUTH_API_EXTERNAL_URL":r;s!==r&&s in t||(n[s]=o)}return n}function va(e,t){return`${C(e).pod}-${nr(t)}`}function Wt(e){return`snoutpod-host-${nr(e)}`}function wu(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return Zn("sha256").update(JSON.stringify([e.image,e.port,e.hostPort,e.healthPath??null,e.memoryMb,t,e.adminPort??null,e.adminHostPort??null,e.command??null,e.volumes??null,e.network??null])).digest("hex").slice(0,32)}function bu(e,t){let n=[],r=er(t);for(let o of e??[])if(nr(o.name),tr(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){tr(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function er(e){let t=e===void 0?[]:typeof e=="string"?[e]:[...e];return["127.0.0.1",...new Set(t.filter(n=>n!=="127.0.0.1"))]}function tr(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function Pa(e){let t=[];for(let n of Object.keys(e)){if(!Su.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function xu(e){let t=e.healthPath;if(t==null||t==="")return[];if(!Aa.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(tr(e.hostPort,`${e.name}: hostPort`),["--label",`${rr}=${e.hostPort}:${t}`])}function _u(e){if(!e)return null;let t=e.indexOf(":");if(t<=0)return null;let n=Number(e.slice(0,t)),r=e.slice(t+1);return!Number.isInteger(n)||n<1||n>65535||!Aa.test(r)?null:{port:n,path:r}}async function vu(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>cu(o,lu.X_OK));for(let o of n.split(uu))if(o)try{let s=du(o,e);return await r(s),s}catch{}return null}function Eu(){return["\\set ON_ERROR_STOP 0","select 'create extension if not exists pg_prewarm' where exists (select 1 from pg_available_extensions where name = 'pg_prewarm') \\gexec","with candidate as ("," select c.oid, pg_relation_size(c.oid) as bytes"," from pg_class c join pg_namespace n on n.oid = c.relnamespace"," where c.relkind in ('r', 'i', 'm')"," and c.relpersistence = 'p'"," and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')","), budgeted as ("," select oid, bytes,"," sum(bytes) over (order by bytes, oid rows between unbounded preceding and current row) as running"," from candidate",")","select coalesce(sum(pg_prewarm(oid)), 0) from budgeted","where bytes > 0 and running <= pg_size_bytes(current_setting('shared_buffers')) * 3 / 4;",""].join(`
296
- `)}function $u(e){return/^(\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)\//.exec(e)?.[1]??null}function Tu(e){if(!e)return[];let t=[],n=/^[1-9][0-9]{0,6}(kB|MB|GB)$/;typeof e.sharedBuffers=="string"&&n.test(e.sharedBuffers)&&t.push(`SNOUTPOD_SHARED_BUFFERS=${e.sharedBuffers}`),typeof e.workMem=="string"&&n.test(e.workMem)&&t.push(`SNOUTPOD_WORK_MEM=${e.workMem}`);let r=typeof e.maxConnections=="number"&&Number.isInteger(e.maxConnections)&&e.maxConnections>=1&&e.maxConnections<=1e4?e.maxConnections:$a;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,$a)}`),typeof e.privilegedExtensions=="string"&&/^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/.test(e.privilegedExtensions)&&t.push(`SNOUTPOD_PRIVILEGED_EXTENSIONS=${e.privilegedExtensions}`),t}function Au(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??ku;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await vu(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function Oa(e){let t=e.binary??"podman",n=e.exec,r=Au(e),o=e.probe??Ra;async function s(d){try{await n(t,["exec","--interactive",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"--quiet","--file","-"],{timeoutMs:Pu,stdin:Eu()})}catch{}}async function a(){let d=await I(n,t,["ps","--all","--filter",`label=${Fe}`,"--format","json"]),p=d.trim()?JSON.parse(d):[],w=new Map,_=new Map;for(let k of p){let R=k.Labels?.[Fe],x=k.Labels?.[st];if(!R||!x)continue;let T=w.get(R)??[];T.push(x),w.set(R,T);let D=Number(k.Labels?.[Xn]??0),K=Number.isInteger(D)?D:0;_.set(R,Math.max(_.get(R)??K,K))}let h=[];for(let k of p){let R=k.Labels?.[Fe];if(!R||k.Labels?.[st])continue;let x=Number(k.Labels?.[Yn]);h.push({ref:R,status:Ru(k.State),generation:Number.isInteger(x)?x:null,hasData:await e.hasData(R),services:w.get(R)??[],..._.has(R)?{servicesGeneration:_.get(R)}:{}})}return h}async function u(d){let p=C(d.ref),w=e.credentials,_=await ua(e.repository,w?()=>w.forProject(d.ref,d.bucket):null),h={SNOUTPOD_OWNER_PASSWORD:d.ownerPassword,POSTGRES_PASSWORD:d.adminPassword,...ma(_)},k=[...await r(),"run","--detach","--name",p.pod,"--label",`${Fe}=${d.ref}`,"--label",`${Yn}=${d.generation}`,"--userns=auto","--memory",`${d.memoryMb}m`,...Nu(d.cpus),"--restart","no",...e.network?["--network",e.network]:[],...d.port?er(e.podBind).flatMap(R=>["--publish",`${R}:${d.port}:5432`]):[],...bu(d.services,e.podBind),"--volume",`${p.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${d.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${d.bucket}`,"--env",`SNOUTPOD_S3_REGION=${d.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...Tu(d.tuning).flatMap(R=>["--env",R]),"--env",`SNOUTPOD_RETENTION_FULL=${Is(d.backup?.retentionDays,d.backup?.fullIntervalHours)}`,...da(_),d.image,"snoutpod-postgres"];await I(n,t,k,{env:h})}async function i(d){let p=await I(n,t,["ps","--all","--filter",`label=${Fe}=${Q(d)}`,"--filter",`label=${st}`,"--format","json"]),w=p.trim()?JSON.parse(p):[],_=[];for(let h of w){let k=h.Labels?.[st];if(!k)continue;let R=Number(h.Labels?.[Xn]??0);_.push({name:k,container:va(d,k),spec:h.Labels?.[xa]??"",pod:h.Labels?.[_a]??"",running:h.State==="running",generation:Number.isInteger(R)?R:0})}return _}async function m(d){let p=await n(t,["container","inspect","--format","{{.Id}}",C(d).pod]);return p.code===0?p.stdout.trim():""}async function l(d,p){let w=[];for(let x of p){let T=x.dbUriRole?Kt(x.dbUriRole):null;x.dbUriEnv&&T&&!w.includes(T)&&w.push(it(T))}let _=new Map;if(w.length===0)return _;let h={},k=[],R=w.includes(de)?[ha]:[];return w.includes(pa)&&R.push(ga),w.forEach((x,T)=>{let D=iu(24).toString("base64url"),K=`SNOUTPOD_SERVICE_PASSWORD_${T}`;_.set(x,D),h[K]=D,k.push("--env",K),R.push(`\\getenv service_password_${T} ${K}`),R.push(`\\set service_role_${T} ${x}`),R.push(`alter role :"service_role_${T}" with password :'service_password_${T}';`)}),R.push(""),await I(n,t,["exec","--interactive",...k,C(d.ref).pod,"psql","--username","snoutpod_admin","--dbname",C(d.ref).database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:h,stdin:R.join(`
297
- `)}),_}async function f(d){let p=d.services??[],w=await i(d.ref),_;p.some(E=>E.smtpSecretEnv)&&(_=await e.smtpSecret?.().catch(()=>{}));let h=p.length>0?await m(d.ref):"",k=new Map(w.map(E=>[E.name,E])),R=new Map(p.map(E=>[E.name,mu(E,_)])),x=new Set(p.filter(E=>{let te=k.get(E.name);return!te||!te.running||te.spec!==R.get(E.name)||h===""||te.pod!==h}).map(E=>E.name)),T=d.servicesGeneration??0;if(x.size===0&&p.length>0&&!w.some(E=>E.generation>=T))for(let E of p)x.add(E.name);let D=E=>E.dbUriEnv&&E.dbUriRole?Kt(E.dbUriRole):null;for(let E=!0;E;){E=!1;let te=new Set(p.filter(ee=>x.has(ee.name)).map(D));for(let ee of p){let mr=D(ee);!x.has(ee.name)&&mr!==null&&te.has(mr)&&(x.add(ee.name),E=!0)}}for(let E of w)(x.has(E.name)||!R.has(E.name))&&await I(n,t,["rm","--force","--ignore",E.container]);let K=p.filter(E=>x.has(E.name)),He=await l(d,K);for(let E of K){let te=va(d.ref,E.name),ee=[...await r(),"run","--detach","--name",te,"--label",`${Fe}=${d.ref}`,"--label",`${Yn}=${d.generation}`,"--label",`${st}=${E.name}`,"--label",`${Xn}=${d.servicesGeneration??0}`,"--label",`${xa}=${R.get(E.name)??""}`,"--label",`${_a}=${h}`,"--network",`container:${C(d.ref).pod}`,"--userns",`container:${C(d.ref).pod}`,"--memory",`${E.memoryMb}m`,"--restart","no",...xu(E),...Pa(ka(d,E,He,_)),E.image];await I(n,t,ee,{env:ka(d,E,He,_)})}}return{list:a,create:u,createServices:f,async start(d){await I(n,t,["start",C(d).pod])},async startServices(d){for(let p of await i(d))await I(n,t,["start",p.container])},async applyHostServices(d){let p=[],w=await I(n,t,["ps","--all","--filter",`label=${at}`,"--format","json"]),_=w.trim()?JSON.parse(w):[],h=new Map;for(let x of _){let T=x.Labels?.[at];T&&h.set(T,{spec:x.Labels?.[Ea]??"",state:x.State??""})}for(let x of d)try{await R(x)}catch(T){p.push({name:x.name,action:"failed",reason:T instanceof Error?T.message:String(T)})}async function k(x){let T=await r();if((await n(t,[...T,"image","exists",x])).code===0)return;let D=await n(t,[...T,"pull","--quiet",x]);if(D.code===0)return;let K=$u(x);if(!K||!e.registryLogin)throw new Error(`${x} could not be pulled: ${(D.stderr||D.stdout).trim()}`);await e.registryLogin(K),await I(n,t,[...T,"pull","--quiet",x])}async function R(x){let T=e.hostServiceLocal?.(x,d),D=T?{...x,...T.env&&Object.keys(T.env).length>0?{env:{...x.env,...T.env}}:{},...T.volumes?{volumes:T.volumes}:{},...T.network?{network:T.network}:{}}:x,K=Wt(D.name),He=wu(D),E=h.get(D.name);if(h.delete(D.name),E&&E.spec===He){E.state!=="running"&&(await I(n,t,["start",K]),p.push({name:D.name,action:"started",reason:E.state}));return}let te=E?"the spec changed":"it was not here";await k(D.image),E&&await I(n,t,["rm","--force","--ignore",K]),await I(n,t,[...await r(),"run","--detach","--name",K,"--label",`${at}=${D.name}`,"--label",`${Ea}=${He}`,...D.healthPath?["--label",`${rr}=${D.hostPort}:${D.healthPath}`]:[],"--memory",`${D.memoryMb}m`,"--restart","no",...D.network?["--network",D.network]:e.network?["--network",e.network]:[],...(D.volumes??[]).flatMap(ee=>["--volume",ee]),...er(e.podBind).flatMap(ee=>["--publish",`${ee}:${D.hostPort}:${D.port}`]),...D.adminPort&&D.adminHostPort?["--publish",`127.0.0.1:${D.adminHostPort}:${D.adminPort}`]:[],...Pa(D.env),D.image,...D.command??[]],{env:{...D.env}}),p.push({name:D.name,action:E?"recreated":"created",reason:te})}for(let x of h.keys())await I(n,t,["rm","--force","--ignore",Wt(x)]),p.push({name:x,action:"removed",reason:"it is no longer on the list"});return p},async hostServiceHealth(d){let p=await I(n,t,["ps","--all","--filter",`label=${at}`,"--format","json"]),w=(p.trim()?JSON.parse(p):[]).map(h=>h.Labels?.[at]??"").filter(h=>h!=="");if(w.length===0)return[];let _=Date.now()+d;for(;;){let h=[];for(let k of w)h.push(await Ta(n,t,k,Wt(k),o));if(h.every(k=>k.ok)||Date.now()>=_)return h;await new Promise(k=>setTimeout(k,500))}},async serviceHealth(d,p){let w=await i(d);if(w.length===0)return[];let _=Date.now()+p;for(;;){let h=[];for(let k of w)h.push(await Ta(n,t,k.name,k.container,o));if(h.every(k=>k.ok)||Date.now()>=_)return h;await new Promise(k=>setTimeout(k,500))}},async stop(d,p){for(let w of await i(d))await I(n,t,["stop","--time",String(fu),w.container]);await I(n,t,["stop","--time",String(p),C(d).pod])},async remove(d){for(let p of await i(d))await I(n,t,["rm","--force","--ignore",p.container]);await I(n,t,["rm","--force","--ignore",C(d).pod])},async removeData(d){await I(n,t,["volume","rm","--force",C(d).pod])},async applyOwnerPassword(d){let p=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${d.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
298
- `);await I(n,t,["exec","--interactive","--env","SNOUTPOD_OWNER_PASSWORD",C(d.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:d.ownerPassword},stdin:p})},async applyRolePassword(d,p,w){let _=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${it(p)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
299
- `);await I(n,t,["exec","--interactive","--env","SNOUTPOD_ROLE_PASSWORD",C(d).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:w},stdin:_})},async updateExtensions(d){let p=C(d),w=await I(n,t,["exec","--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-tA","-F",Vn,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:wa});return ba(w)},async runProjectSql(d,p){let w=C(d);await I(n,t,["exec","--interactive",w.pod,"psql","--username","snoutpod_admin","--dbname",w.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:p})},async ensureHostServiceDatabase(d,p,w){let _=Wt(d),h=it(p),k=it(w),R=T=>["exec",_,"psql","--username",h,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",T];(await I(n,t,R(`select 1 from pg_database where datname = '${k}'`))).trim()!=="1"&&await I(n,t,R(`create database "${k}"`))},async replicationSlots(d){let p=await I(n,t,["exec",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"-tA","-F","|","-c",Zs]);return Qs(p)},async dropReplicationSlot(d,p){if(!/^[a-z0-9_]{1,63}$/.test(p))throw new Error(`not a replication slot name: ${JSON.stringify(p)}`);await I(n,t,["exec",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"-v","ON_ERROR_STOP=1","-c",`select pg_drop_replication_slot('${p}')`])},async renameProject(d,p){p!==d.ref&&await I(n,t,["exec","--interactive",C(d.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:Ou(p,d.ref)})},async waitReady(d,p){let w=C(d).pod,_=Date.now()+p;for(;;){let h=await n(t,["exec",w,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(h.code===0&&h.stdout.trim()==="t")return s(d),!0;if(Date.now()>=_)return!1;await new Promise(k=>setTimeout(k,500))}},async capacity(){let d=await a(),p=await e.capacity();return{pods:d.length,...p}},async databaseBytes(d){let p=await n(t,["exec",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"-tAc","select pg_database_size(current_database())"]);return p.code===0?qn(p.stdout):null},async storageBytes(d){let p=await n(t,["exec",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"-tAc","select coalesce(sum((metadata->>'size')::bigint), 0) from storage.objects"]);return p.code===0?qn(p.stdout):null},async setReadOnly(d,p){let w=C(d);await I(n,t,["exec",w.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${w.database} set default_transaction_read_only = ${p?"on":"off"}`])},async archiveState(d){let p=await n(t,["exec",C(d).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",d,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), case when last_failed_time > coalesce(last_archived_time, '-infinity') then failed_count else 0 end from pg_stat_archiver`]);if(p.code!==0)return null;let[w="",_=""]=p.stdout.trim().split(`
300
- `)[0]?.split("|")??[],h=Number(_);return{lastArchivedAt:w.trim()||null,failures:Number.isInteger(h)&&h>=0?h:0}}}}async function Ta(e,t,n,r,o=Ra){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,u;try{let f=JSON.parse(s.stdout);a=f[0]?.State,u=f[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let i=a?.Status??"unknown";if(i!=="running")return{name:n,ok:!1,detail:i};let m=_u(u?.[rr]);if(!m)return{name:n,ok:!0,detail:"running"};let l=await o(m.port,m.path);return l===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${l}`}}function Ru(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function Ou(e,t){return[`\\set from_db ${e}`,`\\set to_db ${t}`,`\\set from_role ${e}_owner`,`\\set to_role ${t}_owner`,'alter database :"from_db" rename to :"to_db";','alter role :"from_role" rename to :"to_role";',"alter system reset restore_command;","alter system reset recovery_target_time;","alter system reset recovery_target_action;",""].join(`
301
- `)}function Nu(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var Fe,Yn,st,Xn,xa,_a,fu,pu,gu,at,Ea,Su,Aa,rr,Ra,ku,Pu,$a,Na=M(()=>{"use strict";be();Hn();Jn();Gt();fa();Gn();ya();Sa();Fe="snoutpod.ref",Yn="snoutpod.generation",st="snoutpod.service",Xn="snoutpod.services-generation",xa="snoutpod.service-spec",_a="snoutpod.service-pod";fu=10,pu=/^[a-z][a-z0-9-]{0,30}$/;gu=/^[a-z][a-z0-9_]{0,62}$/;at="snoutpod.host-service",Ea="snoutpod.host-service-spec";Su=/^[A-Za-z_][A-Za-z0-9_]*$/;Aa=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,rr="snoutpod.health";Ra=async(e,t)=>{let{request:n}=await import("node:http");return new Promise(r=>{let o=n({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:2e3},s=>{s.resume(),r(s.statusCode??null)});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null)}),o.end()})},ku="crun";Pu=3e4;$a=200});var Ha={};fr(Ha,{DEFAULT_LOCAL_MEMORY_MB:()=>Ia,DEFAULT_LOCAL_PORT:()=>Cu,ENGINE:()=>G,ENGINE_NAME:()=>ar,IMAGE:()=>$e,NETWORK:()=>xe,NO_BLACKHOLE_ROUTES:()=>ja,PUBLIC_IMAGE:()=>Be,capacity:()=>Ca,ensureImage:()=>Fu,ensureNetwork:()=>Ku,exec:()=>J,hasData:()=>Jt,localPsql:()=>zu,localRuntime:()=>Gu,localSpec:()=>Fa,localStatus:()=>ir,localUri:()=>Ua,networkPolicyInPlace:()=>La,newLocalPassword:()=>Lu,newLocalRef:()=>ju,podLog:()=>Ba,podName:()=>zt,podmanReady:()=>Hu,removeLocal:()=>Xu,startLocal:()=>Ju,stopLocal:()=>Yu});import{randomBytes as Ma}from"node:crypto";import{existsSync as Da}from"node:fs";import{platform as sr}from"node:os";import{delimiter as Du,join as Mu}from"node:path";function Iu(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=sr()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(Du))if(t&&e.some(n=>Da(Mu(t,n))))return"podman";return Da("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function ju(){return Ns(e=>Ma(e))}function Lu(){return Ma(18).toString("base64url")}async function Fu(e,t=J){if(process.env.SNOUTPOD_IMAGE||(await t(G,["image","exists",$e])).code===0)return null;e?.(`Fetching the database image (${Be}). This happens once.`);let n=await t(G,["pull",Be],{timeoutMs:Bu}).catch(o=>({code:1,stdout:"",stderr:o instanceof Error?o.message:String(o)}));if(n.code!==0)return`could not fetch the database image ${Be}: ${n.stderr.trim()||n.stdout.trim()||"the pull failed"}`;let r=await t(G,["tag",Be,$e]);return r.code!==0?`fetched ${Be} but could not tag it as ${$e}: ${r.stderr.trim()}`:(e?.("Got it."),null)}async function Hu(e=Uu){if(ar!=="podman")return`SNOUTPOD_ENGINE=${ar}: a local project is Podman-only, because a database that works under Docker and not under Podman is a database that works here and not in the cloud`;let t;try{t=await J(G,["--version"])}catch{return`${G} is not installed. macOS: brew install podman, then podman machine init && podman machine set --rootful=false && podman machine start. Windows: winget install RedHat.Podman, then the same three machine commands. Linux: your distribution's podman package.`}if(t.code!==0)return`${G} --version failed: ${t.stderr.trim()||t.stdout.trim()}`;if((await J(G,["image","exists",$e])).code!==0)return`the pod image ${$e} is not on this machine. ${e}`;let r=await J(G,["machine","inspect","--format","{{.Rootful}}"]);return r.code===0&&r.stdout.trim().startsWith("true")?"the podman machine is ROOTFUL, and the hosts are not: podman machine stop && podman machine set --rootful=false && podman machine start":null}function qu(e){let t=`if out=$(ls -A ${JSON.stringify(e)} 2>/dev/null); then if [ -n "$out" ]; then echo DATA; else echo EMPTY; fi; else echo HIDDEN; fi`;return J(G,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
302
- `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function Jt(e){let t=sr()==="darwin"||sr()==="win32";return sa(J,e,{binary:G,look:t?qu:void 0})}async function Ca(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function Gu(e){return Oa({exec:J,binary:G,network:xe,hasData:Jt,capacity:Ca,repository:e})}function zt(e){return C(e).pod}async function Ku(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Wu()||await J(G,Ht({name:xe}));return}let t=Ht({name:xe,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await La())return;await e?.(),await J(G,["network","rm","--force",xe]).catch(()=>{});let o=await J(G,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${ja}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${xe} with egress blocked${n?`: ${n}`:""}`)}async function Wu(){return(await J(G,["network","inspect",xe,"--format","json"])).code===0}async function La(){let e=await J(G,["network","inspect",xe,"--format","json"]);if(e.code!==0)return!1;try{return Ys(JSON.parse(e.stdout)?.[0])}catch{return!1}}function Ua(e){let t=C(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function Fa(e,t={}){return Q(e.ref),{ref:e.ref,generation:1,image:t.image??$e,memoryMb:t.memoryMb??Ia,cpus:null,bucket:"",region:"us-east-1",ownerPassword:e.ownerPassword,adminPassword:e.adminPassword,port:e.port,tuning:t.tuning??null,backup:null,services:t.services??null}}function or(e,t){e.onProgress?.(t)}async function Ju(e,t,n={}){let r=Fa(t,n),o=n.timeoutMs??12e4,s=n.hasData??Jt,a=(await e.list()).find(u=>u.ref===t.ref);if(a?a.status==="stopped"?(or(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):or(n,"The database is already running."):(or(n,await s(t.ref)?"Starting the database over its existing data.":"Creating the database."),await e.create(r)),!await e.waitReady(t.ref,o))throw new Error(await Vu(t.ref,o,n.podLog??Ba));return await e.applyOwnerPassword(r),await e.createServices(r),ir(e,t,n)}async function zu(e,t){let n=C(e.ref);return J(G,["exec","--interactive","-e","PGPASSWORD",zt(e.ref),"psql","--host","127.0.0.1","--port","5432","--username",n.ownerRole,"--dbname",n.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA","--file","-"],{stdin:t,env:{PGPASSWORD:e.ownerPassword}})}async function Ba(e){let t=await J(G,["logs","--tail","20",zt(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function Vu(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${zt(e)} never became ready in ${t}ms
295
+ `});import{createHash as wr,randomBytes as Qu}from"node:crypto";import{access as ed,constants as td}from"node:fs/promises";import{delimiter as nd,join as rd}from"node:path";function od(e,t){return wr("sha256").update(br(e)).update(e.smtpSecretEnv&&t?wr("sha256").update(t).digest("hex"):"").digest("hex").slice(0,32)}function br(e){if(Array.isArray(e))return`[${e.map(br).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().filter(r=>t[r]!==void 0).map(r=>`${JSON.stringify(r)}:${br(t[r])}`).join(",")}}`}return JSON.stringify(e??null)}function _r(e){if(!ad.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function mt(e){if(!id.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function cd(e,t,n){return`postgres://${mt(t)}:${n}@127.0.0.1:5432/${Q(e)}`}function Za(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=on(t.dbUriRole),a=n.get(s);if(a===void 0)throw new Error(`no password was applied for role ${JSON.stringify(t.dbUriRole)}, which ${JSON.stringify(t.name)} needs to reach its database`);o[t.dbUriEnv]=cd(e.ref,s,a)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),ld(t.image,o)}function ld(e,t){if(!/(^|\/)snout-auth([:@]|$)/.test(e))return{...t};let n={};for(let[r,o]of Object.entries(t)){let s=r.startsWith("GOTRUE_")?`AUTH_${r.slice(7)}`:r==="API_EXTERNAL_URL"?"AUTH_API_EXTERNAL_URL":r;s!==r&&s in t||(n[s]=o)}return n}function Qa(e,t){return`${C(e).pod}-${_r(t)}`}function sn(e){return`snoutpod-host-${_r(e)}`}function ud(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return wr("sha256").update(JSON.stringify([e.image,e.port,e.hostPort,e.healthPath??null,e.memoryMb,t,e.adminPort??null,e.adminHostPort??null,e.command??null,e.volumes??null,e.network??null])).digest("hex").slice(0,32)}function dd(e,t){let n=[],r=Sr(t);for(let o of e??[])if(_r(o.name),xr(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){xr(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function Sr(e){let t=e===void 0?[]:typeof e=="string"?[e]:[...e];return["127.0.0.1",...new Set(t.filter(n=>n!=="127.0.0.1"))]}function xr(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function ti(e){let t=[];for(let n of Object.keys(e)){if(!md.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function fd(e){let t=e.healthPath;if(t==null||t==="")return[];if(!oi.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(xr(e.hostPort,`${e.name}: hostPort`),["--label",`${kr}=${e.hostPort}:${t}`])}function pd(e){if(!e)return null;let t=e.indexOf(":");if(t<=0)return null;let n=Number(e.slice(0,t)),r=e.slice(t+1);return!Number.isInteger(n)||n<1||n>65535||!oi.test(r)?null:{port:n,path:r}}async function hd(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>ed(o,td.X_OK));for(let o of n.split(nd))if(o)try{let s=rd(o,e);return await r(s),s}catch{}return null}function yd(){return["\\set ON_ERROR_STOP 0","select 'create extension if not exists pg_prewarm' where exists (select 1 from pg_available_extensions where name = 'pg_prewarm') \\gexec","with candidate as ("," select c.oid, pg_relation_size(c.oid) as bytes"," from pg_class c join pg_namespace n on n.oid = c.relnamespace"," where c.relkind in ('r', 'i', 'm')"," and c.relpersistence = 'p'"," and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')","), budgeted as ("," select oid, bytes,"," sum(bytes) over (order by bytes, oid rows between unbounded preceding and current row) as running"," from candidate",")","select coalesce(sum(pg_prewarm(oid)), 0) from budgeted","where bytes > 0 and running <= pg_size_bytes(current_setting('shared_buffers')) * 3 / 4;",""].join(`
296
+ `)}function bd(e){return/^(\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)\//.exec(e)?.[1]??null}function Sd(e,t){for(let r of e??[]){let o=/^PG_MAJOR=(\d+)$/.exec(r.trim());if(o)return o[1]??null}let n=t.split("@")[0]??"";return/:(\d+)(?:[.-][^/:]*)?$/.exec(n)?.[1]??null}function vr(e){let t=(e??"").trim();return/^\d+(?:\.\d+)?$/.test(t)?t:null}function xd(e,t){return t.image===null||t.data===null||t.image===t.data?null:`${e} was not started: its data directory was made by Postgres ${t.data} and it was handed a Postgres ${t.image} image. A data directory starts only on the major that made it. Its data is untouched, and it starts again on a ${t.data} image.`}async function _d(e,t,n,r){return{image:await kd(e,t,r),data:await vd(e,t,n)}}async function kd(e,t,n){let r=null;try{let o=await e(t,["image","inspect","--format","{{json .Config.Env}}",n]);if(o.code===0&&o.stdout.trim()){let s=JSON.parse(o.stdout.trim());r=Array.isArray(s)?s.filter(a=>typeof a=="string"):null}}catch{r=null}return Sd(r,n)}async function vd(e,t,n){try{let r=await e(t,["volume","inspect","--format","{{.Mountpoint}}",C(n).pod]),o=r.code===0?r.stdout.trim().replace(/\/+$/,""):"";if(!o)return null;let s=await e(t,["unshare","cat",`${o}/PG_VERSION`]);return s.code===0?vr(s.stdout):null}catch{return null}}function Ed(e){if(!e)return[];let t=[],n=/^[1-9][0-9]{0,6}(kB|MB|GB)$/;typeof e.sharedBuffers=="string"&&n.test(e.sharedBuffers)&&t.push(`SNOUTPOD_SHARED_BUFFERS=${e.sharedBuffers}`),typeof e.workMem=="string"&&n.test(e.workMem)&&t.push(`SNOUTPOD_WORK_MEM=${e.workMem}`);let r=typeof e.maxConnections=="number"&&Number.isInteger(e.maxConnections)&&e.maxConnections>=1&&e.maxConnections<=1e4?e.maxConnections:ni;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,ni)}`),typeof e.privilegedExtensions=="string"&&/^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/.test(e.privilegedExtensions)&&t.push(`SNOUTPOD_PRIVILEGED_EXTENSIONS=${e.privilegedExtensions}`),t}function $d(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??gd;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await hd(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function ai(e){let t=e.binary??"podman",n=e.exec,r=$d(e),o=e.probe??si;async function s(m){try{await n(t,["exec","--interactive",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"--quiet","--file","-"],{timeoutMs:wd,stdin:yd()})}catch{}}async function a(){let m=await L(n,t,["ps","--all","--filter",`label=${He}`,"--format","json"]),p=m.trim()?JSON.parse(m):[],S=new Map,w=new Map;for(let $ of p){let A=$.Labels?.[He],k=$.Labels?.[ut];if(!A||!k)continue;let j=S.get(A)??[];j.push(k),S.set(A,j);let I=Number($.Labels?.[yr]??0),z=Number.isInteger(I)?I:0;w.set(A,Math.max(w.get(A)??z,z))}let _=[];for(let $ of p){let A=$.Labels?.[He];if(!A||$.Labels?.[ut])continue;let k=Number($.Labels?.[hr]);_.push({ref:A,status:Pd($.State),generation:Number.isInteger(k)?k:null,hasData:await e.hasData(A),services:S.get(A)??[],...w.has(A)?{servicesGeneration:w.get(A)}:{}})}return _}async function u(m){let p=await(e.majors??((S,w)=>_d(n,t,S,w)))(m.ref,m.image);return xd(m.ref,p)}async function i(m){let p=C(m.ref),S=await u(m);if(S)throw new Error(S);let w=e.credentials,_=await Ua(e.repository,w?()=>w.forProject(m.ref,m.bucket):null),$={SNOUTPOD_OWNER_PASSWORD:m.ownerPassword,POSTGRES_PASSWORD:m.adminPassword,...Ba(_)},A=[...await r(),"run","--detach","--name",p.pod,"--label",`${He}=${m.ref}`,"--label",`${hr}=${m.generation}`,"--userns=auto","--memory",`${m.memoryMb}m`,...Td(m.cpus),"--restart","no",...e.network?["--network",e.network]:[],...m.port?Sr(e.podBind).flatMap(k=>["--publish",`${k}:${m.port}:5432`]):[],...dd(m.services,e.podBind),"--volume",`${p.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${m.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${m.bucket}`,"--env",`SNOUTPOD_S3_REGION=${m.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...Ed(m.tuning).flatMap(k=>["--env",k]),"--env",`SNOUTPOD_RETENTION_FULL=${ua(m.backup?.retentionDays,m.backup?.fullIntervalHours)}`,...Fa(_),m.image,"snoutpod-postgres"];await L(n,t,A,{env:$})}async function d(m){let p=await L(n,t,["ps","--all","--filter",`label=${He}=${Q(m)}`,"--filter",`label=${ut}`,"--format","json"]),S=p.trim()?JSON.parse(p):[],w=[];for(let _ of S){let $=_.Labels?.[ut];if(!$)continue;let A=Number(_.Labels?.[yr]??0);w.push({name:$,container:Qa(m,$),spec:_.Labels?.[Ya]??"",pod:_.Labels?.[Xa]??"",running:_.State==="running",generation:Number.isInteger(A)?A:0})}return w}async function l(m){let p=await n(t,["container","inspect","--format","{{.Id}}",C(m).pod]);return p.code===0?p.stdout.trim():""}async function f(m,p){let S=[];for(let k of p){let j=k.dbUriRole?on(k.dbUriRole):null;k.dbUriEnv&&j&&!S.includes(j)&&S.push(mt(j))}let w=new Map;if(S.length===0)return w;let _={},$=[],A=S.includes(ge)?[Ka]:[];return S.includes(qa)&&A.push(Ga),S.forEach((k,j)=>{let I=Qu(24).toString("base64url"),z=`SNOUTPOD_SERVICE_PASSWORD_${j}`;w.set(k,I),_[z]=I,$.push("--env",z),A.push(`\\getenv service_password_${j} ${z}`),A.push(`\\set service_role_${j} ${k}`),A.push(`alter role :"service_role_${j}" with password :'service_password_${j}';`)}),A.push(""),await L(n,t,["exec","--interactive",...$,C(m.ref).pod,"psql","--username","snoutpod_admin","--dbname",C(m.ref).database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:_,stdin:A.join(`
297
+ `)}),w}async function h(m){let p=m.services??[],S=await d(m.ref),w;p.some(P=>P.smtpSecretEnv)&&(w=await e.smtpSecret?.().catch(()=>{}));let _=p.length>0?await l(m.ref):"",$=new Map(S.map(P=>[P.name,P])),A=new Map(p.map(P=>[P.name,od(P,w)])),k=new Set(p.filter(P=>{let oe=$.get(P.name);return!oe||!oe.running||oe.spec!==A.get(P.name)||_===""||oe.pod!==_}).map(P=>P.name)),j=m.servicesGeneration??0;if(k.size===0&&p.length>0&&!S.some(P=>P.generation>=j))for(let P of p)k.add(P.name);let I=P=>P.dbUriEnv&&P.dbUriRole?on(P.dbUriRole):null;for(let P=!0;P;){P=!1;let oe=new Set(p.filter(ne=>k.has(ne.name)).map(I));for(let ne of p){let Mr=I(ne);!k.has(ne.name)&&Mr!==null&&oe.has(Mr)&&(k.add(ne.name),P=!0)}}for(let P of S)(k.has(P.name)||!A.has(P.name))&&await L(n,t,["rm","--force","--ignore",P.container]);let z=p.filter(P=>k.has(P.name)),Ge=await f(m,z);for(let P of z){let oe=Qa(m.ref,P.name),ne=[...await r(),"run","--detach","--name",oe,"--label",`${He}=${m.ref}`,"--label",`${hr}=${m.generation}`,"--label",`${ut}=${P.name}`,"--label",`${yr}=${m.servicesGeneration??0}`,"--label",`${Ya}=${A.get(P.name)??""}`,"--label",`${Xa}=${_}`,"--network",`container:${C(m.ref).pod}`,"--userns",`container:${C(m.ref).pod}`,"--memory",`${P.memoryMb}m`,"--restart","no",...fd(P),...ti(Za(m,P,Ge,w)),P.image];await L(n,t,ne,{env:Za(m,P,Ge,w)})}}return{list:a,preflight:u,create:i,createServices:h,async start(m){await L(n,t,["start",C(m).pod])},async startServices(m){for(let p of await d(m))await L(n,t,["start",p.container])},async applyHostServices(m){let p=[],S=await L(n,t,["ps","--all","--filter",`label=${dt}`,"--format","json"]),w=S.trim()?JSON.parse(S):[],_=new Map;for(let k of w){let j=k.Labels?.[dt];j&&_.set(j,{spec:k.Labels?.[ei]??"",state:k.State??""})}for(let k of m)try{await A(k)}catch(j){p.push({name:k.name,action:"failed",reason:j instanceof Error?j.message:String(j)})}async function $(k){let j=await r();if((await n(t,[...j,"image","exists",k])).code===0)return;let I=await n(t,[...j,"pull","--quiet",k]);if(I.code===0)return;let z=bd(k);if(!z||!e.registryLogin)throw new Error(`${k} could not be pulled: ${(I.stderr||I.stdout).trim()}`);await e.registryLogin(z),await L(n,t,[...j,"pull","--quiet",k])}async function A(k){let j=e.hostServiceLocal?.(k,m),I=j?{...k,...j.env&&Object.keys(j.env).length>0?{env:{...k.env,...j.env}}:{},...j.volumes?{volumes:j.volumes}:{},...j.network?{network:j.network}:{}}:k,z=sn(I.name),Ge=ud(I),P=_.get(I.name);if(_.delete(I.name),P&&P.spec===Ge){P.state!=="running"&&(await L(n,t,["start",z]),p.push({name:I.name,action:"started",reason:P.state}));return}let oe=P?"the spec changed":"it was not here";await $(I.image),P&&await L(n,t,["rm","--force","--ignore",z]),await L(n,t,[...await r(),"run","--detach","--name",z,"--label",`${dt}=${I.name}`,"--label",`${ei}=${Ge}`,...I.healthPath?["--label",`${kr}=${I.hostPort}:${I.healthPath}`]:[],"--memory",`${I.memoryMb}m`,"--restart","no",...I.network?["--network",I.network]:e.network?["--network",e.network]:[],...(I.volumes??[]).flatMap(ne=>["--volume",ne]),...Sr(e.podBind).flatMap(ne=>["--publish",`${ne}:${I.hostPort}:${I.port}`]),...I.adminPort&&I.adminHostPort?["--publish",`127.0.0.1:${I.adminHostPort}:${I.adminPort}`]:[],...ti(I.env),I.image,...I.command??[]],{env:{...I.env}}),p.push({name:I.name,action:P?"recreated":"created",reason:oe})}for(let k of _.keys())await L(n,t,["rm","--force","--ignore",sn(k)]),p.push({name:k,action:"removed",reason:"it is no longer on the list"});return p},async hostServiceHealth(m){let p=await L(n,t,["ps","--all","--filter",`label=${dt}`,"--format","json"]),S=(p.trim()?JSON.parse(p):[]).map(_=>_.Labels?.[dt]??"").filter(_=>_!=="");if(S.length===0)return[];let w=Date.now()+m;for(;;){let _=[];for(let $ of S)_.push(await ri(n,t,$,sn($),o));if(_.every($=>$.ok)||Date.now()>=w)return _;await new Promise($=>setTimeout($,500))}},async serviceHealth(m,p){let S=await d(m);if(S.length===0)return[];let w=Date.now()+p;for(;;){let _=[];for(let $ of S)_.push(await ri(n,t,$.name,$.container,o));if(_.every($=>$.ok)||Date.now()>=w)return _;await new Promise($=>setTimeout($,500))}},async stop(m,p){for(let S of await d(m))await L(n,t,["stop","--time",String(sd),S.container]);await L(n,t,["stop","--time",String(p),C(m).pod])},async remove(m){for(let p of await d(m))await L(n,t,["rm","--force","--ignore",p.container]);await L(n,t,["rm","--force","--ignore","--volumes",C(m).pod])},async removeData(m){await L(n,t,["volume","rm","--force",C(m).pod])},async applyOwnerPassword(m){let p=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${m.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
298
+ `);await L(n,t,["exec","--interactive","--env","SNOUTPOD_OWNER_PASSWORD",C(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:m.ownerPassword},stdin:p})},async applyRolePassword(m,p,S){let w=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${mt(p)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
299
+ `);await L(n,t,["exec","--interactive","--env","SNOUTPOD_ROLE_PASSWORD",C(m).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:S},stdin:w})},async updateExtensions(m){let p=C(m),S=await L(n,t,["exec","--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-tA","-F",gr,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:Ja});return za(S)},async runProjectSql(m,p){let S=C(m);await L(n,t,["exec","--interactive",S.pod,"psql","--username","snoutpod_admin","--dbname",S.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:p})},async ensureHostServiceDatabase(m,p,S){let w=sn(m),_=mt(p),$=mt(S),A=j=>["exec",w,"psql","--username",_,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",j];(await L(n,t,A(`select 1 from pg_database where datname = '${$}'`))).trim()!=="1"&&await L(n,t,A(`create database "${$}"`))},async replicationSlots(m){let p=await L(n,t,["exec",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",Pa]);return Ra(p)},async dropReplicationSlot(m,p){if(!/^[a-z0-9_]{1,63}$/.test(p))throw new Error(`not a replication slot name: ${JSON.stringify(p)}`);await L(n,t,["exec",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-v","ON_ERROR_STOP=1","-c",`select pg_drop_replication_slot('${p}')`])},async renameProject(m,p){p!==m.ref&&await L(n,t,["exec","--interactive",C(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:Rd(p,m.ref)})},async waitReady(m,p){let S=C(m).pod,w=Date.now()+p;for(;;){let _=await n(t,["exec",S,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(_.code===0&&_.stdout.trim()==="t")return s(m),!0;if(Date.now()>=w)return!1;await new Promise($=>setTimeout($,500))}},async capacity(){let m=await a(),p=await e.capacity();return{pods:m.length,...p}},async databaseBytes(m){let p=await n(t,["exec",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select pg_database_size(current_database())"]);return p.code===0?lr(p.stdout):null},async storageBytes(m){let p=await n(t,["exec",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select coalesce(sum((metadata->>'size')::bigint), 0) from storage.objects"]);return p.code===0?lr(p.stdout):null},async setReadOnly(m,p){let S=C(m);await L(n,t,["exec",S.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${S.database} set default_transaction_read_only = ${p?"on":"off"}`])},async archiveState(m){let p=await n(t,["exec",C(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), case when last_failed_time > coalesce(last_archived_time, '-infinity') then failed_count else 0 end from pg_stat_archiver`]);if(p.code!==0)return null;let[S="",w=""]=p.stdout.trim().split(`
300
+ `)[0]?.split("|")??[],_=Number(w);return{lastArchivedAt:S.trim()||null,failures:Number.isInteger(_)&&_>=0?_:0}}}}async function ri(e,t,n,r,o=si){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,u;try{let f=JSON.parse(s.stdout);a=f[0]?.State,u=f[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let i=a?.Status??"unknown";if(i!=="running")return{name:n,ok:!1,detail:i};let d=pd(u?.[kr]);if(!d)return{name:n,ok:!0,detail:"running"};let l=await o(d.port,d.path);return l===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${l}`}}function Pd(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function Rd(e,t){return[`\\set from_db ${e}`,`\\set to_db ${t}`,`\\set from_role ${e}_owner`,`\\set to_role ${t}_owner`,'alter database :"from_db" rename to :"to_db";','alter role :"from_role" rename to :"to_role";',"alter system reset restore_command;","alter system reset recovery_target_time;","alter system reset recovery_target_action;",""].join(`
301
+ `)}function Td(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var He,hr,ut,yr,Ya,Xa,sd,ad,id,dt,ei,md,oi,kr,si,gd,wd,ni,ii=M(()=>{"use strict";ke();cr();fr();rn();Ha();ur();Wa();Va();He="snoutpod.ref",hr="snoutpod.generation",ut="snoutpod.service",yr="snoutpod.services-generation",Ya="snoutpod.service-spec",Xa="snoutpod.service-pod";sd=10,ad=/^[a-z][a-z0-9-]{0,30}$/;id=/^[a-z][a-z0-9_]{0,62}$/;dt="snoutpod.host-service",ei="snoutpod.host-service-spec";md=/^[A-Za-z_][A-Za-z0-9_]*$/;oi=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,kr="snoutpod.health";si=async(e,t)=>{let{request:n}=await import("node:http");return new Promise(r=>{let o=n({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:2e3},s=>{s.resume(),r(s.statusCode??null)});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null)}),o.end()})},gd="crun";wd=3e4;ni=200});var bi={};Ir(bi,{DEFAULT_LOCAL_MEMORY_MB:()=>di,DEFAULT_LOCAL_PORT:()=>Md,ENGINE:()=>H,ENGINE_NAME:()=>$r,IMAGE:()=>Rr,NETWORK:()=>Ee,NEW_PROJECT_MAJOR:()=>Pr,NO_BLACKHOLE_ROUTES:()=>pi,PUBLIC_IMAGE:()=>Ld,PUBLISHED_MAJORS:()=>qe,capacity:()=>fi,chooseLocalImage:()=>pt,ensureImage:()=>Ud,ensureNetwork:()=>Gd,exec:()=>V,hasData:()=>un,localImageFor:()=>mi,localPsql:()=>Jd,localRuntime:()=>qd,localSpec:()=>yi,localStatus:()=>Tr,localUri:()=>hi,networkPolicyInPlace:()=>gi,newLocalPassword:()=>jd,newLocalRef:()=>Id,podLog:()=>wi,podName:()=>gt,podmanReady:()=>Bd,removeLocal:()=>Yd,startLocal:()=>Wd,stopLocal:()=>Vd,volumePgVersion:()=>an});import{randomBytes as li}from"node:crypto";import{existsSync as ci}from"node:fs";import{platform as ln}from"node:os";import{delimiter as Ad,join as Od}from"node:path";function Nd(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=ln()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(Ad))if(t&&e.some(n=>ci(Od(t,n))))return"podman";return ci("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function pt(e,t=process.env.SNOUTPOD_IMAGE){if(t)return{name:t,published:null,major:null};if(e===null)return ft(Pr);let n=Number(vr(e));if(!qe.includes(n)){let r=e.trim()||"(empty)",o=qe.map(s=>ft(s).published).join(" or ");throw new Error(`its data directory's PG_VERSION says ${r}, and there is an image only for Postgres ${qe.join(" and ")}. Set SNOUTPOD_IMAGE to an image of that major (${o} for those).`)}return ft(n)}function ft(e){return{name:`${Dd}:${e}`,published:`${ui}:${e}`,major:e}}function Id(){return ia(e=>li(e))}function jd(){return li(18).toString("base64url")}async function Ud(e,t=V,n=pt(null)){let r=n.published;if(!r||(await t(H,["image","exists",n.name])).code===0)return null;e?.(`Fetching the database image (${r}). This happens once.`);let o=await t(H,["pull",r],{timeoutMs:Fd}).catch(a=>({code:1,stdout:"",stderr:a instanceof Error?a.message:String(a)}));if(o.code!==0)return`could not fetch the database image ${r}: ${o.stderr.trim()||o.stdout.trim()||"the pull failed"}`;let s=await t(H,["tag",r,n.name]);return s.code!==0?`fetched ${r} but could not tag it as ${n.name}: ${s.stderr.trim()}`:(e?.("Got it."),null)}async function Bd(e=Cd,t=Rr){if($r!=="podman")return`SNOUTPOD_ENGINE=${$r}: a local project is Podman-only, because a database that works under Docker and not under Podman is a database that works here and not in the cloud`;let n;try{n=await V(H,["--version"])}catch{return`${H} is not installed. macOS: brew install podman, then podman machine init && podman machine set --rootful=false && podman machine start. Windows: winget install RedHat.Podman, then the same three machine commands. Linux: your distribution's podman package.`}if(n.code!==0)return`${H} --version failed: ${n.stderr.trim()||n.stdout.trim()}`;if((await V(H,["image","exists",t])).code!==0)return`the pod image ${t} is not on this machine. ${e}`;let o=await V(H,["machine","inspect","--format","{{.Rootful}}"]);return o.code===0&&o.stdout.trim().startsWith("true")?"the podman machine is ROOTFUL, and the hosts are not: podman machine stop && podman machine set --rootful=false && podman machine start":null}function Hd(e){let t=`if out=$(ls -A ${JSON.stringify(e)} 2>/dev/null); then if [ -n "$out" ]; then echo DATA; else echo EMPTY; fi; else echo HIDDEN; fi`;return V(H,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
302
+ `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function un(e){let t=ln()==="darwin"||ln()==="win32";return Ma(V,e,{binary:H,look:t?Hd:void 0})}async function an(e,t=V,n=ln()){let r=C(e).pod;if((await t(H,["volume","exists",r])).code!==0)return null;let o;if(n==="darwin"||n==="win32"){let s=null;for(let a of qe){let u=ft(a).name;if((await t(H,["image","exists",u])).code===0){s=u;break}}if(!s)throw new Error(`${r} holds a database and there is no pod image on this machine to read its Postgres version with. Set SNOUTPOD_IMAGE to the image it was made with (${qe.map(a=>ft(a).published).join(" or ")}).`);o=await t(H,["run","--rm","--pull=never","--network=none","--userns=host","--user=0","--entrypoint=cat","--volume",`${r}:/data:ro`,s,"/data/PG_VERSION"])}else{let s=await t(H,["volume","inspect","--format","{{.Mountpoint}}",r]),a=s.code===0?s.stdout.trim().replace(/\/+$/,""):"";if(!a)throw new Error(`could not find where ${r} is: ${s.stderr.trim()||"no mountpoint"}`);o=await t(H,["unshare","cat",`${a}/PG_VERSION`])}if(o.code===0)return o.stdout;if(/no such file/i.test(o.stderr))return null;throw new Error(`could not read the Postgres version in ${r}: ${o.stderr.trim()||o.stdout.trim()||`exit ${o.code}`}`)}function mi(e,t=an){if(!e||process.env.SNOUTPOD_IMAGE)return Promise.resolve(pt(null));Q(e);let n=t===an?cn.get(e):void 0;if(n)return n;let r=t(e).then(o=>{try{return pt(o)}catch(s){throw new Error(`${gt(e)}: ${s instanceof Error?s.message:String(s)}`)}});return t===an&&(cn.set(e,r),r.catch(()=>cn.delete(e))),r}async function fi(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function qd(e){return ai({exec:V,binary:H,network:Ee,hasData:un,capacity:fi,repository:e})}function gt(e){return C(e).pod}async function Gd(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Kd()||await V(H,tn({name:Ee}));return}let t=tn({name:Ee,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await gi())return;await e?.(),await V(H,["network","rm","--force",Ee]).catch(()=>{});let o=await V(H,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${pi}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${Ee} with egress blocked${n?`: ${n}`:""}`)}async function Kd(){return(await V(H,["network","inspect",Ee,"--format","json"])).code===0}async function gi(){let e=await V(H,["network","inspect",Ee,"--format","json"]);if(e.code!==0)return!1;try{return Ea(JSON.parse(e.stdout)?.[0])}catch{return!1}}function hi(e){let t=C(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function yi(e,t={}){return Q(e.ref),{ref:e.ref,generation:1,image:t.image??Rr,memoryMb:t.memoryMb??di,cpus:null,bucket:"",region:"us-east-1",ownerPassword:e.ownerPassword,adminPassword:e.adminPassword,port:e.port,tuning:t.tuning??null,backup:null,services:t.services??null}}function Er(e,t){e.onProgress?.(t)}async function Wd(e,t,n={}){let r=n.image??(await mi(t.ref,n.pgVersion)).name,o=yi(t,{...n,image:r}),s=n.timeoutMs??12e4,a=n.hasData??un,u=(await e.list()).find(i=>i.ref===t.ref);if(u?u.status==="stopped"?(Er(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):Er(n,"The database is already running."):(Er(n,await a(t.ref)?"Starting the database over its existing data.":"Creating the database."),await e.create(o)),!await e.waitReady(t.ref,s))throw new Error(await zd(t.ref,s,n.podLog??wi));return await e.applyOwnerPassword(o),await e.createServices(o),Tr(e,t,n)}async function Jd(e,t){let n=C(e.ref);return V(H,["exec","--interactive","-e","PGPASSWORD",gt(e.ref),"psql","--host","127.0.0.1","--port","5432","--username",n.ownerRole,"--dbname",n.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA","--file","-"],{stdin:t,env:{PGPASSWORD:e.ownerPassword}})}async function wi(e){let t=await V(H,["logs","--tail","20",gt(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function zd(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${gt(e)} never became ready in ${t}ms
303
303
  --- pod log ---
304
- ${r}`}async function Yu(e,t,n={}){Q(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,na),ir(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Xu(e,t,n){Q(t),await e.remove(t),n&&await e.removeData(t)}async function ir(e,t,n={}){Q(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??Jt;return{ref:t.ref,exists:!!r,status:r?.status??"absent",ready:o?await e.waitReady(t.ref,1):!1,hasData:r?.hasData??await s(t.ref),port:t.port,databaseBytes:o?await e.databaseBytes(t.ref):null,services:r?.services??[],uri:t.port>0&&t.ownerPassword?Ua(t):null}}var ar,G,$e,xe,Cu,Ia,J,Uu,Be,Bu,ja,qa=M(()=>{"use strict";ra();Gt();aa();Wn();Na();be();ar=process.env.SNOUTPOD_ENGINE??"podman";G=Iu(),$e=process.env.SNOUTPOD_IMAGE??"snoutpod/postgres:17",xe=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",Cu=54322,Ia=512,J=oa();Uu="cd packages/snoutpod && npm run image:build",Be="ghcr.io/snoutdata/snoutpod-postgres:17";Bu=20*60*1e3;ja="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});ne();var Yt=[{name:"init",summary:"Create or link a project and write a DATABASE_URL",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to",flags:[]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database",flags:["--name","--region","--team","--no-wait","--timeout"]},{name:"projects pause",summary:"Stop a project, and wait until it is paused",flags:["--ref","--no-wait","--timeout"]},{name:"projects resume",summary:"Start a paused project, and wait until it is ready",flags:["--ref","--no-wait","--timeout"]},{name:"projects delete",summary:"Delete a project, and wait until it is gone",flags:["--ref","--no-wait","--timeout"]},{name:"projects show",summary:"One project: state, products, functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage, the data API and push: on or off",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions size",summary:"Its memory and workers, within the plan",flags:["--ref","--memory","--concurrency","--reset"]},{name:"functions delete",summary:"Remove one",flags:["--ref"]},{name:"secrets set",summary:"Set the environment functions run with",flags:["--ref","--stdin"]},{name:"secrets list",summary:"The names, never the values",flags:["--ref"]},{name:"secrets unset",summary:"Remove one",flags:["--ref"]},{name:"keys",summary:"The anon and service_role API keys for a project",flags:["--ref"]},{name:"keys rotate",summary:"New keys, breaking every one already issued",flags:["--ref","--force"]},{name:"teams",summary:"Teams this account belongs to",flags:[]},{name:"link",summary:"Point this folder at a project",flags:["--ref"]},{name:"usage",summary:"Size and compute against the plan limit",flags:["--ref","--days","--history"]},{name:"start",summary:"A Postgres on this machine, with your migrations applied",flags:["--port","--dir","--no-migrations","--out-of-order"]},{name:"stop",summary:"Stop the local database. The data stays",flags:[]},{name:"status",summary:"What the local database is doing",flags:[]},{name:"gen types typescript",summary:"The schema as a TypeScript Database type, on stdout",flags:["--ref","--db-url","--local","--schema","--out"]},{name:"db url",summary:"A connection string",flags:["--ref"]},{name:"db psql",summary:"Open psql, or run statements after --",flags:["--ref"]},{name:"db reset-password",summary:"Rotate the project password",flags:["--ref"]},{name:"db export",summary:"Take or download a dump",flags:["--ref","--out","--status","--timeout"]},{name:"db push",summary:"Apply migrations from a folder",flags:["--dir","--dry-run","--out-of-order"]},{name:"db restore",summary:"Put a dump into a project, or rewind it to a moment into a new project",flags:["--file","--force","--at","--name","--window","--ref"]},{name:"mcp",summary:"Serve these operations as tools over stdio",flags:["--allow-delete"]}];H();Y();q();import{spawn as tc}from"node:child_process";function nc(e){return`snout-${e}`}function Ie(e,t,n){return new Promise((r,o)=>{let s=tc("docker",["compose","-p",nc(e.ref),"--project-directory",e.folder,...t],{cwd:e.folder,stdio:["ignore","pipe","pipe"]}),a="",u="";s.stdout.on("data",i=>{a+=String(i)}),s.stderr.on("data",i=>{if(u+=String(i),n)for(let m of String(i).split(/\r?\n/).filter(Boolean))c(b(` ${m.trim()}`))}),s.on("error",i=>{if(i.code==="ENOENT"){o(new L("tool-missing","docker is not installed, or not on PATH. A local project runs in Docker (Docker Desktop on Windows and macOS)."));return}o(new L("failed",String(i)))}),s.on("close",i=>r({code:i??0,out:a,err:u}))})}async function rc(e){let t=await Ie(e,["ps","-a","--format","json"],!1);if(t.code!==0)throw new L("failed",t.err.trim()||"docker compose ps failed. Is Docker running?");let n=t.out.trim();return(n.startsWith("[")?JSON.parse(n):n.split(/\r?\n/).filter(Boolean).map(o=>JSON.parse(o))).map(o=>({service:o.Service??"?",state:o.State==="exited"&&o.ExitCode===0?"done":o.State??"?",health:o.Health??""})).sort((o,s)=>o.service.localeCompare(s.service))}async function Xr(e){let t=await rc(e),n=t.filter(o=>o.state==="running").length,r=t.filter(o=>o.state!=="done").length;return g({ref:e.ref,local:e.folder,api:e.apiUrl,dbPort:e.dbPort,running:n,services:t},()=>{if(c(`${P(e.name??e.ref)} (local, ${e.folder})`),t.length===0){c("Not started. `snoutdata start` brings it up.");return}process.stdout.write(`${U([["SERVICE","STATE","HEALTH"],...t.map(o=>[o.service,o.state,o.health])])}
305
- `),c(`${n} of ${r} services running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}.`)}),0}async function Zr(e){c(`Starting ${e.name??e.ref} (local)\u2026`);let t=await Ie(e,["up","-d","--wait","--wait-timeout","600"],!0);if(t.code!==0)throw new L("failed",`docker compose up failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,api:e.apiUrl,dbPort:e.dbPort,started:!0},()=>{c(`Running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}. \`snoutdata db url\` prints the connection string.`)}),0}async function Qr(e){let t=await Ie(e,["stop"],!0);if(t.code!==0)throw new L("failed",`docker compose stop failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,stopped:!0},()=>c(`Stopped ${e.name??e.ref}. Its data is kept; \`snoutdata start\` brings it back.`)),0}function eo(e){g({ref:e.ref,name:e.name,local:e.folder,api:e.apiUrl,dbPort:e.dbPort},()=>{process.stdout.write(`${U([["name",e.name??"(not set up by Studio)"],["ref",e.ref],["where",`local, ${e.folder}`],["api",e.apiUrl],["database",`127.0.0.1:${e.dbPort}`]])}
306
- `)})}Je();import{cpSync as ro,existsSync as Qe,readdirSync as dc,readFileSync as oo,rmSync as so,statSync as mc,writeFileSync as ao}from"node:fs";import{join as Z,resolve as Pt}from"node:path";var oc=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function sc(e){return oc.test(e)}function ve(e){if(!sc(e))throw new Error(`not a function name: ${JSON.stringify(e)}. Letters, digits, hyphens and underscores, up to 48 characters, starting with a letter or a digit.`);return e}var ac=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function ic(e){if(e.length===0||e.length>255||e.includes("\\")||e.startsWith("/"))return!1;let t=e.split("/");return t.length>8?!1:t.every(n=>n!=="."&&n!==".."&&ac.test(n))}var un=2*1024*1024,to=200;function no(e){let{entrypoint:t}=e;if(e.files.length===0)throw new Error("there is nothing in this function to deploy");if(e.files.length>to)throw new Error(`a function may hold ${to} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!ic(a.path))throw new Error(`${JSON.stringify(a.path)} is not a path a function may contain`);if(n.has(a.path))throw new Error(`${a.path} appears twice`);n.add(a.path)}if(!n.has(t))throw new Error(`the entrypoint ${JSON.stringify(t)} is not one of the files`);let r=[...e.files].sort((a,u)=>a.path<u.path?-1:a.path>u.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=cc(o);if(s.length>un)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${un/1024} KB. A function is source code; put large files in Storage.`);return o}function cc(e){let t=e.files.map(r=>`{"path":${JSON.stringify(r.path)},"text":${JSON.stringify(r.text)}}`).join(","),n=`{"snoutfn":${e.snoutfn},"entrypoint":${JSON.stringify(e.entrypoint)},"files":[${t}]}`;return new TextEncoder().encode(n)}var lc=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/;var uc=["SNOUTDATA_","SNOUT_"];function Ze(e){if(!lc.test(e))return`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`;let t=uc.find(n=>e.startsWith(n));return t?`names beginning with ${t} are set by the platform, so ${e} cannot be one of yours.`:null}var wm=64*1024;Y();ne();q();function io(e){let t=Z(e.folder,"functions");return Qe(t)?dc(t).filter(n=>!n.startsWith(".")&&!n.startsWith("_")&&mc(Z(t,n)).isDirectory()).sort():[]}async function $t(e){c(b(" docker compose run --rm functions-deploy"));let t=await Ie(e,["run","--rm","functions-deploy"],!0);if(t.code!==0)throw new L("failed",`functions-deploy failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`)}function co(e,t,n){let r=Z(e.folder,".env"),o=oo(r,"utf8"),s=(pt(o).get("FUNCTIONS_NO_VERIFY_JWT")??"").split(",").map(m=>m.trim()).filter(Boolean),a=n?[...new Set([...s,t])]:s.filter(m=>m!==t);if(a.join(",")===s.join(","))return!1;let u=`FUNCTIONS_NO_VERIFY_JWT=${a.join(",")}`,i=/^FUNCTIONS_NO_VERIFY_JWT=.*$/m;return ao(r,i.test(o)?o.replace(i,u):`${o.replace(/\n?$/,`
304
+ ${r}`}async function Vd(e,t,n={}){Q(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,Oa),Tr(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Yd(e,t,n){Q(t),await e.remove(t),n&&(await e.removeData(t),cn.delete(t))}async function Tr(e,t,n={}){Q(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??un;return{ref:t.ref,exists:!!r,status:r?.status??"absent",ready:o?await e.waitReady(t.ref,1):!1,hasData:r?.hasData??await s(t.ref),port:t.port,databaseBytes:o?await e.databaseBytes(t.ref):null,services:r?.services??[],uri:t.port>0&&t.ownerPassword?hi(t):null}}var $r,H,Pr,qe,Dd,ui,Rr,Ee,Md,di,V,Cd,Ld,Fd,cn,pi,Si=M(()=>{"use strict";Na();rn();Ia();mr();ii();ke();$r=process.env.SNOUTPOD_ENGINE??"podman";H=Nd(),Pr=18,qe=[17,18],Dd="snoutpod/postgres",ui="ghcr.io/snoutdata/snoutpod-postgres";Rr=pt(null).name,Ee=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",Md=54322,di=512,V=Da();Cd="cd packages/snoutpod && npm run image:build",Ld=`${ui}:${Pr}`;Fd=20*60*1e3;cn=new Map;pi="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});re();var pn=[{name:"init",summary:"Create or link a project for this folder, and print its DATABASE_URL or write it to .env",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--email","--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to, and whether it is the account login --email asked for",flags:[]},{name:"upgrade",summary:"Install the newest CLI the way this one was installed (binary or npm), checksum verified",flags:["--check"]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database, and print its ref",flags:["--name","--region","--team","--no-wait","--timeout","--show-url"]},{name:"projects pause",summary:"Stop a project, and wait until it is paused",flags:["--ref","--no-wait","--timeout"]},{name:"projects resume",summary:"Start a paused project, and wait until it is ready",flags:["--ref","--no-wait","--timeout"]},{name:"projects delete",summary:"Delete a project, and wait until it is gone",flags:["--ref","--no-wait","--timeout"]},{name:"projects show",summary:"One project: state, products (Realtime included), functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage, the data API and push: on or off. Realtime is always on, and listed",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth anonymous",summary:"Guest sign-in on or off: signInAnonymously() gives a browser a session with no email or password",flags:["--ref"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"realtime inspect",summary:"The channels open now: each client, its presence, when it was last heard, and the last minute of messages",flags:["--channel","--watch","--ref"]},{name:"realtime logs",summary:"The connection log: connects, joins, leaves and disconnects, with the reason each ended",flags:["--since","--channel","--ref"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions size",summary:"Its memory and workers, within the plan",flags:["--ref","--memory","--concurrency","--reset"]},{name:"functions delete",summary:"Remove one",flags:["--ref"]},{name:"secrets set",summary:"Set the environment functions run with",flags:["--ref","--stdin"]},{name:"secrets list",summary:"The names, never the values",flags:["--ref"]},{name:"secrets unset",summary:"Remove one",flags:["--ref"]},{name:"keys",summary:"The anon and service_role API keys for a project",flags:["--ref"]},{name:"keys rotate",summary:"New keys, breaking every one already issued",flags:["--ref","--force"]},{name:"teams",summary:"Teams this account belongs to",flags:[]},{name:"link",summary:"Point this folder at a project",flags:["--ref"]},{name:"usage",summary:"Size and compute against the plan limit",flags:["--ref","--days","--history"]},{name:"start",summary:"A Postgres on this machine, with your migrations applied",flags:["--port","--dir","--no-migrations","--out-of-order"]},{name:"stop",summary:"Stop the local database. The data stays",flags:[]},{name:"status",summary:"What the local database is doing",flags:[]},{name:"gen types typescript",summary:"The schema as a TypeScript Database type, on stdout",flags:["--ref","--db-url","--local","--schema","--out"]},{name:"db url",summary:"A connection string",flags:["--ref"]},{name:"db psql",summary:"Open psql, or run statements after --",flags:["--ref"]},{name:"db reset-password",summary:"Rotate the project password",flags:["--ref"]},{name:"db export",summary:"Take or download a dump",flags:["--ref","--out","--status","--timeout"]},{name:"db push",summary:"Apply migrations from a folder",flags:["--dir","--dry-run","--out-of-order"]},{name:"db restore",summary:"Put a dump into a project, or rewind it to a moment into a new project",flags:["--file","--force","--at","--name","--window","--ref"]},{name:"mcp",summary:"Serve these operations as tools over stdio",flags:["--allow-delete"]}],Cr={"--ref":"The project. Defaults to SNOUTDATA_PROJECT, then .snoutdata/project.json here or in a parent","--name":"The project's name","tokens create --name":"What the token is for and where it lives, shown in tokens list","db restore --name":"The name of the new project the restore makes","--region":"Where the project runs. Defaults to the nearest","--env":"Write DATABASE_URL into .env in this folder, and add .env to .gitignore since it holds the password. Without it the URL is printed","--email":"The account to sign in as. The browser offers it, and a different account is reported at once and by whoami","--provider":"google (the default) or github","--sso":"Sign in with your company's identity provider","--domain":"With --sso: your work email or company domain. Only the domain is sent","--no-browser":"Print the sign-in URL instead of opening a browser","--device":"Sign in by typing a code into a browser on any machine: SSH, containers, agents","--timeout":"Seconds to wait before giving up","--check":"Say whether a newer version exists, and install nothing","--expires":"Days until the token stops working. Without it, it works until revoked","--project":"Limit the token to one project ref","--team":"Create it in a team (name or id), shared with its members","--no-wait":"Return as soon as it is asked for, without waiting for it to be true","--show-url":"Also print the connection string, which holds the password. Otherwise db url prints it when wanted","--p8":"The APNs .p8 key file (a path, never the key itself)","--key-id":"The APNs key id","--team-id":"Your Apple developer team id","--topic":"The app's bundle id","--environment":"production or sandbox","--file":"A file to read","push credentials set --file":"The FCM service-account JSON","auth template --file":"The email's HTML body","db restore --file":"The dump to restore (from db export or pg_dump -Fc)","--client-id":"Your Google OAuth client id; the secret is read from stdin with --stdin","--stdin":"Read the secret value from a pipe, so it is not in shell history","--site-url":"Where sign-in emails send people by default","--allow":"Comma-separated addresses a sign-in may return to","--subject":"The email's subject line","--dir":"The folder to read from","db push --dir":"The migrations folder (default ./migrations)","start --dir":"The migrations folder (default ./migrations)","functions deploy --dir":"The function's folder (default ./functions/<name>)","--entrypoint":"The file the runtime starts, when it is not index.ts","--no-verify-jwt":"Let anybody with the URL call it (webhooks). By default a caller needs a project key or a user session","--memory":"Memory per worker, in MB, within the plan","--concurrency":"How many workers it may run at once, within the plan","--reset":"Back to the plan's default size","--force":"Do it although it cannot be undone","--days":"How many days of history","--history":"Print the day-by-day table too","--port":"The port the local database listens on (default 54322)","--no-migrations":"Start the database and apply nothing","--out-of-order":"Apply a new migration that sorts before one already applied","--db-url":"Read a postgres:// database instead of a project","--local":"Read the database snoutdata start is running","--schema":"Comma-separated schemas (default public)","--out":"Write to this file instead of stdout","db export --out":"Where to save the dump","--status":"Show the last export without taking another","--dry-run":"Show what would run, and change nothing","--at":"The moment to restore to (an ISO time), into a NEW project","--window":"How far back a point-in-time restore can go","--allow-delete":"Let the agent's delete_project actually delete","--channel":"Only this channel","--since":"How far back, like 10m, 2h or 1d (default 1h)","--watch":"Keep printing what changes until interrupted"};function gn(e,t){return Cr[`${e} ${t}`]??Cr[t]??""}K();X();q();import{spawn as Uc}from"node:child_process";function Fc(e){return`snout-${e}`}function Ce(e,t,n){return new Promise((r,o)=>{let s=Uc("docker",["compose","-p",Fc(e.ref),"--project-directory",e.folder,...t],{cwd:e.folder,stdio:["ignore","pipe","pipe"]}),a="",u="";s.stdout.on("data",i=>{a+=String(i)}),s.stderr.on("data",i=>{if(u+=String(i),n)for(let d of String(i).split(/\r?\n/).filter(Boolean))c(b(` ${d.trim()}`))}),s.on("error",i=>{if(i.code==="ENOENT"){o(new N("tool-missing","docker is not installed, or not on PATH. A local project runs in Docker (Docker Desktop on Windows and macOS)."));return}o(new N("failed",String(i)))}),s.on("close",i=>r({code:i??0,out:a,err:u}))})}async function Bc(e){let t=await Ce(e,["ps","-a","--format","json"],!1);if(t.code!==0)throw new N("failed",t.err.trim()||"docker compose ps failed. Is Docker running?");let n=t.out.trim();return(n.startsWith("[")?JSON.parse(n):n.split(/\r?\n/).filter(Boolean).map(o=>JSON.parse(o))).map(o=>({service:o.Service??"?",state:o.State==="exited"&&o.ExitCode===0?"done":o.State??"?",health:o.Health??""})).sort((o,s)=>o.service.localeCompare(s.service))}async function ko(e){let t=await Bc(e),n=t.filter(o=>o.state==="running").length,r=t.filter(o=>o.state!=="done").length;return g({ref:e.ref,local:e.folder,api:e.apiUrl,dbPort:e.dbPort,running:n,services:t},()=>{if(c(`${v(e.name??e.ref)} (local, ${e.folder})`),t.length===0){c("Not started. `snoutdata start` brings it up.");return}process.stdout.write(`${U([["SERVICE","STATE","HEALTH"],...t.map(o=>[o.service,o.state,o.health])])}
305
+ `),c(`${n} of ${r} services running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}.`)}),0}async function vo(e){c(`Starting ${e.name??e.ref} (local)\u2026`);let t=await Ce(e,["up","-d","--wait","--wait-timeout","600"],!0);if(t.code!==0)throw new N("failed",`docker compose up failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,api:e.apiUrl,dbPort:e.dbPort,started:!0},()=>{c(`Running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}. \`snoutdata db url\` prints the connection string.`)}),0}async function Eo(e){let t=await Ce(e,["stop"],!0);if(t.code!==0)throw new N("failed",`docker compose stop failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,stopped:!0},()=>c(`Stopped ${e.name??e.ref}. Its data is kept; \`snoutdata start\` brings it back.`)),0}function $o(e){g({ref:e.ref,name:e.name,local:e.folder,api:e.apiUrl,dbPort:e.dbPort},()=>{process.stdout.write(`${U([["name",e.name??"(not set up by Studio)"],["ref",e.ref],["where",`local, ${e.folder}`],["api",e.apiUrl],["database",`127.0.0.1:${e.dbPort}`]])}
306
+ `)})}Ve();import{cpSync as To,existsSync as ot,readdirSync as Vc,readFileSync as Ao,rmSync as Oo,statSync as Yc,writeFileSync as No}from"node:fs";import{join as te,resolve as Lt}from"node:path";var Hc=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function qc(e){return Hc.test(e)}function Re(e){if(!qc(e))throw new Error(`not a function name: ${JSON.stringify(e)}. Letters, digits, hyphens and underscores, up to 48 characters, starting with a letter or a digit.`);return e}var Gc=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function Kc(e){if(e.length===0||e.length>255||e.includes("\\")||e.startsWith("/"))return!1;let t=e.split("/");return t.length>8?!1:t.every(n=>n!=="."&&n!==".."&&Gc.test(n))}var An=2*1024*1024,Po=200;function Ro(e){let{entrypoint:t}=e;if(e.files.length===0)throw new Error("there is nothing in this function to deploy");if(e.files.length>Po)throw new Error(`a function may hold ${Po} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!Kc(a.path))throw new Error(`${JSON.stringify(a.path)} is not a path a function may contain`);if(n.has(a.path))throw new Error(`${a.path} appears twice`);n.add(a.path)}if(!n.has(t))throw new Error(`the entrypoint ${JSON.stringify(t)} is not one of the files`);let r=[...e.files].sort((a,u)=>a.path<u.path?-1:a.path>u.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=Wc(o);if(s.length>An)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${An/1024} KB. A function is source code; put large files in Storage.`);return o}function Wc(e){let t=e.files.map(r=>`{"path":${JSON.stringify(r.path)},"text":${JSON.stringify(r.text)}}`).join(","),n=`{"snoutfn":${e.snoutfn},"entrypoint":${JSON.stringify(e.entrypoint)},"files":[${t}]}`;return new TextEncoder().encode(n)}var Jc=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/;var zc=["SNOUTDATA_","SNOUT_"];function rt(e){if(!Jc.test(e))return`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`;let t=zc.find(n=>e.startsWith(n));return t?`names beginning with ${t} are set by the platform, so ${e} cannot be one of yours.`:null}var Mf=64*1024;X();re();q();function Do(e){let t=te(e.folder,"functions");return ot(t)?Vc(t).filter(n=>!n.startsWith(".")&&!n.startsWith("_")&&Yc(te(t,n)).isDirectory()).sort():[]}async function Ut(e){c(b(" docker compose run --rm functions-deploy"));let t=await Ce(e,["run","--rm","functions-deploy"],!0);if(t.code!==0)throw new N("failed",`functions-deploy failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`)}function Mo(e,t,n){let r=te(e.folder,".env"),o=Ao(r,"utf8"),s=(_t(o).get("FUNCTIONS_NO_VERIFY_JWT")??"").split(",").map(d=>d.trim()).filter(Boolean),a=n?[...new Set([...s,t])]:s.filter(d=>d!==t);if(a.join(",")===s.join(","))return!1;let u=`FUNCTIONS_NO_VERIFY_JWT=${a.join(",")}`,i=/^FUNCTIONS_NO_VERIFY_JWT=.*$/m;return No(r,i.test(o)?o.replace(i,u):`${o.replace(/\n?$/,`
307
307
  `)}${u}
308
- `),!0}async function dn(e,t,n){ve(t);let r=Pt(n.dir??Z(process.cwd(),"functions",t));if(!Qe(Z(r,"index.ts")))throw new y(`${r} has no index.ts. A local function is a folder with an index.ts in it.`);let o=Z(e.folder,"functions",t);if(Pt(o)!==r){so(o,{recursive:!0,force:!0}),ro(r,o,{recursive:!0});let a=Z(r,"..","_shared");Qe(a)&&Pt(a)!==Pt(Z(e.folder,"functions","_shared"))&&ro(a,Z(e.folder,"functions","_shared"),{recursive:!0})}let s=co(e,t,!n.verifyJwt);if(await $t(e),s){let a=await Ie(e,["up","-d","--wait","--wait-timeout","600"],!1);if(a.code!==0)throw new L("failed",`docker compose up failed after changing who may call ${t}: ${a.err.trim().split(/\r?\n/).slice(-2).join(" ")}`)}return{ref:e.ref,name:t,url:`${e.apiUrl}/functions/v1/${t}`,verifyJwt:n.verifyJwt,local:e.folder}}async function lo(e,t,n){let r=await dn(e,t,n);g(r,()=>{c(`${P(t)} deployed locally: ${r.url}`),n.verifyJwt||c(b(" Callable with no key (FUNCTIONS_NO_VERIFY_JWT), so it must check what calls it itself."))})}function Tt(e){return{ref:e.ref,local:e.folder,functions:io(e).map(t=>({name:t,url:`${e.apiUrl}/functions/v1/${t}`}))}}function uo(e){let t=io(e);g(Tt(e),()=>{if(t.length===0){c(`No functions in ${Z(e.folder,"functions")}.`);return}process.stdout.write(`${U([["NAME","URL"],...t.map(n=>[n,`${e.apiUrl}/functions/v1/${n}`])])}
309
- `)})}async function mn(e,t){ve(t);let n=Z(e.folder,"functions",t);if(!Qe(n))throw new L("not-found",`this local project has no function called ${t}`);return so(n,{recursive:!0,force:!0}),co(e,t,!1),await $t(e),{ref:e.ref,name:t,removed:!0}}async function mo(e,t){g(await mn(e,t),()=>c(`${t} is gone.`))}function At(e){return Z(e.folder,"functions",".env")}function fn(e){let t=At(e);return Qe(t)?pt(oo(t,"utf8")):new Map}function fo(e,t){let n=r=>r.includes("'")?JSON.stringify(r):`'${r}'`;ao(At(e),[...t].map(([r,o])=>`${r}=${n(o)}`).join(`
308
+ `),!0}async function On(e,t,n){Re(t);let r=Lt(n.dir??te(process.cwd(),"functions",t));if(!ot(te(r,"index.ts")))throw new y(`${r} has no index.ts. A local function is a folder with an index.ts in it.`);let o=te(e.folder,"functions",t);if(Lt(o)!==r){Oo(o,{recursive:!0,force:!0}),To(r,o,{recursive:!0});let a=te(r,"..","_shared");ot(a)&&Lt(a)!==Lt(te(e.folder,"functions","_shared"))&&To(a,te(e.folder,"functions","_shared"),{recursive:!0})}let s=Mo(e,t,!n.verifyJwt);if(await Ut(e),s){let a=await Ce(e,["up","-d","--wait","--wait-timeout","600"],!1);if(a.code!==0)throw new N("failed",`docker compose up failed after changing who may call ${t}: ${a.err.trim().split(/\r?\n/).slice(-2).join(" ")}`)}return{ref:e.ref,name:t,url:`${e.apiUrl}/functions/v1/${t}`,verifyJwt:n.verifyJwt,local:e.folder}}async function Io(e,t,n){let r=await On(e,t,n);g(r,()=>{c(`${v(t)} deployed locally: ${r.url}`),n.verifyJwt||c(b(" Callable with no key (FUNCTIONS_NO_VERIFY_JWT), so it must check what calls it itself."))})}function Ft(e){return{ref:e.ref,local:e.folder,functions:Do(e).map(t=>({name:t,url:`${e.apiUrl}/functions/v1/${t}`}))}}function jo(e){let t=Do(e);g(Ft(e),()=>{if(t.length===0){c(`No functions in ${te(e.folder,"functions")}.`);return}process.stdout.write(`${U([["NAME","URL"],...t.map(n=>[n,`${e.apiUrl}/functions/v1/${n}`])])}
309
+ `)})}async function Nn(e,t){Re(t);let n=te(e.folder,"functions",t);if(!ot(n))throw new N("not-found",`this local project has no function called ${t}`);return Oo(n,{recursive:!0,force:!0}),Mo(e,t,!1),await Ut(e),{ref:e.ref,name:t,removed:!0}}async function Co(e,t){g(await Nn(e,t),()=>c(`${t} is gone.`))}function Bt(e){return te(e.folder,"functions",".env")}function Dn(e){let t=Bt(e);return ot(t)?_t(Ao(t,"utf8")):new Map}function Lo(e,t){let n=r=>r.includes("'")?JSON.stringify(r):`'${r}'`;No(Bt(e),[...t].map(([r,o])=>`${r}=${n(o)}`).join(`
310
310
  `)+(t.size?`
311
- `:""))}async function po(e,t){for(let r of t){let o=Ze(r.name);if(o)throw new y(o)}let n=fn(e);for(let r of t)n.set(r.name,r.value);fo(e,n),await $t(e),g({ref:e.ref,set:t.map(r=>r.name)},()=>{for(let r of t)c(`${P(r.name)} set in ${At(e)}`)})}function Rt(e){return{ref:e.ref,local:e.folder,secrets:[...fn(e).keys()].sort().map(t=>({name:t}))}}function go(e){let t=Rt(e),n=t.secrets.map(r=>r.name);g(t,()=>{c(n.length?n.join(`
312
- `):`No function secrets in ${At(e)}.`)})}async function ho(e,t){let n=fn(e);if(!n.delete(t))throw new L("not-found",`${t} is not set for this local project`);fo(e,n),await $t(e),g({ref:e.ref,unset:t},()=>c(`${t} unset.`))}Y();Oe();ke();rn();bt();q();cn();H();H();Y();ke();q();async function yo(){let{projects:e,allowance:t}=await X();g({projects:e,allowance:t},()=>{if(e.length===0){c('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${U([["REF","NAME","STATE","REGION","LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?P("read-only"):n.state==="ready"?n.state:P(n.state),n.region,le(n.lastConnectionAt)])])}
313
- `)})}async function pn(e){let t=await S("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;c(`Created ${n}.`);let r=t.project;if(e.wait){c("Waiting for it to start\u2026");let o="";r=await Xe(n,{timeoutMs:e.timeoutMs,onTick:s=>{s!==o&&(o=s,c(b(` ${s}`)))}})}e.link&&Ge(e.link,{ref:n,name:r.name}),g({project:r,password:t.password,uri:`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`},()=>{process.stdout.write(`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require
314
- `),c(""),c(`Ready. ${P(n)} in ${r.region}.`),c("The password is stored for you: `snoutdata db url` prints this again.")})}async function wo(e,t,n={}){let r=`cloud-project-${e}`,o=await S(r,{ref:t}),s=e==="delete"?"deleted":`${e}d`;if(n.wait===!1){g({...o,settled:!1},()=>c(`${t}: asked to be ${s}. \`snoutdata projects list\` shows when it is.`));return}let a=o.project?.state??"",u=await Ye(t,e,{timeoutMs:n.timeoutMs,onTick:m=>{m!==a&&(a=m,c(b(` ${m}`)))}}),i=u?.state??"deleted";if(i==="error")throw new L("failed",`${t} went to error instead of ${s}${u?.stateDetail?`: ${u.stateDetail}`:""}.`,{ref:t,state:i});g({...o,...u?{project:u}:{},settled:!0},()=>{c(o.changed?`${t}: ${s}.`:`${t} was already ${s}.`)})}async function bo(e){let t=await S("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
315
- `),c(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function So(){let{teams:e}=await Et();g({teams:e},()=>{if(e.length===0){c("You are not on any teams.");return}process.stdout.write(`${U([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
316
- `)})}async function xo(e){let{teams:t}=await Et(),n=t.find(s=>s.id===e),r=n?[n]:t.filter(s=>s.name.toLowerCase()===e.toLowerCase());if(r.length===0)throw new Error(t.length===0?`You are not on any teams, so there is no "${e}" to share with.`:`No team called "${e}". You are on: ${t.map(s=>s.name).join(", ")}.`);if(r.length>1)throw new Error(`More than one team is called "${e}". Use the id: ${r.map(s=>s.id).join(", ")}.`);let o=r[0];if(!o.mayShare)throw new Error(`${o.name}: ${o.why??"you cannot share a project into this team"}.`);return o.id}H();q();async function _o(e){let t=await S("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
317
- `),c(""),c(`${P(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),c(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),c("This is the only time it is shown: only its hash is stored."),c(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function ko(){let{tokens:e}=await S("cloud-token-list");g({tokens:e},()=>{if(e.length===0){c("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${U([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?P(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",le(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
318
- `)})}async function vo(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await S("cloud-token-revoke",t);g(n,()=>{c(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}H();ne();q();async function Eo(e){let t=await V(e);g({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){c(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${$o(t.anonKey,t.serviceRoleKey)}
319
- `),gc(t.keysIssuedAt)})}async function Po(e,t){if(!t.force)throw c(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),c("Anything holding one (a deployed app, a CI secret, a teammate) breaks until it is updated."),new y("keys rotate needs --force, because this cannot be undone");let n=await S("cloud-project-rotate-jwt",{ref:e});g(n,()=>{process.stdout.write(`${$o(n.anonKey,n.serviceRoleKey)}
320
- `),c(""),c(n.breaks),c(b(" snoutdata keys --json to read them again later"))})}function $o(e,t){return U([["KEY","VALUE"],["anon",e],["service_role",t]])}function gc(e){c(""),c(`${P("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),c(`${P("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&c(b(` issued ${e.slice(0,10)}`))}H();ne();q();import{readdir as yc,readFile as wc,stat as bc}from"node:fs/promises";import{join as hn,relative as Sc,sep as xc}from"node:path";var To=["functions"];async function gn(e){try{return(await bc(e)).isDirectory()}catch{return!1}}async function _c(e,t){if(t){if(!await gn(t))throw new y(`${t} is not a directory`);return t}for(let n of To){let r=hn(n,e);if(await gn(r))return r}throw new y(`no folder found for ${e} at ${To[0]}/${e}; use --dir to say where it is.`)}async function Ao(e){let t=[];async function n(r){for(let o of await yc(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=hn(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await wc(s),u=a.toString("utf8");if(Buffer.compare(Buffer.from(u,"utf8"),a)!==0)throw new y(`${s} is not text, and a function bundle is source code. Put binary assets in Storage.`);t.push({path:Sc(e,s).split(xc).join("/"),text:u})}}return await n(e),t}async function kc(e,t,n){let r=await Ao(t);if(r.length===0)throw new y(`${t} has no files in it`);let o=n??(r.some(u=>u.path==="index.ts")?"index.ts":"");if(!o)throw new y(`${t} has no index.ts, so there is nothing to start. Name the file with --entrypoint.`);let s=hn(t,"..","_shared");if(!await gn(s))return{entrypoint:o,files:r};let a=(await Ao(s)).map(u=>({path:`_shared/${u.path}`,text:u.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(u=>({path:`${e}/${u.path}`,text:u.text})),...a]}}async function yn(e,t,n){ve(t);let r=await _c(t,n.dir),{entrypoint:o,files:s}=await kc(t,r,n.entrypoint),a=no({entrypoint:o,files:s});return{answer:await S("cloud-project-functions",{ref:e,action:"deploy",name:t,verifyJwt:n.verifyJwt,bundle:a}),directory:r,files:s.length,bytes:JSON.stringify(a).length}}async function Ro(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await yn(e,t,n),u=r.functions.find(i=>i.name===t);g(r,()=>{let i=Math.max(1,Math.round(a/1024));c(`${P(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${i} KB)`),u&&c(` ${u.url}`),n.verifyJwt||(c(""),c(P(" Anybody who knows this URL can run it. No API key is required.")),c(b(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&c(b(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function wn(e){return S("cloud-project-functions",{ref:e})}function bn(e,t){return ve(t),S("cloud-project-functions",{ref:e,action:"delete",name:t})}function Sn(e,t,n){if(ve(t),!n.reset&&n.memoryMb===void 0&&n.concurrency===void 0)throw new y("functions size needs --memory MB, --concurrency N, or --reset");return S("cloud-project-functions",{ref:e,action:"size",name:t,...n.reset?{reset:!0}:{memoryMb:n.memoryMb??null,concurrency:n.concurrency??null}})}function vc(e,t){if(e.memoryMb==null||e.concurrency==null)return null;let n=e.memoryMb*e.concurrency,r=t.podMemoryMb?` of ${t.podMemoryMb} MB`:"";return`${e.memoryMb} MB x ${e.concurrency} worker${e.concurrency===1?"":"s"} = ${n} MB${r}`}function Oo(e){return e.functionMemoryMb==null||e.concurrencyMax==null||e.podMemoryMb==null?null:`${e.tier??"this"} plan: up to ${e.functionMemoryMb} MB a worker and ${e.concurrencyMax} workers a function, memory x workers up to ${e.podMemoryMb} MB`}async function No(e,t,n){let r=await Sn(e,t,n),o=r.functions.find(s=>s.name===t);g(r,()=>{let s=o?vc(o,r.limit):null;c(`${P(t)} ${n.reset?"is back to the plan default":"is resized"}${s?`: ${s}`:""}`),c(b(" The next request runs at it; workers already running finish what they hold."));let a=Oo(r.limit);a&&c(b(` ${a}`))})}async function Do(e){let t=await wn(e);g(t,()=>{if(t.functions.length===0){c(`${e} has no functions.`),c(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${U([["NAME","KEY","SIZE","MEMORY","WORKERS","URL"],...t.functions.map(r=>[r.name,r.verifyJwt?"required":P("open"),r.bytes===null?"-":`${Math.max(1,Math.round(r.bytes/1024))} KB`,r.memoryMb==null?"-":`${r.memoryMb} MB`,r.concurrency==null?"-":String(r.concurrency),r.url])])}
321
- `),t.limit.maxFunctions!==null&&c(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`));let n=Oo(t.limit);n&&c(b(`${n}. Change one with: snoutdata functions size <name> --memory MB --concurrency N`))})}async function Mo(e,t){let n=await bn(e,t);g(n,()=>{c(n.live?`${t} is gone.`:`${t} is gone. It stops answering within a few seconds.`)})}H();ne();q();function Io(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function Co(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function jo(e,t){if(e.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");if(t.stdin){if(e.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");return[{name:e[0],value:await Co()}]}return e.map(n=>{let r=Io(n);if(!r)throw new y(`${n} is not NAME=value. Use --stdin to set a value from a pipe.`);return r})}async function Lo(e,t,n){if(t.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");let r=[];if(n.stdin){if(t.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");r.push({name:t[0],value:await Co()})}else for(let s of t){let a=Io(s);if(!a)throw new y(`${s} is not NAME=value. Use --stdin to set a value from a pipe.`);r.push(a)}for(let s of r){let a=Ze(s.name);if(a)throw new y(a)}let o=null;for(let s of r)o=await S("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});g(o,()=>{for(let s of r)c(`${P(s.name)} set`);c(b(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||c(b(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function Uo(e,t){let n=Ze(t);if(n)throw new y(n);let r=await S("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{c(`${t} is gone.`)})}async function Fo(e){let t=await S("cloud-project-secrets",{ref:e});g(t,()=>{if(t.secrets.length===0){c(`${e} has no function secrets.`),c(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${U([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,le(n.updatedAt)])])}
322
- `),c(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as tt}from"node:child_process";import{appendFileSync as rs,existsSync as Ct,readFileSync as os,unlinkSync as qc,writeFileSync as je}from"node:fs";import{dirname as Gc,join as jt}from"node:path";import{tmpdir as Kc}from"node:os";import{open as ss}from"node:fs/promises";function Bo(e){let t=(r,o)=>String.fromCharCode(...Array.from(e.slice(r,r+o)));return t(0,5)==="PGDMP"?"custom":e.length>=262&&t(257,5)==="ustar"?"tar":Array.from(e.slice(0,256)).every(r=>r===9||r===10||r===13||r>=32&&r<127||r>=128)&&e.length>0?"plain":"unknown"}function Ho(e,t,n={}){return t.readOnly?{ok:!1,code:"read-only",reason:"this project is over its plan's storage limit, so it refuses writes. A restore would fail part way. Delete some rows or change plan first."}:e==="unknown"?{ok:!1,code:"unknown-format",reason:"that file is not a pg_dump archive and does not look like SQL. `snoutdata db export` writes the custom format; psql reads plain .sql."}:!n.force&&t.existingTables>0?{ok:!1,code:"not-empty",reason:`this database already has ${t.existingTables} table${t.existingTables===1?"":"s"}. Restoring into it would leave some objects created and some not. Check --ref is the project you meant, or pass --force.`}:{ok:!0,tool:e==="plain"?"psql":"pg_restore",format:e}}function qo(e){let t=e.split(`
323
- `).filter(r=>/^pg_restore: error:/.test(r.trim())),n=t.filter(r=>!/must be owner of extension/.test(r));return{total:t.length,harmless:t.length-n.length,fatal:n}}var xn={auth:"snout_auth_admin",storage:"snout_storage_admin",push:"snout_push_admin"},$c=/^(snoutpod_admin|snout_[a-z]+_admin)$/,Ot=new Set(["auth","storage","push","realtime","snout_realtime","_realtime","graphql","graphql_public","extensions","net","cron","pgbouncer","vault","pgsodium"]),Tc=/^(pgrst_ddl_watch|pgrst_drop_watch|snoutpod_[a-z_]+|snoutdata_realtime|snout_realtime_messages)(\(|$)/,Ac={auth:"auth",storage:"storage",push:"push"},Rc=new Set(["push.settings"]),Oc=["TABLE DATA","SEQUENCE SET","SEQUENCE OWNED BY","FK CONSTRAINT","CHECK CONSTRAINT","DEFAULT ACL","EVENT TRIGGER","ROW SECURITY","INDEX ATTACH","TABLE ATTACH","MATERIALIZED VIEW DATA","MATERIALIZED VIEW","PUBLICATION TABLES IN SCHEMA","PUBLICATION TABLE","FOREIGN TABLE","FOREIGN DATA WRAPPER","FOREIGN SERVER","USER MAPPING","TEXT SEARCH CONFIGURATION","TEXT SEARCH DICTIONARY","TEXT SEARCH PARSER","TEXT SEARCH TEMPLATE","LARGE OBJECT","PROCEDURAL LANGUAGE","OPERATOR CLASS","OPERATOR FAMILY","ACCESS METHOD","EXTENDED STATISTICS","DATABASE PROPERTIES"];function Nc(e){let t=/^\d+; \d+ \d+ (.+)$/.exec(e.trim());if(!t)return null;let n=t[1],r=Oc.find(i=>n.startsWith(`${i} `))??n.split(" ")[0],o=n.slice(r.length).trim().split(" "),s=o[0]??"-",a=o.length>2?o[o.length-1]:"",u=o.slice(1,o.length>2?-1:void 0).join(" ");return{line:e.trim(),type:r,schema:s,name:u,owner:a}}function Dc(e){let t=new Map,n=o=>o.replace(/"/g,""),r=/ALTER TABLE (?:ONLY )?([\w."$]+)\s+ADD CONSTRAINT [^;]*?FOREIGN KEY \([^)]*\) REFERENCES ([\w."$]+)\(/g;for(let o of e.matchAll(r)){let s=n(o[1]),a=n(o[2]);if(s!==a){let u=t.get(s)??new Set;u.add(a),t.set(s,u)}}return t}function Go(e,t){let n=new Map,r=[],o=new Set,s=0;for(let i of e.split(`
324
- `)){let m=Nc(i);if(!m)continue;let{type:l,schema:f,name:d,owner:p}=m;if(l==="TABLE DATA"||l==="SEQUENCE SET"){let _=/^(schema_migrations|migrations)$/.test(d)&&Ot.has(f);if(/realtime/.test(f)||_||f==="public"&&d==="spatial_ref_sys"){s++;continue}if(f==="cron"){s++,o.add("Scheduled jobs (pg_cron) are not restored: each names the project it came from. Schedule them again with cron.schedule.");continue}if(f==="push"&&d==="credentials"){s++,o.add("Push credentials are not restored: they are sealed for the project they came from. Set them again with `snoutdata push credentials set`.");continue}let h=Ac[f];if(h){h==="storage"&&d==="objects"&&o.add("Storage objects are restored as rows; the files themselves are not part of an export. Upload them again into this project."),n.set(h,[...n.get(h)??[],m]);continue}if(Ot.has(f)){s++;continue}r.push(m.line);continue}if(l==="COMMENT"&&f==="-"&&/^EXTENSION( |$)/.test(d)||(l==="SCHEMA"?Ot.has(d):Ot.has(f))||Tc.test(d)||$c.test(p)&&l!=="SCHEMA"){s++;continue}r.push(m.line)}let a=Dc(t),u=["auth","storage","push"];return{platformData:u.filter(i=>n.has(i)).map(i=>{let m=n.get(i),l=m.filter(d=>d.type==="TABLE DATA"&&Rc.has(`${d.schema}.${d.name}`)).map(d=>`${d.schema}.${d.name}`),f=m.filter(d=>d.type==="TABLE DATA").map(d=>`${d.schema}.${d.name}`);return{product:i,clear:l,tables:f,lines:Mc(m,a)}}),rest:r,products:u.filter(i=>n.has(i)),skipped:s,notes:[...o]}}function Mc(e,t){let n=u=>`${u.schema}.${u.name}`,r=new Set(e.map(n)),o=new Set,s=[],a=[...e];for(;a.length>0;){let i=a.find(m=>[...t.get(n(m))??[]].every(l=>!r.has(l)||o.has(l)||l===n(m)))??a[0];o.add(n(i)),s.push(i.line),a=a.filter(m=>m!==i)}return s}H();ne();q();var Ic="2024-11-05";function j(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var N={type:"string"},Nt=[{name:"whoami",description:"Who this CLI is signed in as, and how (a session or a long-lived access token). Use it to check a credential before doing anything else.",inputSchema:j({})},{name:"list_projects",description:"Every hosted Postgres database on this account, with its ref, name, region, state (ready, paused, error), size, and whether it is read-only because it is over its plan's storage limit. Under `local`, the self-hosted projects on this machine (SnoutData Studio's, or linked with `snoutdata link --local`), whose refs work with get_connection_url, push_migrations, get_project and the function tools.",inputSchema:j({})},{name:"create_project",description:"Make a new hosted Postgres database and wait for it to be ready. Costs a project against the plan allowance; a free plan includes two. Returns its ref, which every other tool takes.",inputSchema:j({name:N,region:N,teamId:N},["name"])},{name:"get_connection_url",description:"The postgres:// URL for a project, ready to put in DATABASE_URL. IT CONTAINS A LIVE PASSWORD, so treat it as a credential and do not print it into a log or a file that is committed. Connecting to a paused project wakes it, which takes a few seconds.",inputSchema:j({ref:N},["ref"])},{name:"pause_project",description:"Stop a project, and wait until it is paused. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:j({ref:N},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it, and wait until it is ready.",inputSchema:j({ref:N},["ref"])},{name:"push_migrations",description:"Run the .sql files in a folder against a project, in name order, once each, recording what ran in a _snoutdata_migrations table. Refuses a file that changed after it ran, a file that has gone, and a new file that sorts before one already applied. Use dryRun first to see what would happen. Needs psql on this machine.",inputSchema:j({ref:N,dir:N,dryRun:{type:"boolean"}},["ref"])},{name:"usage",description:"How much storage and compute a project has used against the limit on this plan, and whether it is near it. READ THIS BEFORE a migration or a bulk insert: a project over its storage limit is made read-only, and `list_projects` will say readOnly afterwards without ever saying how much headroom was left. Takes an optional number of days of history (default 30, maximum 365).",inputSchema:j({ref:N,days:{type:"number"}},["ref"])},{name:"export_status",description:"Whether a copy of this database is being taken, when the last one finished, and a download link if there is a live one. Costs nothing and starts nothing.",inputSchema:j({ref:N},["ref"])},{name:"start_export",description:"Take a copy of the whole database. Runs a pg_dump on the server and can take minutes, so this STARTS it and returns; poll export_status for the link. Worth doing before a migration you are unsure of. It does not block the database.",inputSchema:j({ref:N},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING within a few seconds, including any DATABASE_URL already written into a file or a running process; connections already open keep working. Returns the new connection URL, which works once the change applies (wait about five seconds before connecting). Use it when a credential has leaked, not routinely.",inputSchema:j({ref:N},["ref"])},{name:"list_teams",description:"Teams this account belongs to, with the id create_project takes as teamId. Without this there is no way to discover that id.",inputSchema:j({})},{name:"list_tokens",description:"The long-lived sdt_ access tokens on this account: id, name, prefix, when each was made and last used. Never the token itself, which is shown once when it is made.",inputSchema:j({})},{name:"create_token",description:"Mint a long-lived sdt_ access token, for a CI job or another machine. RETURNS THE TOKEN ONCE AND NEVER AGAIN. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:j({name:N,expires:N,project:N},["name"])},{name:"revoke_token",description:"Revoke an access token by its id. Anything using it stops working at once. Safe to do to a token you think has leaked.",inputSchema:j({id:N},["id"])},{name:"deploy_function",description:"Put a folder of TypeScript on the edge as an HTTP endpoint at https://<ref>.api.snoutdata.com/functions/v1/<name>. Reads functions/<name> unless dir says otherwise. Deno, so no build step and no node_modules. Set openToAnyone ONLY for a webhook receiver whose sender cannot send an API key: it makes the URL callable by anybody who knows it, and the function must then check the sender's signature itself.",inputSchema:j({ref:N,name:N,dir:N,entrypoint:N,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, bundle sizes, whether each one needs an API key, and each one's memory and concurrency with the plan's limits for both. Costs nothing.",inputSchema:j({ref:N},["ref"])},{name:"size_function",description:"Set one function's memory (MB one worker may use) and concurrency (how many workers it may run at once), within the project's plan. memoryMb x concurrency may not exceed the project's memory; list_functions shows each function's size and the plan's limits (limit.functionMemoryMb, limit.concurrencyMax, limit.podMemoryMb). A size that does not fit is refused with a sentence saying why. Omit one to keep it as it runs now; reset: true goes back to the plan's default. A second worker only helps CPU-bound work: requests that wait on the network share one.",inputSchema:j({ref:N,name:N,memoryMb:{type:"integer"},concurrency:{type:"integer"},reset:{type:"boolean"}},["ref","name"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:j({ref:N,name:N},["ref","name"])},{name:"list_function_secrets",description:"The NAMES of the environment variables a project's functions run with, their sizes and when each was last set. Never the values, which are not readable by anything but the runtime. THERE IS NO TOOL THAT SETS ONE: a secret in a tool call is a secret in an audit log, so a person sets it with `snoutdata secrets set`. Use this to check whether one a function needs is there.",inputSchema:j({ref:N},["ref"])},{name:"get_project",description:"Everything about one project in one call: its state, region and size, which products are on (auth, storage, data-api), the names of its deployed functions and function secrets, and its custom domains. Never a password, a key or a download link (export_status has the link to the last export).",inputSchema:j({ref:N},["ref"])},{name:"list_products",description:"Whether a project's auth (user sign-up and sign-in), storage (files) and data API (REST and GraphQL over its tables) are on, and whether the plan allows the data API.",inputSchema:j({ref:N},["ref"])},{name:"set_product",description:"Turn auth, storage or data-api on or off for a project. It starts within about a minute. The data API is on paid plans only, and a free project is refused in words.",inputSchema:j({ref:N,product:{type:"string",enum:["auth","storage","data-api"]},enabled:{type:"boolean"}},["ref","product","enabled"])},{name:"list_domains",description:"The custom domains in front of a project's API, whether each is verified, and the DNS records each needs.",inputSchema:j({ref:N},["ref"])},{name:"add_domain",description:"Serve a project's API at the user's own hostname (paid plans). Returns the DNS records the user must publish before verify_domain can succeed.",inputSchema:j({ref:N,hostname:N},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:j({ref:N,hostname:N},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:j({ref:N,hostname:N},["ref","hostname"])},{name:"restore_window",description:"How far back a point-in-time restore of a project can go, or why it cannot (the plan, or no backup yet). Starts nothing.",inputSchema:j({ref:N},["ref"])},{name:"restore_to_point",description:"Rewind a project to a moment (ISO 8601, inside restore_window) into a NEW project beside it, never over it. Costs a project slot on the plan. The original is untouched.",inputSchema:j({ref:N,at:N,name:N},["ref","at"])},{name:"delete_project",description:"Delete a project. THIS DESTROYS A DATABASE. Off unless the person who started this server passed --allow-delete, in which case it still only marks the project deleted and its data is erasable for a grace period.",inputSchema:j({ref:N},["ref"])}],Cc=new Set(["whoami","list_projects","get_connection_url","usage","export_status","list_teams","list_tokens","list_functions","list_function_secrets","get_project","list_products","list_domains","restore_window"]),jc=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]),Ko=["get_connection_url","push_migrations","get_project","list_functions","deploy_function","delete_function","list_function_secrets"];function _n(e){if(!e||typeof e!="object")return e;let t=e;if(Array.isArray(t.projects))return{...t,projects:t.projects.map(_n)};let n=t.export;if(n&&typeof n=="object"){let{url:r,rolesSql:o,...s}=n;return{...t,export:{...s,hasLink:typeof r=="string"&&r.length>0}}}return e}function vn(e,t){return t.available?null:t.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is part of the Pro and Business plans, and this project is on ${t.tier??"another plan"}.`}function Lc(e){return{...e,annotations:Cc.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:jc.has(e.name)}}}function Uc(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function et(e,t){return{jsonrpc:"2.0",id:e,result:t}}function kn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function $(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return et(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function Fc(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function B(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function Wo(e,t,n){let r=e.id??null,o=e.method??"",s=e.params??{};if(e.id===void 0||e.id===null)return null;switch(o){case"initialize":return et(r,{protocolVersion:Ic,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return et(r,{});case"tools/list":return et(r,{tools:Uc([...Nt.map(Lc),...n.borrowed??[]])});case"tools/call":return Bc(r,s,t,n);default:return kn(r,-32601,`unknown method: ${o}`)}}async function Bc(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(Nt.some(l=>l.name===o)||(r.borrowed?.some(l=>l.name===o)??!1)))return kn(e,-32602,`unknown tool: ${o}`);let u=["get_connection_url","pause_project","resume_project","delete_project","push_migrations","usage","export_status","start_export","reset_password","get_project","list_products","set_product","list_domains","add_domain","verify_domain","remove_domain","restore_window","restore_to_point"],i=B(s,"ref");if(u.includes(o)&&!i)return $(e,`${o} needs a project ref. Call list_projects to see them.`,!0);let m=i&&n.localProject?n.localProject(i):null;if(m&&!Ko.includes(o))return $(e,`${i} is the self-hosted project in ${m.folder}, and ${o} is about SnoutData Cloud. On a local project these tools work: ${Ko.join(", ")}.`,!0);try{switch(o){case"whoami":return $(e,await n.whoami());case"list_projects":return $(e,_n(await n.listProjects()));case"create_project":{let l=B(s,"name");return l?$(e,await n.createProject({name:l,region:B(s,"region")??void 0,teamId:B(s,"teamId")??void 0})):$(e,"create_project needs a name.",!0)}case"get_connection_url":{let l=await n.connection(i);return $(e,{url:l.uri,state:l.state,wakesInstantly:l.wakesInstantly,warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"pause_project":return $(e,await n.setProjectState("pause",i));case"resume_project":return $(e,await n.setProjectState("resume",i));case"usage":{let l=typeof s.days=="number"?s.days:void 0;return $(e,await n.usage(i,l))}case"export_status":return $(e,await n.exportStatus(i));case"start_export":return $(e,await n.requestExport(i));case"reset_password":{let l=await n.resetPassword(i),f=await n.connection(i);return $(e,{url:f.uri,appliesIn:l.appliesIn??"within a few seconds",next:"Wait about five seconds before connecting with this URL: until the change applies the new password is refused and the old one still works. Then update DATABASE_URL wherever it is set.",warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"list_teams":return $(e,await n.listTeams());case"get_project":return $(e,_n(await n.getProject(i)));case"list_products":return $(e,await n.getProducts(i));case"set_product":{let l=B(s,"product");return l!=="auth"&&l!=="storage"&&l!=="data-api"?$(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?$(e,"set_product needs enabled: true or false.",!0):$(e,await n.setProduct(i,l,s.enabled))}case"list_domains":return $(e,await n.listDomains(i));case"add_domain":case"verify_domain":case"remove_domain":{let l=B(s,"hostname");return l?$(e,await n.domainAction(i,o.split("_")[0],l)):$(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":{let l=await n.restoreWindow(i),f=l&&l.restore?vn(i,l.restore):null;return $(e,f?{...l,reason:f}:l)}case"restore_to_point":{let l=B(s,"at");return l?$(e,await n.restoreTo(i,l,B(s,"name")??void 0)):$(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let l=B(s,"ref"),f=B(s,"name");return!l||!f?$(e,"deploy_function needs a project ref and a function name.",!0):$(e,await n.deployFunction(l,f,{dir:B(s,"dir")??void 0,entrypoint:B(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let l=B(s,"ref");return l?$(e,await n.listFunctions(l)):$(e,"list_functions needs a project ref.",!0)}case"size_function":{let l=B(s,"ref"),f=B(s,"name");if(!l||!f)return $(e,"size_function needs a project ref and a function name.",!0);let d=typeof s.memoryMb=="number"?s.memoryMb:void 0,p=typeof s.concurrency=="number"?s.concurrency:void 0,w=s.reset===!0;return!w&&d===void 0&&p===void 0?$(e,"size_function needs memoryMb, concurrency, or reset: true.",!0):$(e,await n.sizeFunction(l,f,{memoryMb:d,concurrency:p,reset:w}))}case"delete_function":{let l=B(s,"ref"),f=B(s,"name");return!l||!f?$(e,"delete_function needs a project ref and a function name.",!0):$(e,await n.deleteFunction(l,f))}case"list_function_secrets":{let l=B(s,"ref");return l?$(e,await n.listFunctionSecrets(l)):$(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return $(e,await n.listTokens());case"create_token":{let l=B(s,"name");if(!l)return $(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let f=B(s,"expires"),d=f?Fc(f):void 0;return d===null?$(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${f}".`,!0):$(e,await n.createToken(l,d,B(s,"project")??void 0))}case"revoke_token":{let l=B(s,"id");return l?$(e,await n.revokeToken(l)):$(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return $(e,await n.pushMigrations(i,{dir:B(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?$(e,await n.setProjectState("delete",i)):$(e,"delete_project is off. The person running this server started it without --allow-delete, so deleting a database is theirs to do. Tell them that is what you wanted.",!0);default:return r.borrowed?.some(l=>l.name===o)?n.callBorrowed?et(e,await n.callBorrowed(o,s)):$(e,`${o} needs SnoutData Studio, which is no longer answering.`,!0):kn(e,-32602,`unknown tool: ${o}`)}}catch(l){return $(e,l instanceof Error?l.message:String(l),!0)}}function Jo(e){let t=e.split(`
325
- `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}var Dt=["auth","storage","data-api","push"];function Ee(e){return{error:e instanceof Error?e.message:String(e)}}async function Ce(e){let[t,n,r,o]=await Promise.all([S("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),Ee),S("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),Ee),S("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),Ee),S("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),Ee)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o}}function zo(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!Dt.includes(t))throw new y(`say which product: ${Dt.join(", ")}`);return t}async function En(e,t,n){if(t==="auth"){let r=await S("cloud-project-auth",{ref:e,action:n?"enable":"disable"});return{ref:e,product:t,enabled:n,note:r.note??(n?"Auth starts within a minute.":"Auth stops. Your users stay in the auth schema.")}}return t==="push"?(await S("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await S(t==="storage"?"cloud-project-storage":"cloud-project-data-api",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?`${t} starts within a minute, when the host picks up the change.`:`${t} is off.`})}async function Vo(e){let t=await Ce(e);g(t,()=>{let n=(u,i,m="")=>[u,i&&typeof i=="object"&&"error"in i?`could not read: ${i.error}`:i.enabled?P("on"):"off",m],r=t.storage,o=t.dataApi;process.stdout.write(`${U([["PRODUCT","STATE",""],n("auth",t.auth),n("storage",r,"pending"in r&&r.pending?"waiting for the host":""),n("data-api",o,"allowedOnPlan"in o&&!o.allowedOnPlan?"paid plans only":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":"")])}
326
- `);let s=u=>!!(u&&typeof u=="object"&&"enabled"in u&&!u.enabled),a=["auth","storage","data-api","push"].find(u=>s(u==="data-api"?o:t[u]));a&&c(b(` snoutdata products enable ${a} --ref ${e}`))})}async function Yo(e,t,n){let r=await En(e,t,n);g(r,()=>c(r.note))}async function Mt(e){let t=await S("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function Pn(e,t,n){if(!n)throw new y(`domains ${t} needs a hostname, e.g. api.example.com`);let r=await S("cloud-project-domain",{ref:e,action:t,hostname:n});return{ref:e,domain:r.domain??null,next:r.next??null}}function Hc(e,t){let n=[["TYPE","NAME","VALUE"]];e.record&&n.push([e.record.type,e.record.name,e.record.value]),e.certificateRecord&&n.push([e.certificateRecord.type,e.certificateRecord.name,e.certificateRecord.value]),n.push(["CNAME",e.hostname,`${t}.api.snoutdata.com`]),process.stdout.write(`${U(n)}
327
- `)}async function Xo(e,t,n){if(t===void 0||t==="list"){let o=await Mt(e);g(o,()=>{if(o.domains.length===0){c(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${U([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":P("no"),s.certificate?.state??""])])}
328
- `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await Pn(e,t,n??"");g(r,()=>{if(t==="remove"){c(`${n} removed.`);return}r.domain&&(c(r.domain.verified?`${n} is verified.`:r.domain.verificationError??`${n} is not verified yet. Publish these records, then \`snoutdata domains verify ${n}\`:`),r.domain.verified||Hc(r.domain,e)),r.next&&c(r.next)})}async function $n(e){return S("cloud-project-restore",{ref:e},"GET")}async function Tn(e,t,n){if(Number.isNaN(Date.parse(t)))throw new y(`--at needs a moment, e.g. 2026-09-20T14:30:00Z (got "${t}")`);return S("cloud-project-restore",{ref:e,at:new Date(t).toISOString(),...n?{name:n}:{}})}async function Zo(e){let t=await $n(e);g(t,()=>{let n=t.restore,r=vn(e,n);if(r){c(r);return}c(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),c(b(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function Qo(e,t,n){let r=await Tn(e,t,n);g(r,()=>{c(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${P(r.project.name)} (${r.project.ref}).`),c(b(" snoutdata projects list to see when it is ready"))})}async function An(e){let{projects:t}=await X(),n=t.find(u=>u.ref===e);if(!n)throw new y(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([Ce(e),S("cloud-project-functions",{ref:e}).then(u=>(u.functions??[]).map(i=>i.name),Ee),S("cloud-project-secrets",{ref:e}).then(u=>(u.secrets??[]).map(i=>i.name),Ee),Mt(e).then(u=>u.domains.map(i=>({hostname:i.hostname,verified:i.verified})),Ee)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function es(e){let t=await An(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${U([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],["auth / storage / data-api / push",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)} / ${r(n.products.push)}`],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
329
- `)})}H();Y();Oe();H();q();async function as(e){try{let{projects:t}=await X(),n=t.find(r=>r.ref===e);n?.readOnly?z(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&z(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function On(e){await as(e);let t=await V(e);g(t,()=>{process.stdout.write(`${t.uri}
330
- `)})}async function is(e,t){if(t.length===0&&!pe()){let o=Re();A("usage",`db psql with no statements opens an interactive session, and ${o?Ae(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await as(e);let n=await V(e);n.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let r=tt("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,PGSSLMODE:n.ssl==="disable"?"disable":"require"}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new L("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new L("failed",String(a)))}),r.on("close",a=>o(a??0))})}function cs(e,t,n="DATABASE_URL"){let r=jt(e,".env"),o=`${n}=${t}`,s=Wc(e);if(!Ct(r))return je(r,`${o}
331
- `),{path:r,replaced:!1,ignored:s};let a=os(r,"utf8"),u=new RegExp(`^${n}=.*$`,"m");return u.test(a)?(je(r,a.replace(u,o)),{path:r,replaced:!0,ignored:s}):(rs(r,a.endsWith(`
311
+ `:""))}async function Uo(e,t){for(let r of t){let o=rt(r.name);if(o)throw new y(o)}let n=Dn(e);for(let r of t)n.set(r.name,r.value);Lo(e,n),await Ut(e),g({ref:e.ref,set:t.map(r=>r.name)},()=>{for(let r of t)c(`${v(r.name)} set in ${Bt(e)}`)})}function Ht(e){return{ref:e.ref,local:e.folder,secrets:[...Dn(e).keys()].sort().map(t=>({name:t}))}}function Fo(e){let t=Ht(e),n=t.secrets.map(r=>r.name);g(t,()=>{c(n.length?n.join(`
312
+ `):`No function secrets in ${Bt(e)}.`)})}async function Bo(e,t){let n=Dn(e);if(!n.delete(t))throw new N("not-found",`${t} is not set for this local project`);Lo(e,n),await Ut(e),g({ref:e.ref,unset:t},()=>c(`${t} unset.`))}X();Me();he();kn();Pt();q();jt();K();K();X();he();q();async function Ho(){let{projects:e,allowance:t}=await ee();g({projects:e,allowance:t},()=>{if(e.length===0){c('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${U([["REF","NAME","STATE","REGION","LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?v("read-only"):n.state==="ready"?n.state:v(n.state),n.region,me(n.lastConnectionAt)])])}
313
+ `)})}async function Mn(e){let t=await x("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;c(`Created ${n}.`);let r=t.project;if(e.wait){c("Waiting for it to start\u2026");let s="";r=await nt(n,{timeoutMs:e.timeoutMs,onTick:a=>{a!==s&&(s=a,c(b(` ${a}`)))}})}e.link&&We(e.link,{ref:n,name:r.name});let o=`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`;g({project:r,ref:n,host:r.host,database:r.database,user:r.user,...e.showUrl?{password:t.password,uri:o}:{connectionString:"snoutdata db url"}},()=>{process.stdout.write(`${e.showUrl?o:n}
314
+ `),c(""),c(`Ready. ${v(n)} in ${r.region}, at ${r.host}.`),c(e.showUrl?"The password is stored for you: `snoutdata db url` prints this again.":"`snoutdata db url` prints the connection string (it holds the password, so it is not shown here).")})}async function qo(e,t,n={}){let r=`cloud-project-${e}`,o=await x(r,{ref:t}),s=e==="delete"?"deleted":`${e}d`;if(n.wait===!1){g({...o,settled:!1},()=>c(`${t}: asked to be ${s}. \`snoutdata projects list\` shows when it is.`));return}let a=o.project?.state??"",u=await tt(t,e,{timeoutMs:n.timeoutMs,onTick:d=>{d!==a&&(a=d,c(b(` ${d}`)))}}),i=u?.state??"deleted";if(i==="error")throw new N("failed",`${t} went to error instead of ${s}${u?.stateDetail?`: ${u.stateDetail}`:""}.`,{ref:t,state:i});g({...o,...u?{project:u}:{},settled:!0},()=>{c(o.changed?`${t}: ${s}.`:`${t} was already ${s}.`)})}async function Go(e){let t=await x("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
315
+ `),c(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function Ko(){let{teams:e}=await Ct();g({teams:e},()=>{if(e.length===0){c("You are not on any teams.");return}process.stdout.write(`${U([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
316
+ `)})}async function Wo(e){let{teams:t}=await Ct(),n=t.find(s=>s.id===e),r=n?[n]:t.filter(s=>s.name.toLowerCase()===e.toLowerCase());if(r.length===0)throw new Error(t.length===0?`You are not on any teams, so there is no "${e}" to share with.`:`No team called "${e}". You are on: ${t.map(s=>s.name).join(", ")}.`);if(r.length>1)throw new Error(`More than one team is called "${e}". Use the id: ${r.map(s=>s.id).join(", ")}.`);let o=r[0];if(!o.mayShare)throw new Error(`${o.name}: ${o.why??"you cannot share a project into this team"}.`);return o.id}K();q();async function Jo(e){let t=await x("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
317
+ `),c(""),c(`${v(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),c(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),c("This is the only time it is shown: only its hash is stored."),c(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function zo(){let{tokens:e}=await x("cloud-token-list");g({tokens:e},()=>{if(e.length===0){c("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${U([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?v(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",me(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
318
+ `)})}async function Vo(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await x("cloud-token-revoke",t);g(n,()=>{c(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}K();re();q();async function Yo(e){let t=await Z(e);g({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){c(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${Zo(t.anonKey,t.serviceRoleKey)}
319
+ `),Qc(t.keysIssuedAt)})}async function Xo(e,t){if(!t.force)throw c(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),c("Anything holding one (a deployed app, a CI secret, a teammate) breaks until it is updated."),new y("keys rotate needs --force, because this cannot be undone");let n=await x("cloud-project-rotate-jwt",{ref:e});g(n,()=>{process.stdout.write(`${Zo(n.anonKey,n.serviceRoleKey)}
320
+ `),c(""),c(n.breaks),c(b(" snoutdata keys --json to read them again later"))})}function Zo(e,t){return U([["KEY","VALUE"],["anon",e],["service_role",t]])}function Qc(e){c(""),c(`${v("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),c(`${v("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&c(b(` issued ${e.slice(0,10)}`))}K();re();q();import{readdir as tl,readFile as nl,stat as rl}from"node:fs/promises";import{join as jn,relative as ol,sep as sl}from"node:path";var Qo=["functions"];async function In(e){try{return(await rl(e)).isDirectory()}catch{return!1}}async function al(e,t){if(t){if(!await In(t))throw new y(`${t} is not a directory`);return t}for(let n of Qo){let r=jn(n,e);if(await In(r))return r}throw new y(`no folder found for ${e} at ${Qo[0]}/${e}; use --dir to say where it is.`)}async function es(e){let t=[];async function n(r){for(let o of await tl(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=jn(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await nl(s),u=a.toString("utf8");if(Buffer.compare(Buffer.from(u,"utf8"),a)!==0)throw new y(`${s} is not text, and a function bundle is source code. Put binary assets in Storage.`);t.push({path:ol(e,s).split(sl).join("/"),text:u})}}return await n(e),t}async function il(e,t,n){let r=await es(t);if(r.length===0)throw new y(`${t} has no files in it`);let o=n??(r.some(u=>u.path==="index.ts")?"index.ts":"");if(!o)throw new y(`${t} has no index.ts, so there is nothing to start. Name the file with --entrypoint.`);let s=jn(t,"..","_shared");if(!await In(s))return{entrypoint:o,files:r};let a=(await es(s)).map(u=>({path:`_shared/${u.path}`,text:u.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(u=>({path:`${e}/${u.path}`,text:u.text})),...a]}}async function Cn(e,t,n){Re(t);let r=await al(t,n.dir),{entrypoint:o,files:s}=await il(t,r,n.entrypoint),a=Ro({entrypoint:o,files:s});return{answer:await x("cloud-project-functions",{ref:e,action:"deploy",name:t,verifyJwt:n.verifyJwt,bundle:a}),directory:r,files:s.length,bytes:JSON.stringify(a).length}}async function ts(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await Cn(e,t,n),u=r.functions.find(i=>i.name===t);g(r,()=>{let i=Math.max(1,Math.round(a/1024));c(`${v(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${i} KB)`),u&&c(` ${u.url}`),n.verifyJwt||(c(""),c(v(" Anybody who knows this URL can run it. No API key is required.")),c(b(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&c(b(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function Ln(e){return x("cloud-project-functions",{ref:e})}function Un(e,t){return Re(t),x("cloud-project-functions",{ref:e,action:"delete",name:t})}function Fn(e,t,n){if(Re(t),!n.reset&&n.memoryMb===void 0&&n.concurrency===void 0)throw new y("functions size needs --memory MB, --concurrency N, or --reset");return x("cloud-project-functions",{ref:e,action:"size",name:t,...n.reset?{reset:!0}:{memoryMb:n.memoryMb??null,concurrency:n.concurrency??null}})}function cl(e,t){if(e.memoryMb==null||e.concurrency==null)return null;let n=e.memoryMb*e.concurrency,r=t.podMemoryMb?` of ${t.podMemoryMb} MB`:"";return`${e.memoryMb} MB x ${e.concurrency} worker${e.concurrency===1?"":"s"} = ${n} MB${r}`}function ns(e){return e.functionMemoryMb==null||e.concurrencyMax==null||e.podMemoryMb==null?null:`${e.tier??"this"} plan: up to ${e.functionMemoryMb} MB a worker and ${e.concurrencyMax} workers a function, memory x workers up to ${e.podMemoryMb} MB`}async function rs(e,t,n){let r=await Fn(e,t,n),o=r.functions.find(s=>s.name===t);g(r,()=>{let s=o?cl(o,r.limit):null;c(`${v(t)} ${n.reset?"is back to the plan default":"is resized"}${s?`: ${s}`:""}`),c(b(" The next request runs at it; workers already running finish what they hold."));let a=ns(r.limit);a&&c(b(` ${a}`))})}async function os(e){let t=await Ln(e);g(t,()=>{if(t.functions.length===0){c(`${e} has no functions.`),c(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${U([["NAME","KEY","SIZE","MEMORY","WORKERS","URL"],...t.functions.map(r=>[r.name,r.verifyJwt?"required":v("open"),r.bytes===null?"-":`${Math.max(1,Math.round(r.bytes/1024))} KB`,r.memoryMb==null?"-":`${r.memoryMb} MB`,r.concurrency==null?"-":String(r.concurrency),r.url])])}
321
+ `),t.limit.maxFunctions!==null&&c(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`));let n=ns(t.limit);n&&c(b(`${n}. Change one with: snoutdata functions size <name> --memory MB --concurrency N`))})}async function ss(e,t){let n=await Un(e,t);g(n,()=>{c(n.live?`${t} is gone.`:`${t} is gone. It stops answering within a few seconds.`)})}K();re();q();function as(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function is(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function cs(e,t){if(e.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");if(t.stdin){if(e.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");return[{name:e[0],value:await is()}]}return e.map(n=>{let r=as(n);if(!r)throw new y(`${n} is not NAME=value. Use --stdin to set a value from a pipe.`);return r})}async function ls(e,t,n){if(t.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");let r=[];if(n.stdin){if(t.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");r.push({name:t[0],value:await is()})}else for(let s of t){let a=as(s);if(!a)throw new y(`${s} is not NAME=value. Use --stdin to set a value from a pipe.`);r.push(a)}for(let s of r){let a=rt(s.name);if(a)throw new y(a)}let o=null;for(let s of r)o=await x("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});g(o,()=>{for(let s of r)c(`${v(s.name)} set`);c(b(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||c(b(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function us(e,t){let n=rt(t);if(n)throw new y(n);let r=await x("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{c(`${t} is gone.`)})}async function ds(e){let t=await x("cloud-project-secrets",{ref:e});g(t,()=>{if(t.secrets.length===0){c(`${e} has no function secrets.`),c(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${U([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,me(n.updatedAt)])])}
322
+ `),c(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as at}from"node:child_process";import{appendFileSync as Rs,existsSync as zt,readFileSync as Ts,unlinkSync as Rl,writeFileSync as Ue}from"node:fs";import{dirname as Tl,join as Vt}from"node:path";import{tmpdir as Al}from"node:os";import{open as As}from"node:fs/promises";function ms(e){let t=(r,o)=>String.fromCharCode(...Array.from(e.slice(r,r+o)));return t(0,5)==="PGDMP"?"custom":e.length>=262&&t(257,5)==="ustar"?"tar":Array.from(e.slice(0,256)).every(r=>r===9||r===10||r===13||r>=32&&r<127||r>=128)&&e.length>0?"plain":"unknown"}function fs(e,t,n={}){return t.readOnly?{ok:!1,code:"read-only",reason:"this project is over its plan's storage limit, so it refuses writes. A restore would fail part way. Delete some rows or change plan first."}:e==="unknown"?{ok:!1,code:"unknown-format",reason:"that file is not a pg_dump archive and does not look like SQL. `snoutdata db export` writes the custom format; psql reads plain .sql."}:!n.force&&t.existingTables>0?{ok:!1,code:"not-empty",reason:`this database already has ${t.existingTables} table${t.existingTables===1?"":"s"}. Restoring into it would leave some objects created and some not. Check --ref is the project you meant, or pass --force.`}:{ok:!0,tool:e==="plain"?"psql":"pg_restore",format:e}}function ps(e){let t=e.split(`
323
+ `).filter(r=>/^pg_restore: error:/.test(r.trim())),n=t.filter(r=>!/must be owner of extension/.test(r));return{total:t.length,harmless:t.length-n.length,fatal:n}}var Bn={auth:"snout_auth_admin",storage:"snout_storage_admin",push:"snout_push_admin"},dl=/^(snoutpod_admin|snout_[a-z]+_admin)$/,qt=new Set(["auth","storage","push","realtime","snout_realtime","_realtime","graphql","graphql_public","extensions","net","cron","pgbouncer","vault","pgsodium"]),ml=/^(pgrst_ddl_watch|pgrst_drop_watch|snoutpod_[a-z_]+|snoutdata_realtime|snout_realtime_messages)(\(|$)/,fl={auth:"auth",storage:"storage",push:"push"},pl=new Set(["push.settings"]),gl=["TABLE DATA","SEQUENCE SET","SEQUENCE OWNED BY","FK CONSTRAINT","CHECK CONSTRAINT","DEFAULT ACL","EVENT TRIGGER","ROW SECURITY","INDEX ATTACH","TABLE ATTACH","MATERIALIZED VIEW DATA","MATERIALIZED VIEW","PUBLICATION TABLES IN SCHEMA","PUBLICATION TABLE","FOREIGN TABLE","FOREIGN DATA WRAPPER","FOREIGN SERVER","USER MAPPING","TEXT SEARCH CONFIGURATION","TEXT SEARCH DICTIONARY","TEXT SEARCH PARSER","TEXT SEARCH TEMPLATE","LARGE OBJECT","PROCEDURAL LANGUAGE","OPERATOR CLASS","OPERATOR FAMILY","ACCESS METHOD","EXTENDED STATISTICS","DATABASE PROPERTIES"];function hl(e){let t=/^\d+; \d+ \d+ (.+)$/.exec(e.trim());if(!t)return null;let n=t[1],r=gl.find(i=>n.startsWith(`${i} `))??n.split(" ")[0],o=n.slice(r.length).trim().split(" "),s=o[0]??"-",a=o.length>2?o[o.length-1]:"",u=o.slice(1,o.length>2?-1:void 0).join(" ");return{line:e.trim(),type:r,schema:s,name:u,owner:a}}function yl(e){let t=new Map,n=o=>o.replace(/"/g,""),r=/ALTER TABLE (?:ONLY )?([\w."$]+)\s+ADD CONSTRAINT [^;]*?FOREIGN KEY \([^)]*\) REFERENCES ([\w."$]+)\(/g;for(let o of e.matchAll(r)){let s=n(o[1]),a=n(o[2]);if(s!==a){let u=t.get(s)??new Set;u.add(a),t.set(s,u)}}return t}function gs(e,t){let n=new Map,r=[],o=new Set,s=0;for(let i of e.split(`
324
+ `)){let d=hl(i);if(!d)continue;let{type:l,schema:f,name:h,owner:m}=d;if(l==="TABLE DATA"||l==="SEQUENCE SET"){let S=/^(schema_migrations|migrations)$/.test(h)&&qt.has(f);if(/realtime/.test(f)||S||f==="public"&&h==="spatial_ref_sys"){s++;continue}if(f==="cron"){s++,o.add("Scheduled jobs (pg_cron) are not restored: each names the project it came from. Schedule them again with cron.schedule.");continue}if(f==="push"&&h==="credentials"){s++,o.add("Push credentials are not restored: they are sealed for the project they came from. Set them again with `snoutdata push credentials set`.");continue}let w=fl[f];if(w){w==="storage"&&h==="objects"&&o.add("Storage objects are restored as rows; the files themselves are not part of an export. Upload them again into this project."),n.set(w,[...n.get(w)??[],d]);continue}if(qt.has(f)){s++;continue}r.push(d.line);continue}if(l==="COMMENT"&&f==="-"&&/^EXTENSION( |$)/.test(h)||(l==="SCHEMA"?qt.has(h):qt.has(f))||ml.test(h)||dl.test(m)&&l!=="SCHEMA"){s++;continue}r.push(d.line)}let a=yl(t),u=["auth","storage","push"];return{platformData:u.filter(i=>n.has(i)).map(i=>{let d=n.get(i),l=d.filter(h=>h.type==="TABLE DATA"&&pl.has(`${h.schema}.${h.name}`)).map(h=>`${h.schema}.${h.name}`),f=d.filter(h=>h.type==="TABLE DATA").map(h=>`${h.schema}.${h.name}`);return{product:i,clear:l,tables:f,lines:wl(d,a)}}),rest:r,products:u.filter(i=>n.has(i)),skipped:s,notes:[...o]}}function wl(e,t){let n=u=>`${u.schema}.${u.name}`,r=new Set(e.map(n)),o=new Set,s=[],a=[...e];for(;a.length>0;){let i=a.find(d=>[...t.get(n(d))??[]].every(l=>!r.has(l)||o.has(l)||l===n(d)))??a[0];o.add(n(i)),s.push(i.line),a=a.filter(d=>d!==i)}return s}K();re();q();var bl="2024-11-05";function F(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var D={type:"string"},Gt=[{name:"whoami",description:"Who this CLI is signed in as, and how (a session or a long-lived access token). Use it to check a credential before doing anything else.",inputSchema:F({})},{name:"list_projects",description:"Every hosted Postgres database on this account, with its ref, name, region, state (ready, paused, error), size, and whether it is read-only because it is over its plan's storage limit. Under `local`, the self-hosted projects on this machine (SnoutData Studio's, or linked with `snoutdata link --local`), whose refs work with get_connection_url, push_migrations, get_project and the function tools.",inputSchema:F({})},{name:"create_project",description:"Make a new hosted Postgres database and wait for it to be ready. Costs a project against the plan allowance; a free plan includes two. Returns its ref, which every other tool takes.",inputSchema:F({name:D,region:D,teamId:D},["name"])},{name:"get_connection_url",description:"The postgres:// URL for a project, ready to put in DATABASE_URL. IT CONTAINS A LIVE PASSWORD, so treat it as a credential and do not print it into a log or a file that is committed. Connecting to a paused project wakes it, which takes a few seconds.",inputSchema:F({ref:D},["ref"])},{name:"pause_project",description:"Stop a project, and wait until it is paused. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:F({ref:D},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it, and wait until it is ready.",inputSchema:F({ref:D},["ref"])},{name:"push_migrations",description:"Run the .sql files in a folder against a project, in name order, once each, recording what ran in a _snoutdata_migrations table. Refuses a file that changed after it ran, a file that has gone, and a new file that sorts before one already applied. Use dryRun first to see what would happen. Needs psql on this machine.",inputSchema:F({ref:D,dir:D,dryRun:{type:"boolean"}},["ref"])},{name:"usage",description:"How much storage and compute a project has used against the limit on this plan, and whether it is near it. READ THIS BEFORE a migration or a bulk insert: a project over its storage limit is made read-only, and `list_projects` will say readOnly afterwards without ever saying how much headroom was left. Takes an optional number of days of history (default 30, maximum 365).",inputSchema:F({ref:D,days:{type:"number"}},["ref"])},{name:"export_status",description:"Whether a copy of this database is being taken, when the last one finished, and a download link if there is a live one. Costs nothing and starts nothing.",inputSchema:F({ref:D},["ref"])},{name:"start_export",description:"Take a copy of the whole database. Runs a pg_dump on the server and can take minutes, so this STARTS it and returns; poll export_status for the link. Worth doing before a migration you are unsure of. It does not block the database.",inputSchema:F({ref:D},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING within a few seconds, including any DATABASE_URL already written into a file or a running process; connections already open keep working. Returns the new connection URL, which works once the change applies (wait about five seconds before connecting). Use it when a credential has leaked, not routinely.",inputSchema:F({ref:D},["ref"])},{name:"list_teams",description:"Teams this account belongs to, with the id create_project takes as teamId. Without this there is no way to discover that id.",inputSchema:F({})},{name:"list_tokens",description:"The long-lived sdt_ access tokens on this account: id, name, prefix, when each was made and last used. Never the token itself, which is shown once when it is made.",inputSchema:F({})},{name:"create_token",description:"Mint a long-lived sdt_ access token, for a CI job or another machine. RETURNS THE TOKEN ONCE AND NEVER AGAIN. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:F({name:D,expires:D,project:D},["name"])},{name:"revoke_token",description:"Revoke an access token by its id. Anything using it stops working at once. Safe to do to a token you think has leaked.",inputSchema:F({id:D},["id"])},{name:"deploy_function",description:"Put a folder of TypeScript on the edge as an HTTP endpoint at https://<ref>.api.snoutdata.com/functions/v1/<name>. Reads functions/<name> unless dir says otherwise. Deno, so no build step and no node_modules. Set openToAnyone ONLY for a webhook receiver whose sender cannot send an API key: it makes the URL callable by anybody who knows it, and the function must then check the sender's signature itself.",inputSchema:F({ref:D,name:D,dir:D,entrypoint:D,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, bundle sizes, whether each one needs an API key, and each one's memory and concurrency with the plan's limits for both. Costs nothing.",inputSchema:F({ref:D},["ref"])},{name:"size_function",description:"Set one function's memory (MB one worker may use) and concurrency (how many workers it may run at once), within the project's plan. memoryMb x concurrency may not exceed the project's memory; list_functions shows each function's size and the plan's limits (limit.functionMemoryMb, limit.concurrencyMax, limit.podMemoryMb). A size that does not fit is refused with a sentence saying why. Omit one to keep it as it runs now; reset: true goes back to the plan's default. A second worker only helps CPU-bound work: requests that wait on the network share one.",inputSchema:F({ref:D,name:D,memoryMb:{type:"integer"},concurrency:{type:"integer"},reset:{type:"boolean"}},["ref","name"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:F({ref:D,name:D},["ref","name"])},{name:"list_function_secrets",description:"The NAMES of the environment variables a project's functions run with, their sizes and when each was last set. Never the values, which are not readable by anything but the runtime. THERE IS NO TOOL THAT SETS ONE: a secret in a tool call is a secret in an audit log, so a person sets it with `snoutdata secrets set`. Use this to check whether one a function needs is there.",inputSchema:F({ref:D},["ref"])},{name:"get_project",description:"Everything about one project in one call: its state, region and size, which products are on (auth, storage, data-api), the names of its deployed functions and function secrets, and its custom domains. Never a password, a key or a download link (export_status has the link to the last export).",inputSchema:F({ref:D},["ref"])},{name:"list_products",description:"Whether a project's auth (user sign-up and sign-in), storage (files) and data API (REST and GraphQL over its tables) are on, and whether the plan allows the data API.",inputSchema:F({ref:D},["ref"])},{name:"set_product",description:"Turn auth, storage or data-api on or off for a project. It starts within about a minute. The data API is on paid plans only, and a free project is refused in words.",inputSchema:F({ref:D,product:{type:"string",enum:["auth","storage","data-api"]},enabled:{type:"boolean"}},["ref","product","enabled"])},{name:"list_domains",description:"The custom domains in front of a project's API, whether each is verified, and the DNS records each needs.",inputSchema:F({ref:D},["ref"])},{name:"add_domain",description:"Serve a project's API at the user's own hostname (paid plans). Returns the DNS records the user must publish before verify_domain can succeed.",inputSchema:F({ref:D,hostname:D},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:F({ref:D,hostname:D},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:F({ref:D,hostname:D},["ref","hostname"])},{name:"restore_window",description:"How far back a point-in-time restore of a project can go, or why it cannot (the plan, or no backup yet). Starts nothing.",inputSchema:F({ref:D},["ref"])},{name:"restore_to_point",description:"Rewind a project to a moment (ISO 8601, inside restore_window) into a NEW project beside it, never over it. Costs a project slot on the plan. The original is untouched.",inputSchema:F({ref:D,at:D,name:D},["ref","at"])},{name:"delete_project",description:"Delete a project. THIS DESTROYS A DATABASE. Off unless the person who started this server passed --allow-delete, in which case it still only marks the project deleted and its data is erasable for a grace period.",inputSchema:F({ref:D},["ref"])}],Sl=new Set(["whoami","list_projects","get_connection_url","usage","export_status","list_teams","list_tokens","list_functions","list_function_secrets","get_project","list_products","list_domains","restore_window"]),xl=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]),hs=["get_connection_url","push_migrations","get_project","list_functions","deploy_function","delete_function","list_function_secrets"];function Hn(e){if(!e||typeof e!="object")return e;let t=e;if(Array.isArray(t.projects))return{...t,projects:t.projects.map(Hn)};let n=t.export;if(n&&typeof n=="object"){let{url:r,rolesSql:o,...s}=n;return{...t,export:{...s,hasLink:typeof r=="string"&&r.length>0}}}return e}function Gn(e,t){return t.available?null:t.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is part of the Pro and Business plans, and this project is on ${t.tier??"another plan"}.`}function _l(e){return{...e,annotations:Sl.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:xl.has(e.name)}}}function kl(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function st(e,t){return{jsonrpc:"2.0",id:e,result:t}}function qn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function T(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return st(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function vl(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function G(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function ys(e,t,n){let r=e.id??null,o=e.method??"",s=e.params??{};if(e.id===void 0||e.id===null)return null;switch(o){case"initialize":return st(r,{protocolVersion:bl,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return st(r,{});case"tools/list":return st(r,{tools:kl([...Gt.map(_l),...n.borrowed??[]])});case"tools/call":return El(r,s,t,n);default:return qn(r,-32601,`unknown method: ${o}`)}}async function El(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(Gt.some(l=>l.name===o)||(r.borrowed?.some(l=>l.name===o)??!1)))return qn(e,-32602,`unknown tool: ${o}`);let u=["get_connection_url","pause_project","resume_project","delete_project","push_migrations","usage","export_status","start_export","reset_password","get_project","list_products","set_product","list_domains","add_domain","verify_domain","remove_domain","restore_window","restore_to_point"],i=G(s,"ref");if(u.includes(o)&&!i)return T(e,`${o} needs a project ref. Call list_projects to see them.`,!0);let d=i&&n.localProject?n.localProject(i):null;if(d&&!hs.includes(o))return T(e,`${i} is the self-hosted project in ${d.folder}, and ${o} is about SnoutData Cloud. On a local project these tools work: ${hs.join(", ")}.`,!0);try{switch(o){case"whoami":return T(e,await n.whoami());case"list_projects":return T(e,Hn(await n.listProjects()));case"create_project":{let l=G(s,"name");return l?T(e,await n.createProject({name:l,region:G(s,"region")??void 0,teamId:G(s,"teamId")??void 0})):T(e,"create_project needs a name.",!0)}case"get_connection_url":{let l=await n.connection(i);return T(e,{url:l.uri,state:l.state,wakesInstantly:l.wakesInstantly,warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"pause_project":return T(e,await n.setProjectState("pause",i));case"resume_project":return T(e,await n.setProjectState("resume",i));case"usage":{let l=typeof s.days=="number"?s.days:void 0;return T(e,await n.usage(i,l))}case"export_status":return T(e,await n.exportStatus(i));case"start_export":return T(e,await n.requestExport(i));case"reset_password":{let l=await n.resetPassword(i),f=await n.connection(i);return T(e,{url:f.uri,appliesIn:l.appliesIn??"within a few seconds",next:"Wait about five seconds before connecting with this URL: until the change applies the new password is refused and the old one still works. Then update DATABASE_URL wherever it is set.",warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"list_teams":return T(e,await n.listTeams());case"get_project":return T(e,Hn(await n.getProject(i)));case"list_products":return T(e,await n.getProducts(i));case"set_product":{let l=G(s,"product");return l!=="auth"&&l!=="storage"&&l!=="data-api"?T(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?T(e,"set_product needs enabled: true or false.",!0):T(e,await n.setProduct(i,l,s.enabled))}case"list_domains":return T(e,await n.listDomains(i));case"add_domain":case"verify_domain":case"remove_domain":{let l=G(s,"hostname");return l?T(e,await n.domainAction(i,o.split("_")[0],l)):T(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":{let l=await n.restoreWindow(i),f=l&&l.restore?Gn(i,l.restore):null;return T(e,f?{...l,reason:f}:l)}case"restore_to_point":{let l=G(s,"at");return l?T(e,await n.restoreTo(i,l,G(s,"name")??void 0)):T(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let l=G(s,"ref"),f=G(s,"name");return!l||!f?T(e,"deploy_function needs a project ref and a function name.",!0):T(e,await n.deployFunction(l,f,{dir:G(s,"dir")??void 0,entrypoint:G(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let l=G(s,"ref");return l?T(e,await n.listFunctions(l)):T(e,"list_functions needs a project ref.",!0)}case"size_function":{let l=G(s,"ref"),f=G(s,"name");if(!l||!f)return T(e,"size_function needs a project ref and a function name.",!0);let h=typeof s.memoryMb=="number"?s.memoryMb:void 0,m=typeof s.concurrency=="number"?s.concurrency:void 0,p=s.reset===!0;return!p&&h===void 0&&m===void 0?T(e,"size_function needs memoryMb, concurrency, or reset: true.",!0):T(e,await n.sizeFunction(l,f,{memoryMb:h,concurrency:m,reset:p}))}case"delete_function":{let l=G(s,"ref"),f=G(s,"name");return!l||!f?T(e,"delete_function needs a project ref and a function name.",!0):T(e,await n.deleteFunction(l,f))}case"list_function_secrets":{let l=G(s,"ref");return l?T(e,await n.listFunctionSecrets(l)):T(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return T(e,await n.listTokens());case"create_token":{let l=G(s,"name");if(!l)return T(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let f=G(s,"expires"),h=f?vl(f):void 0;return h===null?T(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${f}".`,!0):T(e,await n.createToken(l,h,G(s,"project")??void 0))}case"revoke_token":{let l=G(s,"id");return l?T(e,await n.revokeToken(l)):T(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return T(e,await n.pushMigrations(i,{dir:G(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?T(e,await n.setProjectState("delete",i)):T(e,"delete_project is off. The person running this server started it without --allow-delete, so deleting a database is theirs to do. Tell them that is what you wanted.",!0);default:return r.borrowed?.some(l=>l.name===o)?n.callBorrowed?st(e,await n.callBorrowed(o,s)):T(e,`${o} needs SnoutData Studio, which is no longer answering.`,!0):qn(e,-32602,`unknown tool: ${o}`)}}catch(l){return T(e,l instanceof Error?l.message:String(l),!0)}}function ws(e){let t=e.split(`
325
+ `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}var Kt=["auth","storage","data-api","push"];function Te(e){return{error:e instanceof Error?e.message:String(e)}}async function Le(e){let[t,n,r,o]=await Promise.all([x("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),Te),x("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),Te),x("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),Te),x("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),Te)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o,realtime:$l}}var $l={enabled:!0,broadcast:!0,presence:!0,tableChanges:"Plus and Pro plans"};function bs(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!Kt.includes(t))throw new y(`say which product: ${Kt.join(", ")}`);return t}async function Kn(e,t,n){if(t==="auth"){let r=await x("cloud-project-auth",{ref:e,action:n?"enable":"disable"});return{ref:e,product:t,enabled:n,note:r.note??(n?"Auth starts within a minute.":"Auth stops. Your users stay in the auth schema.")}}return t==="push"?(await x("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await x(t==="storage"?"cloud-project-storage":"cloud-project-data-api",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?`${t} starts within a minute, when the host picks up the change.`:`${t} is off.`})}async function Ss(e){let t=await Le(e);g(t,()=>{let n=(u,i,d="")=>[u,i&&typeof i=="object"&&"error"in i?`could not read: ${i.error}`:i.enabled?v("on"):"off",d],r=t.storage,o=t.dataApi;process.stdout.write(`${U([["PRODUCT","STATE",""],n("auth",t.auth),n("storage",r,"pending"in r&&r.pending?"waiting for the host":""),n("data-api",o,"allowedOnPlan"in o&&!o.allowedOnPlan?"paid plans only":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":""),n("realtime",t.realtime,"always on: broadcast, presence; table changes on Plus and Pro")])}
326
+ `);let s=u=>!!(u&&typeof u=="object"&&"enabled"in u&&!u.enabled),a=["auth","storage","data-api","push"].find(u=>s(u==="data-api"?o:t[u]));a&&c(b(` snoutdata products enable ${a} --ref ${e}`))})}async function xs(e,t,n){let r=await Kn(e,t,n);g(r,()=>c(r.note))}async function Wt(e){let t=await x("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function Wn(e,t,n){if(!n)throw new y(`domains ${t} needs a hostname, e.g. api.example.com`);let r=await x("cloud-project-domain",{ref:e,action:t,hostname:n});return{ref:e,domain:r.domain??null,next:r.next??null}}function Pl(e,t){let n=[["TYPE","NAME","VALUE"]];e.record&&n.push([e.record.type,e.record.name,e.record.value]),e.certificateRecord&&n.push([e.certificateRecord.type,e.certificateRecord.name,e.certificateRecord.value]),n.push(["CNAME",e.hostname,`${t}.api.snoutdata.com`]),process.stdout.write(`${U(n)}
327
+ `)}async function _s(e,t,n){if(t===void 0||t==="list"){let o=await Wt(e);g(o,()=>{if(o.domains.length===0){c(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${U([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":v("no"),s.certificate?.state??""])])}
328
+ `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await Wn(e,t,n??"");g(r,()=>{if(t==="remove"){c(`${n} removed.`);return}r.domain&&(c(r.domain.verified?`${n} is verified.`:r.domain.verificationError??`${n} is not verified yet. Publish these records, then \`snoutdata domains verify ${n}\`:`),r.domain.verified||Pl(r.domain,e)),r.next&&c(r.next)})}async function Jn(e){return x("cloud-project-restore",{ref:e},"GET")}async function zn(e,t,n){if(Number.isNaN(Date.parse(t)))throw new y(`--at needs a moment, e.g. 2026-09-20T14:30:00Z (got "${t}")`);return x("cloud-project-restore",{ref:e,at:new Date(t).toISOString(),...n?{name:n}:{}})}async function ks(e){let t=await Jn(e);g(t,()=>{let n=t.restore,r=Gn(e,n);if(r){c(r);return}c(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),c(b(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function vs(e,t,n){let r=await zn(e,t,n);g(r,()=>{c(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${v(r.project.name)} (${r.project.ref}).`),c(b(" snoutdata projects list to see when it is ready"))})}async function Vn(e){let{projects:t}=await ee(),n=t.find(u=>u.ref===e);if(!n)throw new y(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([Le(e),x("cloud-project-functions",{ref:e}).then(u=>(u.functions??[]).map(i=>i.name),Te),x("cloud-project-secrets",{ref:e}).then(u=>(u.secrets??[]).map(i=>i.name),Te),Wt(e).then(u=>u.domains.map(i=>({hostname:i.hostname,verified:i.verified})),Te)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Es(e){let t=await Vn(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${U([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],["auth / storage / data-api / push",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)} / ${r(n.products.push)}`],["realtime","on (broadcast and presence; table changes on Plus and Pro)"],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
329
+ `)})}K();X();Me();K();q();async function Os(e){try{let{projects:t}=await ee(),n=t.find(r=>r.ref===e);n?.readOnly?W(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&W(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function Xn(e){await Os(e);let t=await Z(e);g(t,()=>{process.stdout.write(`${t.uri}
330
+ `)})}async function Ns(e,t){if(t.length===0&&!be()){let o=De();O("usage",`db psql with no statements opens an interactive session, and ${o?Ne(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Os(e);let n=await Z(e);n.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let r=at("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,PGSSLMODE:n.ssl==="disable"?"disable":"require"}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new N("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new N("failed",String(a)))}),r.on("close",a=>o(a??0))})}function Ds(e,t,n="DATABASE_URL"){let r=Vt(e,".env"),o=`${n}=${t}`,s=Ol(e);if(!zt(r))return Ue(r,`${o}
331
+ `),{path:r,replaced:!1,ignored:s};let a=Ts(r,"utf8"),u=new RegExp(`^${n}=.*$`,"m");return u.test(a)?(Ue(r,a.replace(u,o)),{path:r,replaced:!0,ignored:s}):(Rs(r,a.endsWith(`
332
332
  `)?`${o}
333
333
  `:`
334
334
  ${o}
335
- `),{path:r,replaced:!1,ignored:s})}function Wc(e){let t=e;for(;!Ct(jt(t,".git"));){let s=Gc(t);if(s===t)return!1;t=s}let n=jt(e,".gitignore"),r=Ct(n)?os(n,"utf8"):"";return r.split(/\r?\n/).some(s=>/^\/?(\.env|\.env\*|\*\.env|\.env\.\*)$/.test(s.trim()))?!1:(rs(n,r===""||r.endsWith(`
335
+ `),{path:r,replaced:!1,ignored:s})}function Ol(e){let t=e;for(;!zt(Vt(t,".git"));){let s=Tl(t);if(s===t)return!1;t=s}let n=Vt(e,".gitignore"),r=zt(n)?Ts(n,"utf8"):"";return r.split(/\r?\n/).some(s=>/^\/?(\.env|\.env\*|\*\.env|\.env\.\*)$/.test(s.trim()))?!1:(Rs(n,r===""||r.endsWith(`
336
336
  `)?`.env
337
337
  `:`
338
338
  .env
339
- `),!0)}async function ls(e,t={}){if(t.statusOnly){let{export:a}=await Me(e);return g({ref:e,export:a},()=>Rn(a)),0}let n=await Ve(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>Rn(n.export)),0;c("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await zc(e,t.timeoutMs);if(r.error&&A("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>Rn(r)),0;r.url||A("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await Vc(r.url,t.out),s=r.rolesSql?Jc(t.out):null;return s&&r.rolesSql&&je(s,r.rolesSql.endsWith(`
339
+ `),!0)}async function Ms(e,t={}){if(t.statusOnly){let{export:a}=await je(e);return g({ref:e,export:a},()=>Yn(a)),0}let n=await et(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>Yn(n.export)),0;c("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await Dl(e,t.timeoutMs);if(r.error&&O("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>Yn(r)),0;r.url||O("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await Ml(r.url,t.out),s=r.rolesSql?Nl(t.out):null;return s&&r.rolesSql&&Ue(s,r.rolesSql.endsWith(`
340
340
  `)?r.rolesSql:`${r.rolesSql}
341
- `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{c(`Saved ${ue(o)} to ${t.out}.`),c(b(`Into a SnoutData project: snoutdata db restore --file ${t.out} --ref <ref>`)),s?(c(`Saved the roles it needs to ${s}.`),c(b("Into any other Postgres, in this order:")),c(b(` psql --dbname <your database> -f ${s}`)),c(b(` pg_restore --dbname <your database> ${t.out}`)),c(b("The roles are created without passwords. Set them yourself if anything signs in as one."))):(c(b("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),c(b("Owners and grants are skipped: this export carries no roles file.")))}),0}function Jc(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function zc(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await Me(e);if(!r.pending)return r;if(Date.now()>=n)throw new Error(`the export of ${e} was still running after waiting`);await new Promise(o=>setTimeout(o,3e3))}}async function Vc(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw new Error(`the download failed: ${n.status} ${n.statusText}`);let r=await ss(t,"w"),o=0;try{for await(let s of n.body)await r.write(s),o+=s.byteLength}finally{await r.close()}return o}function Rn(e){if(e.error){z(`The last export did not finish: ${e.error}`);return}if(e.pending){c("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){c("This project has never been exported.");return}c(`A copy taken ${le(e.completedAt)}, ${ue(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
342
- `),e.urlExpiresAt&&c(b(`That link stops working ${le(e.urlExpiresAt)}.`))):c(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function ue(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],n=e/1024,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n.toFixed(n>=10?0:1)} ${t[r]}`}async function us(e,t){let n=t.file;n||A("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),Ct(n)||A("usage",`no file at ${n}`,{file:n});let r=await ss(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=Bo(o.slice(0,s.bytesRead)),u=await V(e);u.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:i}=await X(),m=i.find(_=>_.ref===e),l=await Yc(u);l===null&&A("tool-missing","psql is not installed, so the tables in this project could not be counted. Install the Postgres client tools, or restore with your own client using `snoutdata db url`.");let f=Ho(a,{existingTables:l,readOnly:m?.readOnly===!0},{force:t.force});f.ok||A("conflict",f.reason,{ref:e,file:n,refusal:f.code});let d=null;if(f.tool==="pg_restore"){let _=await ns("pg_restore",["--list",n]),h=await ns("pg_restore",["--section=post-data","--file=-",n]);if((_.code!==0||h.code!==0)&&A("failed",`pg_restore could not read ${n}: ${(_.stderr||h.stderr).trim().slice(0,300)}`,{ref:e,file:n}),d=Go(_.stdout,h.stdout),d.products.length>0){let k=await Ce(e),R=T=>!!(T&&typeof T=="object"&&"enabled"in T&&T.enabled),x=d.products.filter(T=>!R(k[T]));x.length>0&&A("conflict",`this dump holds ${x.length>1?`${x.slice(0,-1).join(", ")} and ${x[x.length-1]}`:x[0]} data, and ${e} has ${x.length===1?"it":"them"} off. Their tables are the platform's, so the product has to be on to take the rows. Switch ${x.length===1?"it":"them"} on, wait a minute, and run this again:
343
- ${x.map(T=>` snoutdata products enable ${T} --ref ${e}`).join(`
344
- `)}`,{ref:e,file:n,refusal:"products-off",products:x})}}if(d&&d.platformData.length>0){let _=d.platformData.flatMap(({tables:R})=>R),h=await Xc(u,_);h===null&&A("failed",`could not check ${e} for the tables this dump loads into.`,{ref:e});let k=d.platformData.filter(({tables:R})=>R.some(x=>h.includes(x))).map(({product:R})=>R);k.length>0&&A("conflict",`${k.join(", ")} on ${e} ${k.length===1?"has":"have"} not finished setting up ${k.length===1?"its":"their"} tables yet. Run this again in a minute.`,{ref:e,file:n,refusal:"products-starting",products:k})}c(`Restoring ${n} into ${e} with ${f.tool} (${f.format} format).`);let p=d?await Zc(u,n,d):await It(u,f.tool,n),w=qo(p.stderr);for(let _ of d?.notes??[])c(b(_));return p.code!==0&&w.fatal.length===0&&w.total>0?(g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format,ignoredErrors:w.harmless},()=>{c(`Restored into ${e}.`),c(`${w.harmless} error${w.harmless===1?"":"s"} above ${w.harmless===1?"is":"are"} expected and nothing is missing: a dump carries comments on the extensions this image installs, and your role does not own them.`)}),0):(p.code!==0&&A("failed",`${f.tool} exited ${p.code}. Nothing here retried it: a half-finished restore is undone by deleting the project and making another.`,{ref:e,file:n,errors:w.fatal}),g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format},()=>c(`Restored into ${e}.`)),0)}async function Yc(e){return new Promise(t=>{let n=tt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc","select count(*) from pg_class c join pg_namespace n on n.oid = c.relnamespace where c.relkind = 'r' and n.nspname not in ('pg_catalog', 'information_schema') and n.nspname not like 'pg\\_toast%' and not exists (select 1 from pg_depend d where d.objid = c.oid and d.deptype = 'e') and pg_get_userbyid(c.relowner) !~ '^(snoutpod_admin|snout_[a-z]+_admin)$'"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{z(o.code==="ENOENT"?"psql is not installed, and db restore needs it (and pg_restore for an archive).":String(o)),t(null)}),n.on("close",o=>t(o===0?Number(r.trim())||0:null))})}function Xc(e,t){if(t.length===0)return Promise.resolve([]);let n=t.map(r=>`'${r.replace(/'/g,"''")}'`).join(", ");return new Promise(r=>{let o=tt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc",`select t from unnest(array[${n}]) t where to_regclass(t) is null`],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),s="";o.stdout.on("data",a=>s+=String(a)),o.on("error",()=>r(null)),o.on("close",a=>r(a===0?s.split(`
345
- `).map(u=>u.trim()).filter(Boolean):null))})}function ns(e,t){return new Promise(n=>{let r=tt(e,t,{stdio:["ignore","pipe","pipe"]}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{z(a.code==="ENOENT"?`${e} is not installed, and this dump needs it.`:String(a)),n({code:127,stdout:o,stderr:s})}),r.on("close",a=>n({code:a??0,stdout:o,stderr:s}))})}async function Zc(e,t,n){let r=jt(Kc(),`snoutdata-restore-${process.pid}-${Date.now()}`),o=0,s="";try{for(let{product:u,clear:i,lines:m}of n.platformData){if(i.length>0){je(`${r}.sql`,`set role ${xn[u]};
346
- ${i.map(d=>`delete from ${d};`).join(`
341
+ `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{c(`Saved ${pe(o)} to ${t.out}.`),c(b(`Into a SnoutData project: snoutdata db restore --file ${t.out} --ref <ref>`)),s?(c(`Saved the roles it needs to ${s}.`),c(b("Into any other Postgres, in this order:")),c(b(` psql --dbname <your database> -f ${s}`)),c(b(` pg_restore --dbname <your database> ${t.out}`)),c(b("The roles are created without passwords. Set them yourself if anything signs in as one."))):(c(b("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),c(b("Owners and grants are skipped: this export carries no roles file.")))}),0}function Nl(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function Dl(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await je(e);if(!r.pending)return r;if(Date.now()>=n)throw new Error(`the export of ${e} was still running after waiting`);await new Promise(o=>setTimeout(o,3e3))}}async function Ml(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw new Error(`the download failed: ${n.status} ${n.statusText}`);let r=await As(t,"w"),o=0;try{for await(let s of n.body)await r.write(s),o+=s.byteLength}finally{await r.close()}return o}function Yn(e){if(e.error){W(`The last export did not finish: ${e.error}`);return}if(e.pending){c("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){c("This project has never been exported.");return}c(`A copy taken ${me(e.completedAt)}, ${pe(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
342
+ `),e.urlExpiresAt&&c(b(`That link stops working ${me(e.urlExpiresAt)}.`))):c(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function pe(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],n=e/1024,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n.toFixed(n>=10?0:1)} ${t[r]}`}async function Is(e,t){let n=t.file;n||O("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),zt(n)||O("usage",`no file at ${n}`,{file:n});let r=await As(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=ms(o.slice(0,s.bytesRead)),u=await Z(e);u.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:i}=await ee(),d=i.find(S=>S.ref===e),l=await Il(u);l===null&&O("tool-missing","psql is not installed, so the tables in this project could not be counted. Install the Postgres client tools, or restore with your own client using `snoutdata db url`.");let f=fs(a,{existingTables:l,readOnly:d?.readOnly===!0},{force:t.force});f.ok||O("conflict",f.reason,{ref:e,file:n,refusal:f.code});let h=null;if(f.tool==="pg_restore"){let S=await Ps("pg_restore",["--list",n]),w=await Ps("pg_restore",["--section=post-data","--file=-",n]);if((S.code!==0||w.code!==0)&&O("failed",`pg_restore could not read ${n}: ${(S.stderr||w.stderr).trim().slice(0,300)}`,{ref:e,file:n}),h=gs(S.stdout,w.stdout),h.products.length>0){let _=await Le(e),$=k=>!!(k&&typeof k=="object"&&"enabled"in k&&k.enabled),A=h.products.filter(k=>!$(_[k]));A.length>0&&O("conflict",`this dump holds ${A.length>1?`${A.slice(0,-1).join(", ")} and ${A[A.length-1]}`:A[0]} data, and ${e} has ${A.length===1?"it":"them"} off. Their tables are the platform's, so the product has to be on to take the rows. Switch ${A.length===1?"it":"them"} on, wait a minute, and run this again:
343
+ ${A.map(k=>` snoutdata products enable ${k} --ref ${e}`).join(`
344
+ `)}`,{ref:e,file:n,refusal:"products-off",products:A})}}if(h&&h.platformData.length>0){let S=h.platformData.flatMap(({tables:$})=>$),w=await jl(u,S);w===null&&O("failed",`could not check ${e} for the tables this dump loads into.`,{ref:e});let _=h.platformData.filter(({tables:$})=>$.some(A=>w.includes(A))).map(({product:$})=>$);_.length>0&&O("conflict",`${_.join(", ")} on ${e} ${_.length===1?"has":"have"} not finished setting up ${_.length===1?"its":"their"} tables yet. Run this again in a minute.`,{ref:e,file:n,refusal:"products-starting",products:_})}c(`Restoring ${n} into ${e} with ${f.tool} (${f.format} format).`);let m=h?await Cl(u,n,h):await Jt(u,f.tool,n),p=ps(m.stderr);for(let S of h?.notes??[])c(b(S));return m.code!==0&&p.fatal.length===0&&p.total>0?(g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format,ignoredErrors:p.harmless},()=>{c(`Restored into ${e}.`),c(`${p.harmless} error${p.harmless===1?"":"s"} above ${p.harmless===1?"is":"are"} expected and nothing is missing: a dump carries comments on the extensions this image installs, and your role does not own them.`)}),0):(m.code!==0&&O("failed",`${f.tool} exited ${m.code}. Nothing here retried it: a half-finished restore is undone by deleting the project and making another.`,{ref:e,file:n,errors:p.fatal}),g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format},()=>c(`Restored into ${e}.`)),0)}async function Il(e){return new Promise(t=>{let n=at("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc","select count(*) from pg_class c join pg_namespace n on n.oid = c.relnamespace where c.relkind = 'r' and n.nspname not in ('pg_catalog', 'information_schema') and n.nspname not like 'pg\\_toast%' and not exists (select 1 from pg_depend d where d.objid = c.oid and d.deptype = 'e') and pg_get_userbyid(c.relowner) !~ '^(snoutpod_admin|snout_[a-z]+_admin)$'"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{W(o.code==="ENOENT"?"psql is not installed, and db restore needs it (and pg_restore for an archive).":String(o)),t(null)}),n.on("close",o=>t(o===0?Number(r.trim())||0:null))})}function jl(e,t){if(t.length===0)return Promise.resolve([]);let n=t.map(r=>`'${r.replace(/'/g,"''")}'`).join(", ");return new Promise(r=>{let o=at("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc",`select t from unnest(array[${n}]) t where to_regclass(t) is null`],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),s="";o.stdout.on("data",a=>s+=String(a)),o.on("error",()=>r(null)),o.on("close",a=>r(a===0?s.split(`
345
+ `).map(u=>u.trim()).filter(Boolean):null))})}function Ps(e,t){return new Promise(n=>{let r=at(e,t,{stdio:["ignore","pipe","pipe"]}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{W(a.code==="ENOENT"?`${e} is not installed, and this dump needs it.`:String(a)),n({code:127,stdout:o,stderr:s})}),r.on("close",a=>n({code:a??0,stdout:o,stderr:s}))})}async function Cl(e,t,n){let r=Vt(Al(),`snoutdata-restore-${process.pid}-${Date.now()}`),o=0,s="";try{for(let{product:u,clear:i,lines:d}of n.platformData){if(i.length>0){Ue(`${r}.sql`,`set role ${Bn[u]};
346
+ ${i.map(h=>`delete from ${h};`).join(`
347
347
  `)}
348
- `,"utf8");let f=await It(e,"psql",`${r}.sql`,["--quiet"]);o=o||f.code,s+=f.stderr}je(`${r}.platform`,`${m.join(`
348
+ `,"utf8");let f=await Jt(e,"psql",`${r}.sql`,["--quiet"]);o=o||f.code,s+=f.stderr}Ue(`${r}.platform`,`${d.join(`
349
349
  `)}
350
- `,"utf8");let l=await It(e,"pg_restore",t,["--data-only",`--role=${xn[u]}`,`--use-list=${r}.platform`]);o=o||l.code,s+=l.stderr}je(`${r}.rest`,`${n.rest.join(`
350
+ `,"utf8");let l=await Jt(e,"pg_restore",t,["--data-only",`--role=${Bn[u]}`,`--use-list=${r}.platform`]);o=o||l.code,s+=l.stderr}Ue(`${r}.rest`,`${n.rest.join(`
351
351
  `)}
352
- `,"utf8");let a=await It(e,"pg_restore",t,[`--use-list=${r}.rest`]);return{code:o||a.code,stderr:s+a.stderr}}finally{for(let a of[".platform",".rest",".sql"])try{qc(`${r}${a}`)}catch{}}}function It(e,t,n,r=[]){let o=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],s=t==="psql"?[...o,"--no-psqlrc","-v","ON_ERROR_STOP=1",...r,"-f",n]:[...o,"--no-owner","--no-privileges",...r,n];return new Promise(a=>{let u=tt(t,s,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),i="";u.stderr?.on("data",m=>{let l=String(m);i+=l,process.stderr.write(l)}),u.on("error",m=>{z(m.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(m)),a({code:127,stderr:i})}),u.on("close",m=>a({code:m??0,stderr:i}))})}H();ne();q();import{readFile as el}from"node:fs/promises";var tl="api.snoutdata.com";function fs(e,t){return{ref:e,enabled:t.enabled===!0,siteUrl:t.settings?.site_url??null,redirects:t.settings?.uri_allow_list??[],google:{enabled:t.google?.enabled===!0,clientId:t.google?.clientId??null,clientSecretSet:t.google?.clientSecretSet===!0,ready:t.google?.ready===!0},googleCallback:`https://${e}.${tl}/auth/v1/callback`}}async function nl(e){return fs(e,await S("cloud-project-auth",{ref:e,action:"status"}))}async function Nn(e,t){return fs(e,await S("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function Dn(e){let t=e.google,n="off";t.ready?n=P("on"):t.enabled&&(n=`switched on, not offered yet: needs ${[!t.clientId&&"a client id",!t.clientSecretSet&&"a client secret"].filter(Boolean).join(" and ")}`),process.stdout.write(`${U([["auth",e.enabled?P("on"):"off"],["site url",e.siteUrl??b("not set")],["redirects",e.redirects.length?e.redirects.join(", "):b("none")],["google",n],["google client id",t.clientId??b("not set")],["google callback",e.googleCallback]])}
353
- `),e.enabled||c(b(` snoutdata products enable auth --ref ${e.ref}`))}async function rl(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\r?\n$/,"")}async function ps(e,t,n,r){if(t===void 0||t==="show"){let o=await nl(e);g(o,()=>Dn(o));return}if(t==="google"){if(n[0]==="off"){let a=await Nn(e,{googleEnabled:!1});g(a,()=>c("Google sign-in is off. Your auth service restarts within about a minute."));return}if(n.length>0)throw new y('auth google takes --client-id and --stdin, or "off"');let o={googleEnabled:!0};if(r.clientId&&(o.googleClientId=r.clientId),r.stdin){let a=await rl();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await Nn(e,o);g(s,()=>{s.google.ready?c("Google sign-in is on. Your auth service restarts within about a minute."):Dn(s),c(b(` Add ${s.googleCallback} to your Google client's Authorized redirect URIs.`))});return}if(t==="redirects"){let o={};if(r.siteUrl!==void 0&&(o.siteUrl=r.siteUrl||null),r.allow!==void 0&&(o.uriAllowList=r.allow.split(",").map(a=>a.trim()).filter(Boolean)),Object.keys(o).length===0)throw new y("auth redirects needs --site-url and/or --allow URL,URL (an empty --allow= clears the list)");let s=await Nn(e,o);g(s,()=>Dn(s));return}if(t==="templates"){let o=await S("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>ms(o.templates));return}if(t==="template"){let o=n[0];if(!o||!ds.includes(o))throw new y(`auth template needs a kind: ${ds.join(", ")}`);let s;if(n[1]==="reset")s={kind:o,reset:!0};else{if(!r.file)throw new y(`auth template ${o} needs --file body.html (and --subject), or "reset"`);let u=await el(r.file,"utf8"),i=await S("cloud-project-auth",{ref:e,action:"templates"}),m=r.subject??i.templates.find(l=>l.kind===o)?.subject;s={kind:o,subject:m,body:u}}let a=await S("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{ms(a.templates),c(b(" Your auth service restarts with it within about a minute."))});return}throw new y(`unknown command: auth ${t}. auth [google [off] | redirects | templates | template KIND]`)}var ds=["confirmation","recovery","magic_link","invite","email_change"];function ms(e){process.stdout.write(`${U([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?P("yours"):"ours",t.subject])])}
354
- `)}H();q();Y();import{spawn as sl}from"node:child_process";import{createHash as al}from"node:crypto";import{existsSync as il,readdirSync as cl,readFileSync as ll,statSync as ul}from"node:fs";import{isAbsolute as dl,join as Mn,relative as ml,resolve as fl}from"node:path";function ol(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function Lt(e,t,n={}){let r=ol(e),o=new Map(r.map(m=>[m.name,m])),s=[];for(let m of t){let l=o.get(m.name);if(!l){s.push({code:"missing",name:m.name,reason:`${m.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}l.checksum!==m.checksum&&s.push({code:"changed",name:m.name,reason:`${m.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(m=>m.name).sort((m,l)=>m<l?-1:m>l?1:0)[t.length-1],u=new Set(t.map(m=>m.name)),i=[];for(let m of r)if(!u.has(m.name)){if(a!==void 0&&m.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:m.name,reason:`${m.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}i.push(m)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:i}}function nt(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function hs(e){let t=ml(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var rt="_snoutdata_migrations",pl=`create table if not exists ${rt} (
352
+ `,"utf8");let a=await Jt(e,"pg_restore",t,[`--use-list=${r}.rest`]);return{code:o||a.code,stderr:s+a.stderr}}finally{for(let a of[".platform",".rest",".sql"])try{Rl(`${r}${a}`)}catch{}}}function Jt(e,t,n,r=[]){let o=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],s=t==="psql"?[...o,"--no-psqlrc","-v","ON_ERROR_STOP=1",...r,"-f",n]:[...o,"--no-owner","--no-privileges",...r,n];return new Promise(a=>{let u=at(t,s,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),i="";u.stderr?.on("data",d=>{let l=String(d);i+=l,process.stderr.write(l)}),u.on("error",d=>{W(d.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(d)),a({code:127,stderr:i})}),u.on("close",d=>a({code:d??0,stderr:i}))})}K();re();q();var Ul=6e4;async function Zn(e,t={}){return x("cloud-project-realtime",{ref:e,...t.channel?{channel:t.channel}:{},...t.since!==void 0?{since:t.since}:{}})}function Fl(e,t=Date.now()){let n=/^(\d+)\s*(s|m|h|d)$/i.exec(e.trim());if(n){let o={s:1e3,m:6e4,h:36e5,d:864e5}[n[2].toLowerCase()];return t-Number(n[1])*o}let r=Date.parse(e);if(Number.isFinite(r))return r;throw new y(`--since wants a duration like 10m, 2h or 1d, or a time like 2026-10-03T14:00, not "${e}"`)}function Bl(e){let t=new Date(e),n=r=>String(r).padStart(2,"0");return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}function js(e){if(e===null)return"-";let t=Math.round(e/1e3);if(t<60)return`${t}s ago`;let n=Math.round(t/60);return n<60?`${n}m ago`:`${Math.round(n/60)}h ago`}var Hl={connect:"connected",connect_refused:"refused at connect",join:"joined",join_refused:"join refused",leave:"left",channel_closed:"channel closed by the server",disconnect:"disconnected"};function Cs(e){let t=[e.presence_key?e.presence_key:null,e.socket!==void 0?`socket ${e.socket}`:null].filter(Boolean).join(", "),n=[Bl(e.at),Hl[e.kind]??e.kind];return e.channel&&n.push(e.channel),t&&n.push(`(${t})`),e.reason?`${n.join(" ")}: ${e.reason}`:n.join(" ")}function ql(e){let t=Object.fromEntries(Object.entries(e).filter(([n])=>!n.startsWith("phx_ref")));return JSON.stringify(t)}function Gl(e){let t=e.inspect;if(c(v(`Realtime for ${e.ref}`)),c(`${t.connections} connection${t.connections===1?"":"s"}, ${t.connected_users} on a channel. The project's limit is ${t.limits.max_events_per_second} messages a second, counted once per message sent, however many receive it.`),t.channels.length===0){c("No channels are open.");return}for(let n of t.channels){let r=n.messages;if(c(""),c(v(`${n.name}${n.private?" (private)":""}`)+` ${n.clients.length} client${n.clients.length===1?"":"s"}`),c(b(` last ${r.window_seconds}s: ${r.broadcasts_received} broadcast${r.broadcasts_received===1?"":"s"} sent, ${r.broadcasts_delivered} delivered, busiest second ${r.peak_broadcasts_per_second}; ${r.presence_diffs_delivered} presence update${r.presence_diffs_delivered===1?"":"s"} delivered`)),n.clients.length>0){let s=n.clients.map(a=>[String(a.socket),a.presence_key,js(Date.now()-a.joined_at),js(a.last_seen_ms_ago)+(a.last_seen_ms_ago!==null&&a.last_seen_ms_ago>Ul?" (silent: likely gone)":"")]);c(U([["SOCKET","PRESENCE KEY","JOINED","LAST HEARD"],...s]).split(`
353
+ `).map(a=>` ${a}`).join(`
354
+ `))}let o=Object.keys(n.presence);if(o.length>0){c(" presence:");for(let s of o)for(let a of n.presence[s].metas)c(` ${s} ${ql(a)}`)}}}async function Ls(e,t={}){if(t.watch&&lo())throw new y("--watch is for a person; with --json, poll `realtime inspect --json` or `realtime logs --since 1m --json`");let n=await Zn(e,{channel:t.channel});if(g(n.inspect,()=>Gl(n)),!t.watch)return 0;c(""),c(b("Watching. New events appear below as they happen; Ctrl+C to stop."));let r=n.at+1,o=t.intervalMs??2e3;for(;;){await new Promise(a=>setTimeout(a,o));let s=await Zn(e,{channel:t.channel,since:r});for(let a of s.events)process.stdout.write(`${Cs(a)}
355
+ `);if(s.events.length>0){r=Math.max(...s.events.map(u=>u.at))+1;let a=s.inspect.channels.map(u=>`${u.name}: ${Object.keys(u.presence).join(", ")||"nobody tracked"}`).join("; ");process.stdout.write(b(` now ${a||"no channels open"}
356
+ `))}}}async function Us(e,t={}){let n=t.since?Fl(t.since):0,r=await Zn(e,{channel:t.channel,since:n});return g({ref:r.ref,events:r.events,capacity:r.logCapacity},()=>{if(r.events.length===0){c(t.since||t.channel?"Nothing in the log for that.":"Nothing in the log yet. It fills as clients connect, and starts again when the project's host restarts.");return}for(let o of r.events)process.stdout.write(`${Cs(o)}
357
+ `);r.logCapacity!==null&&r.events.length>=r.logCapacity&&c(b(`The log keeps the newest ${r.logCapacity} events; older ones have gone.`))}),0}K();re();q();import{readFile as Wl}from"node:fs/promises";var Jl="api.snoutdata.com";function Hs(e,t){return{ref:e,enabled:t.enabled===!0,siteUrl:t.settings?.site_url??null,redirects:t.settings?.uri_allow_list??[],google:{enabled:t.google?.enabled===!0,clientId:t.google?.clientId??null,clientSecretSet:t.google?.clientSecretSet===!0,ready:t.google?.ready===!0},googleCallback:`https://${e}.${Jl}/auth/v1/callback`,anonymous:{enabled:t.anonymous?.enabled===!0,ready:t.anonymous?.ready===!0}}}async function zl(e){return Hs(e,await x("cloud-project-auth",{ref:e,action:"status"}))}async function Yt(e,t){return Hs(e,await x("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function Qn(e){let t=e.google,n="off";t.ready?n=v("on"):t.enabled&&(n=`switched on, not offered yet: needs ${[!t.clientId&&"a client id",!t.clientSecretSet&&"a client secret"].filter(Boolean).join(" and ")}`),process.stdout.write(`${U([["auth",e.enabled?v("on"):"off"],["site url",e.siteUrl??b("not set")],["redirects",e.redirects.length?e.redirects.join(", "):b("none")],["google",n],["google client id",t.clientId??b("not set")],["google callback",e.googleCallback],["guests",e.anonymous.ready?v("on"):e.anonymous.enabled?"switched on, not applied yet":"off"]])}
358
+ `),e.enabled||c(b(` snoutdata products enable auth --ref ${e.ref}`))}async function Vl(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\r?\n$/,"")}async function qs(e,t,n,r){if(t===void 0||t==="show"){let o=await zl(e);g(o,()=>Qn(o));return}if(t==="google"){if(n[0]==="off"){let a=await Yt(e,{googleEnabled:!1});g(a,()=>c("Google sign-in is off. Your auth service restarts within about a minute."));return}if(n.length>0)throw new y('auth google takes --client-id and --stdin, or "off"');let o={googleEnabled:!0};if(r.clientId&&(o.googleClientId=r.clientId),r.stdin){let a=await Vl();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await Yt(e,o);g(s,()=>{s.google.ready?c("Google sign-in is on. Your auth service restarts within about a minute."):Qn(s),c(b(` Add ${s.googleCallback} to your Google client's Authorized redirect URIs.`))});return}if(t==="anonymous"||t==="guests"){let o=n[0];if(o!=="on"&&o!=="off")throw new y('auth anonymous takes "on" or "off"');let s=await Yt(e,{anonymousEnabled:o==="on"});g(s,()=>{o==="off"?c("Guest sign-in is off. Guests who already have a session keep it until it ends. Your auth service restarts within about a minute."):s.enabled?c("Guest sign-in is on: signInAnonymously() gives a browser a session with no email or password. Your auth service restarts within about a minute."):(c("Guest sign-in is switched on, and takes effect when auth is."),c(b(` snoutdata products enable auth --ref ${s.ref}`)))});return}if(t==="redirects"){let o={};if(r.siteUrl!==void 0&&(o.siteUrl=r.siteUrl||null),r.allow!==void 0&&(o.uriAllowList=r.allow.split(",").map(a=>a.trim()).filter(Boolean)),Object.keys(o).length===0)throw new y("auth redirects needs --site-url and/or --allow URL,URL (an empty --allow= clears the list)");let s=await Yt(e,o);g(s,()=>Qn(s));return}if(t==="templates"){let o=await x("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>Bs(o.templates));return}if(t==="template"){let o=n[0];if(!o||!Fs.includes(o))throw new y(`auth template needs a kind: ${Fs.join(", ")}`);let s;if(n[1]==="reset")s={kind:o,reset:!0};else{if(!r.file)throw new y(`auth template ${o} needs --file body.html (and --subject), or "reset"`);let u=await Wl(r.file,"utf8"),i=await x("cloud-project-auth",{ref:e,action:"templates"}),d=r.subject??i.templates.find(l=>l.kind===o)?.subject;s={kind:o,subject:d,body:u}}let a=await x("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{Bs(a.templates),c(b(" Your auth service restarts with it within about a minute."))});return}throw new y(`unknown command: auth ${t}. auth [google [off] | anonymous on|off | redirects | templates | template KIND]`)}var Fs=["confirmation","recovery","magic_link","invite","email_change"];function Bs(e){process.stdout.write(`${U([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?v("yours"):"ours",t.subject])])}
359
+ `)}K();q();X();import{spawn as Xl}from"node:child_process";import{createHash as Zl}from"node:crypto";import{existsSync as Ql,readdirSync as eu,readFileSync as tu,statSync as nu}from"node:fs";import{isAbsolute as ru,join as er,relative as ou,resolve as su}from"node:path";function Yl(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function Xt(e,t,n={}){let r=Yl(e),o=new Map(r.map(d=>[d.name,d])),s=[];for(let d of t){let l=o.get(d.name);if(!l){s.push({code:"missing",name:d.name,reason:`${d.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}l.checksum!==d.checksum&&s.push({code:"changed",name:d.name,reason:`${d.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(d=>d.name).sort((d,l)=>d<l?-1:d>l?1:0)[t.length-1],u=new Set(t.map(d=>d.name)),i=[];for(let d of r)if(!u.has(d.name)){if(a!==void 0&&d.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:d.name,reason:`${d.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}i.push(d)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:i}}function it(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function Ks(e){let t=ou(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var ct="_snoutdata_migrations",au=`create table if not exists ${ct} (
355
360
  name text primary key,
356
361
  checksum text not null,
357
362
  applied_at timestamptz not null default now()
358
- );`;function gl(e){if(!il(e)||!ul(e).isDirectory())throw new Error(`no migrations folder at ${hs(e)}. Put your .sql files there, or pass --dir.`);return cl(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=Mn(e,t),r=ll(n,"utf8");return{name:t,path:n,sql:r,checksum:al("sha256").update(r,"utf8").digest("hex")}})}function In(e,t,n=[]){return new Promise(r=>{let o=sl("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1",...n],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),s="",a="";o.stdout.on("data",u=>s+=String(u)),o.stderr.on("data",u=>a+=String(u)),o.on("error",u=>{r({code:127,out:"",err:u.code==="ENOENT"?"psql is not installed, and db push needs it to run your migrations. `snoutdata db url` prints a connection string for any client.":String(u)})}),o.on("close",u=>r({code:u??0,out:s,err:a})),o.stdin.end(t)})}async function hl(e){let t=await In(e,pl);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${rt}`);let n=await In(e,`select name || E'\\t' || checksum from ${rt};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${rt}`);return n.out.split(`
359
- `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let o=r.indexOf(" ");return{name:r.slice(0,o),checksum:r.slice(o+1)}})}function gs(e){return`'${e.replace(/'/g,"''")}'`}async function ys(e,t={}){let n=await Cn(e,t,r=>c(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
360
- `);A("conflict",`Refused. This folder and ${e} do not agree:
361
- ${r}`,{ref:e,problems:n.problems})}A("failed",`${n.failed} failed, and nothing after it was tried.
362
- ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(n,()=>{if(n.wouldApply){c(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)c(` ${r}`);return}if(n.upToDate){c(`${e} is up to date.`);return}c(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function Cn(e,t={},n=()=>{}){let r=fl(t.dir===void 0?Mn(process.cwd(),"migrations"):dl(t.dir)?t.dir:Mn(process.cwd(),t.dir)),o=[];function s(p){o.push(p),n(p)}let a=gl(r);if(a.length===0)return s(`No .sql files in ${hs(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let u=await V(e);u.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let i=await hl(u),m=Lt(a,i,{outOfOrder:t.outOfOrder});if(!m.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...m.problems],notes:o};let l=new Map(a.map(p=>[p.name,p])),f=m.pending.map(p=>l.get(p.name));if(f.length===0)return s(`${e} is up to date: ${i.length} migration${i.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let p of f)nt(p.sql)||s(`${p.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:f.map(p=>p.name),notes:o}}let d=[];for(let p of f){let w=`insert into ${rt} (name, checksum) values (${gs(p.name)}, ${gs(p.checksum)});`,_=nt(p.sql);_||s(`${p.name} runs outside a transaction, so an interruption can leave it half applied.`);let h=_?`begin;
363
- ${p.sql}
364
- ${w}
363
+ );`;function iu(e){if(!Ql(e)||!nu(e).isDirectory())throw new Error(`no migrations folder at ${Ks(e)}. Put your .sql files there, or pass --dir.`);return eu(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=er(e,t),r=tu(n,"utf8");return{name:t,path:n,sql:r,checksum:Zl("sha256").update(r,"utf8").digest("hex")}})}function tr(e,t,n=[]){return new Promise(r=>{let o=Xl("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1",...n],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.ssl==="disable"?"disable":"require"}}),s="",a="";o.stdout.on("data",u=>s+=String(u)),o.stderr.on("data",u=>a+=String(u)),o.on("error",u=>{r({code:127,out:"",err:u.code==="ENOENT"?"psql is not installed, and db push needs it to run your migrations. `snoutdata db url` prints a connection string for any client.":String(u)})}),o.on("close",u=>r({code:u??0,out:s,err:a})),o.stdin.end(t)})}async function cu(e){let t=await tr(e,au);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${ct}`);let n=await tr(e,`select name || E'\\t' || checksum from ${ct};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${ct}`);return n.out.split(`
364
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let o=r.indexOf(" ");return{name:r.slice(0,o),checksum:r.slice(o+1)}})}function Gs(e){return`'${e.replace(/'/g,"''")}'`}async function Ws(e,t={}){let n=await nr(e,t,r=>c(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
365
+ `);O("conflict",`Refused. This folder and ${e} do not agree:
366
+ ${r}`,{ref:e,problems:n.problems})}O("failed",`${n.failed} failed, and nothing after it was tried.
367
+ ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(n,()=>{if(n.wouldApply){c(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)c(` ${r}`);return}if(n.upToDate){c(`${e} is up to date.`);return}c(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function nr(e,t={},n=()=>{}){let r=su(t.dir===void 0?er(process.cwd(),"migrations"):ru(t.dir)?t.dir:er(process.cwd(),t.dir)),o=[];function s(m){o.push(m),n(m)}let a=iu(r);if(a.length===0)return s(`No .sql files in ${Ks(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let u=await Z(e);u.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let i=await cu(u),d=Xt(a,i,{outOfOrder:t.outOfOrder});if(!d.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...d.problems],notes:o};let l=new Map(a.map(m=>[m.name,m])),f=d.pending.map(m=>l.get(m.name));if(f.length===0)return s(`${e} is up to date: ${i.length} migration${i.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let m of f)it(m.sql)||s(`${m.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:f.map(m=>m.name),notes:o}}let h=[];for(let m of f){let p=`insert into ${ct} (name, checksum) values (${Gs(m.name)}, ${Gs(m.checksum)});`,S=it(m.sql);S||s(`${m.name} runs outside a transaction, so an interruption can leave it half applied.`);let w=S?`begin;
368
+ ${m.sql}
369
+ ${p}
365
370
  commit;
366
- `:`${p.sql}
367
- ${w}
368
- `,k=await In(u,h);if(k.code!==0)return _&&s("It ran inside a transaction, so nothing from it is in the database."),{ref:e,directory:r,ok:!1,applied:d,failed:p.name,error:k.err.trim(),notes:o};d.push(p.name),s(` applied ${p.name}`)}return{ref:e,directory:r,ok:!0,applied:d,notes:o}}H();ne();q();import{readFile as ws}from"node:fs/promises";function jn(e){if(e==="apns"||e==="fcm")return e;throw new y("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function yl(e){let t=await S("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function bs(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new y("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new y("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await ws(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function Ss(e){if(!e)throw new y("push credentials set fcm needs --file service-account.json");return{service_account:await ws(e,"utf8")}}async function wl(e,t,n){let r=await S("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function bl(e,t){return await S("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function xs(e){let t=await yl(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${U([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
369
- `),(!n||!r)&&c(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function _s(e,t,n){let r=await wl(e,t,n);g(r,()=>c(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function ks(e,t){let n=await bl(e,t);g(n,()=>c(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}H();Y();q();import{spawn as Dl}from"node:child_process";import{writeFileSync as Ml}from"node:fs";import{isAbsolute as Il,resolve as Cl}from"node:path";var xl=" ";function v(e){return xl.repeat(e)}function W(e){return JSON.stringify(e)}function se(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:W(e)}function Pe(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function _l(e){let t=(e??"").trim(),n=!1;for(;t.endsWith("[]");)n=!0,t=t.slice(0,-2).trim();t.startsWith("_")&&t.length>1&&(n=!0,t=t.slice(1));let r=t.indexOf("(");return r>0&&(t=t.slice(0,r)),{base:t.trim().toLowerCase(),array:n}}var kl={smallint:"number",int2:"number",integer:"number",int:"number",int4:"number",bigint:"number",int8:"number",decimal:"number",numeric:"number",number:"number",real:"number",float:"number",float4:"number",float8:"number","double precision":"number",double:"number",binary_float:"number",binary_double:"number",smallserial:"number",serial:"number",bigserial:"number",oid:"number",smallmoney:"number",money:"number",bool:"boolean",boolean:"boolean",json:"json",jsonb:"json",variant:"json",object:"json",char:"string",character:"string","character varying":"string",varchar:"string",varchar2:"string",nvarchar:"string",nvarchar2:"string",nchar:"string",text:"string",ntext:"string",citext:"string",clob:"string",nclob:"string",longtext:"string",mediumtext:"string",tinytext:"string",uuid:"string",uniqueidentifier:"string",date:"string",time:"string","time without time zone":"string","time with time zone":"string",timetz:"string",timestamp:"string","timestamp without time zone":"string","timestamp with time zone":"string",timestamptz:"string",datetime:"string",datetime2:"string",datetimeoffset:"string",smalldatetime:"string",timestamp_ntz:"string",timestamp_ltz:"string",timestamp_tz:"string",interval:"string",year:"string",bytea:"string",blob:"string",longblob:"string",mediumblob:"string",tinyblob:"string",binary:"string",varbinary:"string",raw:"string",xml:"string",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",tsvector:"string",tsquery:"string",name:"string",bit:"string",varbit:"string",tinyint:"number",enum:"string",set:"string"},vl={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function El(e,t){let n=vl[e]?.[t];return n||(kl[t]??"unknown")}function Ut(e,t){let{base:n,array:r}=_l(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=El(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function Pl(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function Ln(e,t,n,r,o=!1){if(o&&t.generatedAlways===!0)return`${v(n)}${se(t.name)}?: never`;let s=Ut(e,t.dataType),a=t.nullable?`${s} | null`:s;return`${v(n)}${se(t.name)}${r?"?":""}: ${a}`}function $l(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,u)=>a===n[u])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function Tl(e,t){let n=[],r=[...e.foreignKeys].sort((o,s)=>o.constraintName<s.constraintName?-1:o.constraintName>s.constraintName?1:0);if(r.length===0)return n.push(`${v(t)}Relationships: []`),n;n.push(`${v(t)}Relationships: [`);for(let o of r)n.push(`${v(t+1)}{`),n.push(`${v(t+2)}foreignKeyName: ${W(o.constraintName)}`),n.push(`${v(t+2)}columns: [${o.columns.map(W).join(", ")}]`),n.push(`${v(t+2)}isOneToOne: ${$l(e,o)?"true":"false"}`),n.push(`${v(t+2)}referencedRelation: ${W(o.referencedTable)}`),n.push(`${v(t+2)}referencedColumns: [${o.referencedColumns.map(W).join(", ")}]`),n.push(`${v(t+1)}},`);return n.push(`${v(t)}]`),n}function vs(e,t,n){let r=Pe(t.columns),o=[];o.push(`${v(n)}${se(t.name)}: {`),o.push(`${v(n+1)}Row: {`);for(let s of r)o.push(Ln(e,s,n+2,!1));if(o.push(`${v(n+1)}}`),t.kind!=="view"){o.push(`${v(n+1)}Insert: {`);for(let s of r)o.push(Ln(e,s,n+2,Pl(s),!0));o.push(`${v(n+1)}}`),o.push(`${v(n+1)}Update: {`);for(let s of r)o.push(Ln(e,s,n+2,!0,!0));o.push(`${v(n+1)}}`)}return o.push(...Tl(t,n+1)),o.push(`${v(n)}}`),o}function ot(e,t){return[`${v(t)}${e}: {`,`${v(t+1)}[_ in never]: never`,`${v(t)}}`]}function Al(e,t){if(t.kind==="procedure"||!t.returnType)return"undefined";let n=t.returnType.trim(),r=n.toLowerCase();if(r==="void"||r==="")return"undefined";if(r.startsWith("table("))return"Record<string, unknown>[]";if(r.startsWith("setof ")){let s=n.slice(6).trim();if(s.toLowerCase()==="record")return"Record<string, unknown>[]";let a=e.rowRefs.get(s.toLowerCase());return a?`${a}[]`:`${Ut(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??Ut(e,n)}function Rl(e,t,n){let r=[],o=Pe((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${v(n)}${se(t.name)}: {`),o.length===0)r.push(`${v(n+1)}Args: never`);else{r.push(`${v(n+1)}Args: {`);for(let s of o)r.push(`${v(n+2)}${se(s.name)}: ${Ut(e,s.dataType)}`);r.push(`${v(n+1)}}`)}return r.push(`${v(n+1)}Returns: ${Al(e,t)}`),r.push(`${v(n)}}`),r}function Le(e,t){return e.schema&&e.schema.length>0?e.schema:t}function Es(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let f of o){let d=f.schema&&f.schema.length>0?f.schema:n;s.set(f.name.toLowerCase(),`Database[${W(d)}][${W("Enums")}][${W(f.name)}]`)}let a=new Map;for(let f of e.tables){let d=Le(f,n),p=f.kind==="view"?"Views":"Tables";a.set(f.name.toLowerCase(),`Database[${W(d)}][${W(p)}][${W(f.name)}][${W("Row")}]`)}let u={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},i=new Set([n]);for(let f of e.tables)i.add(Le(f,n));for(let f of e.routines??[])i.add(Le(f,n));for(let f of o)i.add(f.schema&&f.schema.length>0?f.schema:n);let m=[...i].sort(),l=[];l.push("export type Json ="),l.push(" | string"),l.push(" | number"),l.push(" | boolean"),l.push(" | null"),l.push(" | { [key: string]: Json | undefined }"),l.push(" | Json[]"),l.push(""),l.push("export type Database = {");for(let f of m){let d=Pe(e.tables.filter(h=>Le(h,n)===f&&h.kind!=="view")),p=Pe(e.tables.filter(h=>Le(h,n)===f&&h.kind==="view")),w=Pe((e.routines??[]).filter(h=>Le(h,n)===f)),_=Pe(o.filter(h=>(h.schema&&h.schema.length>0?h.schema:n)===f));if(l.push(`${v(1)}${se(f)}: {`),d.length===0)l.push(...ot("Tables",2));else{l.push(`${v(2)}Tables: {`);for(let h of d)l.push(...vs(u,h,3));l.push(`${v(2)}}`)}if(p.length===0)l.push(...ot("Views",2));else{l.push(`${v(2)}Views: {`);for(let h of p)l.push(...vs(u,h,3));l.push(`${v(2)}}`)}if(w.length===0)l.push(...ot("Functions",2));else{l.push(`${v(2)}Functions: {`);for(let h of w)l.push(...Rl(u,h,3));l.push(`${v(2)}}`)}if(_.length===0)l.push(...ot("Enums",2));else{l.push(`${v(2)}Enums: {`);for(let h of _)l.push(`${v(3)}${se(h.name)}: ${h.values.map(W).join(" | ")}`);l.push(`${v(2)}}`)}l.push(...ot("CompositeTypes",2)),l.push(`${v(1)}}`)}return l.push("}"),t.helpers!==!1&&(l.push(""),l.push(Nl(n))),t.constants!==!1&&(l.push(""),l.push(Ol(m,o,n))),`${l.join(`
371
+ `:`${m.sql}
372
+ ${p}
373
+ `,_=await tr(u,w);if(_.code!==0)return S&&s("It ran inside a transaction, so nothing from it is in the database."),{ref:e,directory:r,ok:!1,applied:h,failed:m.name,error:_.err.trim(),notes:o};h.push(m.name),s(` applied ${m.name}`)}return{ref:e,directory:r,ok:!0,applied:h,notes:o}}K();re();q();import{readFile as Js}from"node:fs/promises";function rr(e){if(e==="apns"||e==="fcm")return e;throw new y("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function lu(e){let t=await x("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function zs(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new y("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new y("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await Js(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function Vs(e){if(!e)throw new y("push credentials set fcm needs --file service-account.json");return{service_account:await Js(e,"utf8")}}async function uu(e,t,n){let r=await x("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function du(e,t){return await x("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function Ys(e){let t=await lu(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${U([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
374
+ `),(!n||!r)&&c(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function Xs(e,t,n){let r=await uu(e,t,n);g(r,()=>c(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function Zs(e,t){let n=await du(e,t);g(n,()=>c(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}K();X();q();import{spawn as Eu}from"node:child_process";import{writeFileSync as $u}from"node:fs";import{isAbsolute as Pu,resolve as Ru}from"node:path";var fu=" ";function E(e){return fu.repeat(e)}function Y(e){return JSON.stringify(e)}function ce(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:Y(e)}function Ae(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function pu(e){let t=(e??"").trim(),n=!1;for(;t.endsWith("[]");)n=!0,t=t.slice(0,-2).trim();t.startsWith("_")&&t.length>1&&(n=!0,t=t.slice(1));let r=t.indexOf("(");return r>0&&(t=t.slice(0,r)),{base:t.trim().toLowerCase(),array:n}}var gu={smallint:"number",int2:"number",integer:"number",int:"number",int4:"number",bigint:"number",int8:"number",decimal:"number",numeric:"number",number:"number",real:"number",float:"number",float4:"number",float8:"number","double precision":"number",double:"number",binary_float:"number",binary_double:"number",smallserial:"number",serial:"number",bigserial:"number",oid:"number",smallmoney:"number",money:"number",bool:"boolean",boolean:"boolean",json:"json",jsonb:"json",variant:"json",object:"json",char:"string",character:"string","character varying":"string",varchar:"string",varchar2:"string",nvarchar:"string",nvarchar2:"string",nchar:"string",text:"string",ntext:"string",citext:"string",clob:"string",nclob:"string",longtext:"string",mediumtext:"string",tinytext:"string",uuid:"string",uniqueidentifier:"string",date:"string",time:"string","time without time zone":"string","time with time zone":"string",timetz:"string",timestamp:"string","timestamp without time zone":"string","timestamp with time zone":"string",timestamptz:"string",datetime:"string",datetime2:"string",datetimeoffset:"string",smalldatetime:"string",timestamp_ntz:"string",timestamp_ltz:"string",timestamp_tz:"string",interval:"string",year:"string",bytea:"string",blob:"string",longblob:"string",mediumblob:"string",tinyblob:"string",binary:"string",varbinary:"string",raw:"string",xml:"string",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",tsvector:"string",tsquery:"string",name:"string",bit:"string",varbit:"string",tinyint:"number",enum:"string",set:"string"},hu={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function yu(e,t){let n=hu[e]?.[t];return n||(gu[t]??"unknown")}function Zt(e,t){let{base:n,array:r}=pu(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=yu(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function wu(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function or(e,t,n,r,o=!1){if(o&&t.generatedAlways===!0)return`${E(n)}${ce(t.name)}?: never`;let s=Zt(e,t.dataType),a=t.nullable?`${s} | null`:s;return`${E(n)}${ce(t.name)}${r?"?":""}: ${a}`}function bu(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,u)=>a===n[u])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function Su(e,t){let n=[],r=[...e.foreignKeys].sort((o,s)=>o.constraintName<s.constraintName?-1:o.constraintName>s.constraintName?1:0);if(r.length===0)return n.push(`${E(t)}Relationships: []`),n;n.push(`${E(t)}Relationships: [`);for(let o of r)n.push(`${E(t+1)}{`),n.push(`${E(t+2)}foreignKeyName: ${Y(o.constraintName)}`),n.push(`${E(t+2)}columns: [${o.columns.map(Y).join(", ")}]`),n.push(`${E(t+2)}isOneToOne: ${bu(e,o)?"true":"false"}`),n.push(`${E(t+2)}referencedRelation: ${Y(o.referencedTable)}`),n.push(`${E(t+2)}referencedColumns: [${o.referencedColumns.map(Y).join(", ")}]`),n.push(`${E(t+1)}},`);return n.push(`${E(t)}]`),n}function Qs(e,t,n){let r=Ae(t.columns),o=[];o.push(`${E(n)}${ce(t.name)}: {`),o.push(`${E(n+1)}Row: {`);for(let s of r)o.push(or(e,s,n+2,!1));if(o.push(`${E(n+1)}}`),t.kind!=="view"){o.push(`${E(n+1)}Insert: {`);for(let s of r)o.push(or(e,s,n+2,wu(s),!0));o.push(`${E(n+1)}}`),o.push(`${E(n+1)}Update: {`);for(let s of r)o.push(or(e,s,n+2,!0,!0));o.push(`${E(n+1)}}`)}return o.push(...Su(t,n+1)),o.push(`${E(n)}}`),o}function lt(e,t){return[`${E(t)}${e}: {`,`${E(t+1)}[_ in never]: never`,`${E(t)}}`]}function xu(e,t){if(t.kind==="procedure"||!t.returnType)return"undefined";let n=t.returnType.trim(),r=n.toLowerCase();if(r==="void"||r==="")return"undefined";if(r.startsWith("table("))return"Record<string, unknown>[]";if(r.startsWith("setof ")){let s=n.slice(6).trim();if(s.toLowerCase()==="record")return"Record<string, unknown>[]";let a=e.rowRefs.get(s.toLowerCase());return a?`${a}[]`:`${Zt(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??Zt(e,n)}function _u(e,t,n){let r=[],o=Ae((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${E(n)}${ce(t.name)}: {`),o.length===0)r.push(`${E(n+1)}Args: never`);else{r.push(`${E(n+1)}Args: {`);for(let s of o)r.push(`${E(n+2)}${ce(s.name)}: ${Zt(e,s.dataType)}`);r.push(`${E(n+1)}}`)}return r.push(`${E(n+1)}Returns: ${xu(e,t)}`),r.push(`${E(n)}}`),r}function Fe(e,t){return e.schema&&e.schema.length>0?e.schema:t}function ea(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let f of o){let h=f.schema&&f.schema.length>0?f.schema:n;s.set(f.name.toLowerCase(),`Database[${Y(h)}][${Y("Enums")}][${Y(f.name)}]`)}let a=new Map;for(let f of e.tables){let h=Fe(f,n),m=f.kind==="view"?"Views":"Tables";a.set(f.name.toLowerCase(),`Database[${Y(h)}][${Y(m)}][${Y(f.name)}][${Y("Row")}]`)}let u={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},i=new Set([n]);for(let f of e.tables)i.add(Fe(f,n));for(let f of e.routines??[])i.add(Fe(f,n));for(let f of o)i.add(f.schema&&f.schema.length>0?f.schema:n);let d=[...i].sort(),l=[];l.push("export type Json ="),l.push(" | string"),l.push(" | number"),l.push(" | boolean"),l.push(" | null"),l.push(" | { [key: string]: Json | undefined }"),l.push(" | Json[]"),l.push(""),l.push("export type Database = {");for(let f of d){let h=Ae(e.tables.filter(w=>Fe(w,n)===f&&w.kind!=="view")),m=Ae(e.tables.filter(w=>Fe(w,n)===f&&w.kind==="view")),p=Ae((e.routines??[]).filter(w=>Fe(w,n)===f)),S=Ae(o.filter(w=>(w.schema&&w.schema.length>0?w.schema:n)===f));if(l.push(`${E(1)}${ce(f)}: {`),h.length===0)l.push(...lt("Tables",2));else{l.push(`${E(2)}Tables: {`);for(let w of h)l.push(...Qs(u,w,3));l.push(`${E(2)}}`)}if(m.length===0)l.push(...lt("Views",2));else{l.push(`${E(2)}Views: {`);for(let w of m)l.push(...Qs(u,w,3));l.push(`${E(2)}}`)}if(p.length===0)l.push(...lt("Functions",2));else{l.push(`${E(2)}Functions: {`);for(let w of p)l.push(..._u(u,w,3));l.push(`${E(2)}}`)}if(S.length===0)l.push(...lt("Enums",2));else{l.push(`${E(2)}Enums: {`);for(let w of S)l.push(`${E(3)}${ce(w.name)}: ${w.values.map(Y).join(" | ")}`);l.push(`${E(2)}}`)}l.push(...lt("CompositeTypes",2)),l.push(`${E(1)}}`)}return l.push("}"),t.helpers!==!1&&(l.push(""),l.push(vu(n))),t.constants!==!1&&(l.push(""),l.push(ku(d,o,n))),`${l.join(`
370
375
  `)}
371
- `}function Ol(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=Pe(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${v(1)}${se(o)}: {`),r.push(`${v(2)}Enums: {},`),r.push(`${v(1)}},`);continue}r.push(`${v(1)}${se(o)}: {`),r.push(`${v(2)}Enums: {`);for(let a of s)r.push(`${v(3)}${se(a.name)}: [${a.values.map(W).join(", ")}],`);r.push(`${v(2)}},`),r.push(`${v(1)}},`)}return r.push("} as const"),r.join(`
372
- `)}function Nl(e){return`type DatabaseWithoutInternals = Database
376
+ `}function ku(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=Ae(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${E(1)}${ce(o)}: {`),r.push(`${E(2)}Enums: {},`),r.push(`${E(1)}},`);continue}r.push(`${E(1)}${ce(o)}: {`),r.push(`${E(2)}Enums: {`);for(let a of s)r.push(`${E(3)}${ce(a.name)}: [${a.values.map(Y).join(", ")}],`);r.push(`${E(2)}},`),r.push(`${E(1)}},`)}return r.push("} as const"),r.join(`
377
+ `)}function vu(e){return`type DatabaseWithoutInternals = Database
373
378
 
374
- type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${W(e)}>]
379
+ type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${Y(e)}>]
375
380
 
376
381
  export type Tables<
377
382
  DefaultSchemaTableNameOrOptions extends
@@ -484,7 +489,7 @@ export type CompositeTypes<
484
489
  ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
485
490
  : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
486
491
  ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
487
- : never`}function jl(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:e.ssl==="disable"?"disable":"require",label:e.ref}}function Ft(e){let t;try{t=new URL(e)}catch{throw new Error(`--db-url is not a URL: ${e}`)}if(t.protocol!=="postgres:"&&t.protocol!=="postgresql:")throw new Error(`--db-url must be a postgres:// URL, not ${t.protocol}//`);let n=decodeURIComponent(t.pathname.replace(/^\//,""));if(!n)throw new Error("--db-url has no database name in its path");let r=t.searchParams.get("sslmode")??"prefer";return{host:t.hostname||"127.0.0.1",port:t.port?Number(t.port):5432,database:n,user:decodeURIComponent(t.username)||"postgres",password:decodeURIComponent(t.password),sslMode:r,label:`${t.hostname||"127.0.0.1"}/${n}`}}function Un(e,t){return new Promise(n=>{let r=Dl("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA"],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.sslMode}}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{n({code:127,out:"",err:a.code==="ENOENT"?"psql is not installed, and gen types needs it to read the schema. `snoutdata db url` prints a connection string for any client.":String(a)})}),r.on("close",a=>n({code:a??0,out:o,err:s})),r.stdin.end(t)})}var Ll=`
492
+ : never`}function Tu(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:e.ssl==="disable"?"disable":"require",label:e.ref}}function Qt(e){let t;try{t=new URL(e)}catch{throw new Error(`--db-url is not a URL: ${e}`)}if(t.protocol!=="postgres:"&&t.protocol!=="postgresql:")throw new Error(`--db-url must be a postgres:// URL, not ${t.protocol}//`);let n=decodeURIComponent(t.pathname.replace(/^\//,""));if(!n)throw new Error("--db-url has no database name in its path");let r=t.searchParams.get("sslmode")??"prefer";return{host:t.hostname||"127.0.0.1",port:t.port?Number(t.port):5432,database:n,user:decodeURIComponent(t.username)||"postgres",password:decodeURIComponent(t.password),sslMode:r,label:`${t.hostname||"127.0.0.1"}/${n}`}}function sr(e,t){return new Promise(n=>{let r=Eu("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA"],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.sslMode}}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{n({code:127,out:"",err:a.code==="ENOENT"?"psql is not installed, and gen types needs it to read the schema. `snoutdata db url` prints a connection string for any client.":String(a)})}),r.on("close",a=>n({code:a??0,out:o,err:s})),r.stdin.end(t)})}var Au=`
488
493
  with wanted as (select unnest($SCHEMAS$::text[]) as nspname)
489
494
  select json_build_object(
490
495
  'tables', coalesce((
@@ -588,30 +593,36 @@ select json_build_object(
588
593
  where t.typtype = 'e' and n.nspname in (select nspname from wanted)
589
594
  ), '[]'::json)
590
595
  )::text
591
- `;function Ul(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Fl(e,t,n=0){let r=e.tables.map(a=>{let u=a.primaryKey??[],i=new Set(u),m=a.columns.map(l=>({name:l.name,dataType:l.dataType,nullable:l.nullable,isPrimaryKey:i.has(l.name),default:l.default,autoIncrement:l.autoIncrement,...l.generatedAlways===!0?{generatedAlways:!0}:{}}));return{name:a.name,schema:a.schema,kind:a.kind,columns:m,primaryKey:u,foreignKeys:(a.foreignKeys??[]).map(l=>({constraintName:l.constraintName,columns:l.columns??[],referencedTable:l.referencedTable,referencedColumns:l.referencedColumns??[]})),indexes:(a.indexes??[]).filter(l=>Array.isArray(l.columns)&&l.columns.every(f=>typeof f=="string")).map(l=>({name:l.name,unique:l.unique,columns:l.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(u=>({name:u.name,dataType:u.dataType,mode:u.mode}))})),s=e.enums.map(a=>({name:a.name,schema:a.schema,values:a.values??[]}));return{schema:{database:t,tables:r,routines:o,fetchedAt:n},enums:s}}async function Bl(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?Ft(e.dbUrl):jl(await V(ql(e))),r=Ll.replace("$SCHEMAS$",Ul(t)),o=await(e.sql?e.sql(r):Un(n,r));o.code===127&&!e.sql&&!e.dbUrl&&e.ref&&(o=await Hl(e.ref,r)),o.code===127&&A("tool-missing",o.err.trim()),o.code!==0&&A("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||A("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{A("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:u,enums:i}=Fl(a,n.database,Date.now()),m=Es(u,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:i}),l=null;return e.out&&(l=Il(e.out)?e.out:Cl(process.cwd(),e.out),Ml(l,m,"utf8")),{target:n.label,schemas:t,tables:u.tables.filter(f=>f.kind!=="view").length,views:u.tables.filter(f=>f.kind==="view").length,functions:(u.routines??[]).length,enums:i.length,path:l,types:m}}async function Hl(e,t,n=S){try{let r=await n("cloud-project-sql",{ref:e,sql:t});if(r.error)return{code:1,out:"",err:r.error};let o=Object.values(r.rows?.[0]??{})[0];return{code:0,out:typeof o=="string"?o:o==null?"":JSON.stringify(o),err:""}}catch(r){return{code:1,out:"",err:r instanceof Error?r.message:String(r)}}}function ql(e){if(!e.ref)throw new Error("no project: pass --ref, --db-url, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return e.ref}async function Ps(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&A("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await Bl(t);return r.path&&c(`Wrote ${r.tables} table${r.tables===1?"":"s"}, ${r.views} view${r.views===1?"":"s"}, ${r.functions} function${r.functions===1?"":"s"} and ${r.enums} enum${r.enums===1?"":"s"} to ${r.path}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}Y();import{createHash as Zu}from"node:crypto";import{existsSync as cr,mkdirSync as Qu,readFileSync as lr,readdirSync as ed,statSync as td,writeFileSync as nd}from"node:fs";import{dirname as rd,isAbsolute as Ka,join as lt,resolve as Wa}from"node:path";import{pathToFileURL as od}from"node:url";q();function sd(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:Ka(e)||e.startsWith("./")||e.startsWith("../")?od(Wa(e)).href:e}function ad(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(sd(e)):Promise.resolve().then(()=>(qa(),Ha))}function id(e){let t=e instanceof Error?e.message:String(e),n=process.env.SNOUTDATA_LOCAL_RUNTIME;return n?`SNOUTDATA_LOCAL_RUNTIME points at ${n}, and it could not be loaded: ${t}`:`this build of the CLI has no local pod runtime in it, so \`snoutdata start\` cannot run. That is a packaging fault rather than anything you did: please report it. (${t})`}var cd="It is fetched from ghcr.io/snoutdata/snoutpod-postgres on the first `snoutdata start`, so this usually means that registry could not be reached from here.";async function ut(){let e;try{e=await ad()}catch(t){A("tool-missing",id(t))}return{ready:()=>e.podmanReady(cd),ensureImage:t=>e.ensureImage(t),prepare:()=>e.ensureNetwork(),start:(t,n)=>e.startLocal(e.localRuntime(),t,{onProgress:n}),stop:t=>e.stopLocal(e.localRuntime(),t),status:t=>e.localStatus(e.localRuntime(),t),remove:(t,n)=>e.removeLocal(e.localRuntime(),t,n),uri:t=>e.localUri(t),psql:async(t,n)=>{let r=await e.localPsql(t,n);return{code:r.code,out:r.stdout,err:r.stderr}},mint:t=>({ref:e.newLocalRef(),port:t,ownerPassword:e.newLocalPassword(),adminPassword:e.newLocalPassword()})}}function Ja(e){return lt(e,".snoutdata","local.json")}function Vt(e){let t=Ja(e);if(!cr(t))return null;try{let n=JSON.parse(lr(t,"utf8"));return!n.ref||!n.port||!n.ownerPassword||!n.adminPassword?null:{ref:n.ref,port:n.port,ownerPassword:n.ownerPassword,adminPassword:n.adminPassword}}catch{return null}}function ld(e,t){let n=Ja(e);return Qu(rd(n),{recursive:!0}),nd(n,`${JSON.stringify(t,null,2)}
592
- `,{encoding:"utf8",mode:384}),n}var ud=54322;async function za(e){let t=await e.ensureImage(r=>c(r));t&&A("tool-missing",t);let n=await e.ready();n&&A("tool-missing",n),await e.prepare()}function Va(e,t,n,r=Un){let o=!1;return async s=>{if(o)return e.psql(t,s);let a=await r(n,s);return a.code!==127?a:(o=!0,e.psql(t,s))}}async function dd(e,t={}){let n=t.cwd??process.cwd();await za(e);let r=Vt(n),o=r??e.mint(t.port??ud),s=r?null:ld(n,o),a=await e.start(o,f=>c(f)),u=e.uri(o);if(t.noMigrations)return{status:a,uri:u,applied:[],seeded:!1,created:s};let i=Va(e,o,Ft(u)),m=await md(i,n,t),l=await fd(i,n,m.length>0||!!s);return{status:a,uri:u,applied:m,seeded:l,created:s}}async function md(e,t,n){let r=Wa(n.dir===void 0?lt(t,"migrations"):Ka(n.dir)?n.dir:lt(t,n.dir));if(!cr(r)||!td(r).isDirectory())return[];let o=ed(r).filter(f=>f.endsWith(".sql")).map(f=>{let d=lr(lt(r,f),"utf8");return{name:f,sql:d,checksum:Zu("sha256").update(d,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(pd);s.code!==0&&A(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${ct}`);let a=await e(`select name || E'\\t' || checksum from ${ct};`);a.code!==0&&A("failed",a.err.trim()||`could not read ${ct}`);let u=a.out.split(`
593
- `).map(f=>f.trim()).filter(f=>f.length>0).map(f=>{let d=f.indexOf(" ");return{name:f.slice(0,d),checksum:f.slice(d+1)}}),i=Lt(o,u,{outOfOrder:n.outOfOrder});i.ok||A("conflict",`Refused. This folder and the local database do not agree:
596
+ `;function Ou(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Nu(e,t,n=0){let r=e.tables.map(a=>{let u=a.primaryKey??[],i=new Set(u),d=a.columns.map(l=>({name:l.name,dataType:l.dataType,nullable:l.nullable,isPrimaryKey:i.has(l.name),default:l.default,autoIncrement:l.autoIncrement,...l.generatedAlways===!0?{generatedAlways:!0}:{}}));return{name:a.name,schema:a.schema,kind:a.kind,columns:d,primaryKey:u,foreignKeys:(a.foreignKeys??[]).map(l=>({constraintName:l.constraintName,columns:l.columns??[],referencedTable:l.referencedTable,referencedColumns:l.referencedColumns??[]})),indexes:(a.indexes??[]).filter(l=>Array.isArray(l.columns)&&l.columns.every(f=>typeof f=="string")).map(l=>({name:l.name,unique:l.unique,columns:l.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(u=>({name:u.name,dataType:u.dataType,mode:u.mode}))})),s=e.enums.map(a=>({name:a.name,schema:a.schema,values:a.values??[]}));return{schema:{database:t,tables:r,routines:o,fetchedAt:n},enums:s}}async function Du(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?Qt(e.dbUrl):Tu(await Z(Iu(e))),r=Au.replace("$SCHEMAS$",Ou(t)),o=await(e.sql?e.sql(r):sr(n,r));o.code===127&&!e.sql&&!e.dbUrl&&e.ref&&(o=await Mu(e.ref,r)),o.code===127&&O("tool-missing",o.err.trim()),o.code!==0&&O("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||O("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{O("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:u,enums:i}=Nu(a,n.database,Date.now()),d=ea(u,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:i}),l=null;return e.out&&(l=Pu(e.out)?e.out:Ru(process.cwd(),e.out),$u(l,d,"utf8")),{target:n.label,schemas:t,tables:u.tables.filter(f=>f.kind!=="view").length,views:u.tables.filter(f=>f.kind==="view").length,functions:(u.routines??[]).length,enums:i.length,path:l,types:d}}async function Mu(e,t,n=x){try{let r=await n("cloud-project-sql",{ref:e,sql:t});if(r.error)return{code:1,out:"",err:r.error};let o=Object.values(r.rows?.[0]??{})[0];return{code:0,out:typeof o=="string"?o:o==null?"":JSON.stringify(o),err:""}}catch(r){return{code:1,out:"",err:r instanceof Error?r.message:String(r)}}}function Iu(e){if(!e.ref)throw new Error("no project: pass --ref, --db-url, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return e.ref}async function ta(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&O("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await Du(t);return r.path&&c(`Wrote ${r.tables} table${r.tables===1?"":"s"}, ${r.views} view${r.views===1?"":"s"}, ${r.functions} function${r.functions===1?"":"s"} and ${r.enums} enum${r.enums===1?"":"s"} to ${r.path}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}X();import{createHash as Xd}from"node:crypto";import{existsSync as Ar,mkdirSync as Zd,readFileSync as Or,readdirSync as Qd,statSync as em,writeFileSync as tm}from"node:fs";import{dirname as nm,isAbsolute as vi,join as yt,resolve as Ei}from"node:path";import{pathToFileURL as rm}from"node:url";q();function om(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:vi(e)||e.startsWith("./")||e.startsWith("../")?rm(Ei(e)).href:e}function sm(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(om(e)):Promise.resolve().then(()=>(Si(),bi))}function am(e){let t=e instanceof Error?e.message:String(e),n=process.env.SNOUTDATA_LOCAL_RUNTIME;return n?`SNOUTDATA_LOCAL_RUNTIME points at ${n}, and it could not be loaded: ${t}`:`this build of the CLI has no local pod runtime in it, so \`snoutdata start\` cannot run. That is a packaging fault rather than anything you did: please report it. (${t})`}var xi="It is fetched from ghcr.io/snoutdata/snoutpod-postgres on the first `snoutdata start`, so this usually means that registry could not be reached from here.";async function _i(e,t){try{return await e.localImageFor(t)}catch(n){return n instanceof Error?n.message:String(n)}}async function wt(){let e;try{e=await sm()}catch(t){O("tool-missing",am(t))}return{ready:async t=>{let n=await _i(e,t);return typeof n=="string"?await e.podmanReady(xi)??n:e.podmanReady(xi,n.name)},ensureImage:async(t,n)=>{let r=await _i(e,t);return typeof r=="string"?r:e.ensureImage(n,void 0,r)},prepare:()=>e.ensureNetwork(),start:(t,n)=>e.startLocal(e.localRuntime(),t,{onProgress:n}),stop:t=>e.stopLocal(e.localRuntime(),t),status:t=>e.localStatus(e.localRuntime(),t),remove:(t,n)=>e.removeLocal(e.localRuntime(),t,n),uri:t=>e.localUri(t),psql:async(t,n)=>{let r=await e.localPsql(t,n);return{code:r.code,out:r.stdout,err:r.stderr}},mint:t=>({ref:e.newLocalRef(),port:t,ownerPassword:e.newLocalPassword(),adminPassword:e.newLocalPassword()})}}function $i(e){return yt(e,".snoutdata","local.json")}function dn(e){let t=$i(e);if(!Ar(t))return null;try{let n=JSON.parse(Or(t,"utf8"));return!n.ref||!n.port||!n.ownerPassword||!n.adminPassword?null:{ref:n.ref,port:n.port,ownerPassword:n.ownerPassword,adminPassword:n.adminPassword}}catch{return null}}function im(e,t){let n=$i(e);return Zd(nm(n),{recursive:!0}),tm(n,`${JSON.stringify(t,null,2)}
597
+ `,{encoding:"utf8",mode:384}),n}var cm=54322;async function Pi(e,t){let n=await e.ensureImage(t,o=>c(o));n&&O("tool-missing",n);let r=await e.ready(t);r&&O("tool-missing",r),await e.prepare()}function Ri(e,t,n,r=sr){let o=!1;return async s=>{if(o)return e.psql(t,s);let a=await r(n,s);return a.code!==127?a:(o=!0,e.psql(t,s))}}async function lm(e,t={}){let n=t.cwd??process.cwd(),r=dn(n);await Pi(e,r?.ref??null);let o=r??e.mint(t.port??cm),s=r?null:im(n,o),a=await e.start(o,f=>c(f)),u=e.uri(o);if(t.noMigrations)return{status:a,uri:u,applied:[],seeded:!1,created:s};let i=Ri(e,o,Qt(u)),d=await um(i,n,t),l=await dm(i,n,d.length>0||!!s);return{status:a,uri:u,applied:d,seeded:l,created:s}}async function um(e,t,n){let r=Ei(n.dir===void 0?yt(t,"migrations"):vi(n.dir)?n.dir:yt(t,n.dir));if(!Ar(r)||!em(r).isDirectory())return[];let o=Qd(r).filter(f=>f.endsWith(".sql")).map(f=>{let h=Or(yt(r,f),"utf8");return{name:f,sql:h,checksum:Xd("sha256").update(h,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(mm);s.code!==0&&O(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${ht}`);let a=await e(`select name || E'\\t' || checksum from ${ht};`);a.code!==0&&O("failed",a.err.trim()||`could not read ${ht}`);let u=a.out.split(`
598
+ `).map(f=>f.trim()).filter(f=>f.length>0).map(f=>{let h=f.indexOf(" ");return{name:f.slice(0,h),checksum:f.slice(h+1)}}),i=Xt(o,u,{outOfOrder:n.outOfOrder});i.ok||O("conflict",`Refused. This folder and the local database do not agree:
594
599
  ${i.problems.map(f=>` ${f.reason}`).join(`
595
- `)}`,{problems:i.problems});let m=new Map(o.map(f=>[f.name,f])),l=[];for(let f of i.pending){let d=m.get(f.name),p=`insert into ${ct} (name, checksum) values (${Ga(d.name)}, ${Ga(d.checksum)});`,w=nt(d.sql);w||c(`${d.name} runs outside a transaction, so an interruption can leave it half applied.`);let _=w?`begin;
596
- ${d.sql}
597
- ${p}
600
+ `)}`,{problems:i.problems});let d=new Map(o.map(f=>[f.name,f])),l=[];for(let f of i.pending){let h=d.get(f.name),m=`insert into ${ht} (name, checksum) values (${ki(h.name)}, ${ki(h.checksum)});`,p=it(h.sql);p||c(`${h.name} runs outside a transaction, so an interruption can leave it half applied.`);let S=p?`begin;
601
+ ${h.sql}
602
+ ${m}
598
603
  commit;
599
- `:`${d.sql}
600
- ${p}
601
- `,h=await e(_);h.code!==0&&A("failed",`${d.name} failed, and nothing after it was tried.
602
- ${h.err.trim()}`,{failed:d.name,applied:l}),l.push(d.name),c(` applied ${d.name}`)}return l}async function fd(e,t,n){let r=lt(t,"seed.sql");if(!n||!cr(r))return!1;let o=await e(lr(r,"utf8"));return o.code!==0&&A("failed",`seed.sql failed.
603
- ${o.err.trim()}`),c(" ran seed.sql"),!0}var ct="_snoutdata_migrations",pd=`create table if not exists ${ct} (
604
+ `:`${h.sql}
605
+ ${m}
606
+ `,w=await e(S);w.code!==0&&O("failed",`${h.name} failed, and nothing after it was tried.
607
+ ${w.err.trim()}`,{failed:h.name,applied:l}),l.push(h.name),c(` applied ${h.name}`)}return l}async function dm(e,t,n){let r=yt(t,"seed.sql");if(!n||!Ar(r))return!1;let o=await e(Or(r,"utf8"));return o.code!==0&&O("failed",`seed.sql failed.
608
+ ${o.err.trim()}`),c(" ran seed.sql"),!0}var ht="_snoutdata_migrations",mm=`create table if not exists ${ht} (
604
609
  name text primary key,
605
610
  checksum text not null,
606
611
  applied_at timestamptz not null default now()
607
- );`;function Ga(e){return`'${e.replace(/'/g,"''")}'`}function gd(e){if(e===null)return"unknown";let t=["B","kB","MB","GB"],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n<10&&r>0?n.toFixed(1):Math.round(n)} ${t[r]}`}async function Ya(e,t={}){let n=await dd(e,t);return g(n,()=>{n.created&&(c(""),c(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),c(""),c(` ${P("Database")} running on port ${n.status.port}`),n.applied.length>0&&c(` ${P("Migrations")} ${n.applied.length} applied`),n.seeded&&c(` ${P("Seed")} seed.sql ran`),c(""),process.stdout.write(`${n.uri}
608
- `),c(b(" snoutdata gen types typescript --local > database.types.ts")),c(b(" snoutdata stop"))}),0}async function Xa(e,t={}){let n=t.cwd??process.cwd(),r=Vt(n);r||A("not-found","there is no local database for this folder. `snoutdata start` makes one."),await za(e);let o=await e.stop(r.ref);return g(o,()=>c("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function Za(e,t={}){let n=t.cwd??process.cwd(),r=Vt(n);if(!r)return g({exists:!1,ref:null,status:"absent"},()=>c("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready();if(o)return g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>c(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{c(` ${P("Project")} ${s.ref}`),c(` ${P("State")} ${s.ready?"running":s.status}`),c(` ${P("Port")} ${s.port}`),c(` ${P("Size")} ${gd(s.databaseBytes)}`),s.services.length>0&&c(` ${P("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
609
- `)}),0}async function Qa(e,t=process.cwd()){let n=Vt(t);n||A("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:Va(e,n,Ft(r))}}H();q();bt();Je();ke();import{resolve as ei}from"node:path";function hd(){let e=t=>ye(t);return{whoami:()=>De(),listProjects:async()=>({...await X(),local:yd()}),localProject:t=>e(t),createProject:async t=>{let n=await S("cloud-project-create",{name:t.name,...t.region?{region:t.region}:{},...t.teamId?{teamId:t.teamId}:{}});try{return await Xe(n.project.ref)}catch{return{...n.project,ready:!1,note:`${n.project.ref} was created and is still starting. It was not ready within the wait. Call list_projects in a few seconds; do not connect to it yet.`}}},usage:(t,n)=>S("cloud-project-usage",{ref:t,...n?{days:n}:{}}),exportStatus:t=>Me(t),requestExport:t=>Ve(t),resetPassword:t=>S("cloud-project-reset-password",{ref:t}),listTeams:()=>S("cloud-team-list",{}),deployFunction:(t,n,r)=>{let o=e(t);return o?dn(o,n,r):yn(t,n,r).then(s=>s.answer)},listFunctions:async t=>{let n=e(t);return n?Tt(n):wn(t)},deleteFunction:(t,n)=>{let r=e(t);return r?mn(r,n):bn(t,n)},sizeFunction:(t,n,r)=>Sn(t,n,r),listFunctionSecrets:async t=>{let n=e(t);return n?Rt(n):S("cloud-project-secrets",{ref:t})},listTokens:()=>S("cloud-token-list",{}),createToken:(t,n,r)=>S("cloud-token-create",{name:t,...n?{expiresInDays:n}:{},...r?{project:r}:{}}),revokeToken:t=>S("cloud-token-revoke",{id:t}),getProject:async t=>{let n=e(t);return n?{ref:n.ref,name:n.name,local:n.folder,api:n.apiUrl,database:`127.0.0.1:${n.dbPort}`,functions:Tt(n).functions.map(r=>r.name),secretNames:Rt(n).secrets.map(r=>r.name)}:An(t)},getProducts:t=>Ce(t),setProduct:(t,n,r)=>En(t,n,r),listDomains:t=>Mt(t),domainAction:(t,n,r)=>Pn(t,n,r),restoreWindow:t=>$n(t),restoreTo:(t,n,r)=>Tn(t,n,r),setProjectState:async(t,n)=>{let r=await S(`cloud-project-${t}`,{ref:n});try{let o=await Ye(n,t,{timeoutMs:12e4});return{changed:r.changed,state:o?.state??"deleted",...o?{project:o}:{}}}catch{return{...r,settled:!1,note:`${n} has not finished yet. Call list_projects in a few seconds.`}}},connection:t=>V(t),pushMigrations:(t,n)=>Cn(t,{dir:n.dir,dryRun:n.dryRun})}}function yd(){let e=ft().filter(n=>n.ref!==null).map(n=>({ref:n.ref,name:n.name,folder:n.folder})),t=qe(process.cwd())?.local;if(t&&!e.some(n=>ei(n.folder)===ei(t)))try{let n=fe(t);e.push({ref:n.ref,name:n.name,folder:n.folder})}catch{}return e}var ti="app_";async function wd(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await yt();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await Mr(t)).map(o=>({name:`${ti}${o.name}`,description:`[SnoutData Studio, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function ni(e){ze(!0);let{tools:t,config:n}=await wd(),r=hd();n&&(r.callBorrowed=(i,m)=>Ir(n,i.slice(ti.length),m));let o={...e,borrowed:t};z(t.length>0?`snoutdata mcp: ${Nt.length} cloud tools, and ${t.length} borrowed from SnoutData Studio on this machine.`:"snoutdata mcp: cloud tools only. SnoutData Studio is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let i=await De();z(`snoutdata mcp: signed in as ${i.email??"this account"}.`)}catch(i){return z(`snoutdata mcp: ${i instanceof Error?i.message:String(i)}`),z("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}z(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function u(i){a.add(i),i.finally(()=>a.delete(i))}return new Promise(i=>{process.stdin.on("data",m=>{let l=Jo(s+m);s=l.rest;for(let f of l.messages)u(bd(f,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);i(0)})()}),process.stdin.on("error",()=>i(1))})}async function bd(e,t,n){let r;try{r=JSON.parse(e)}catch{z(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await Wo(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
612
+ );`;function ki(e){return`'${e.replace(/'/g,"''")}'`}function fm(e){if(e===null)return"unknown";let t=["B","kB","MB","GB"],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n<10&&r>0?n.toFixed(1):Math.round(n)} ${t[r]}`}async function Ti(e,t={}){let n=await lm(e,t);return g(n,()=>{n.created&&(c(""),c(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),c(""),c(` ${v("Database")} running on port ${n.status.port}`),n.applied.length>0&&c(` ${v("Migrations")} ${n.applied.length} applied`),n.seeded&&c(` ${v("Seed")} seed.sql ran`),c(""),process.stdout.write(`${n.uri}
613
+ `),c(b(" snoutdata gen types typescript --local > database.types.ts")),c(b(" snoutdata stop"))}),0}async function Ai(e,t={}){let n=t.cwd??process.cwd(),r=dn(n);r||O("not-found","there is no local database for this folder. `snoutdata start` makes one."),await Pi(e,r.ref);let o=await e.stop(r.ref);return g(o,()=>c("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function Oi(e,t={}){let n=t.cwd??process.cwd(),r=dn(n);if(!r)return g({exists:!1,ref:null,status:"absent"},()=>c("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready(r.ref);if(o)return g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>c(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{c(` ${v("Project")} ${s.ref}`),c(` ${v("State")} ${s.ready?"running":s.status}`),c(` ${v("Port")} ${s.port}`),c(` ${v("Size")} ${fm(s.databaseBytes)}`),s.services.length>0&&c(` ${v("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
614
+ `)}),0}async function Ni(e,t=process.cwd()){let n=dn(t);n||O("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:Ri(e,n,Qt(r))}}K();q();Pt();Ve();he();import{resolve as Di}from"node:path";function pm(){let e=t=>_e(t);return{whoami:()=>Pe(),listProjects:async()=>({...await ee(),local:gm()}),localProject:t=>e(t),createProject:async t=>{let n=await x("cloud-project-create",{name:t.name,...t.region?{region:t.region}:{},...t.teamId?{teamId:t.teamId}:{}});try{return await nt(n.project.ref)}catch{return{...n.project,ready:!1,note:`${n.project.ref} was created and is still starting. It was not ready within the wait. Call list_projects in a few seconds; do not connect to it yet.`}}},usage:(t,n)=>x("cloud-project-usage",{ref:t,...n?{days:n}:{}}),exportStatus:t=>je(t),requestExport:t=>et(t),resetPassword:t=>x("cloud-project-reset-password",{ref:t}),listTeams:()=>x("cloud-team-list",{}),deployFunction:(t,n,r)=>{let o=e(t);return o?On(o,n,r):Cn(t,n,r).then(s=>s.answer)},listFunctions:async t=>{let n=e(t);return n?Ft(n):Ln(t)},deleteFunction:(t,n)=>{let r=e(t);return r?Nn(r,n):Un(t,n)},sizeFunction:(t,n,r)=>Fn(t,n,r),listFunctionSecrets:async t=>{let n=e(t);return n?Ht(n):x("cloud-project-secrets",{ref:t})},listTokens:()=>x("cloud-token-list",{}),createToken:(t,n,r)=>x("cloud-token-create",{name:t,...n?{expiresInDays:n}:{},...r?{project:r}:{}}),revokeToken:t=>x("cloud-token-revoke",{id:t}),getProject:async t=>{let n=e(t);return n?{ref:n.ref,name:n.name,local:n.folder,api:n.apiUrl,database:`127.0.0.1:${n.dbPort}`,functions:Ft(n).functions.map(r=>r.name),secretNames:Ht(n).secrets.map(r=>r.name)}:Vn(t)},getProducts:t=>Le(t),setProduct:(t,n,r)=>Kn(t,n,r),listDomains:t=>Wt(t),domainAction:(t,n,r)=>Wn(t,n,r),restoreWindow:t=>Jn(t),restoreTo:(t,n,r)=>zn(t,n,r),setProjectState:async(t,n)=>{let r=await x(`cloud-project-${t}`,{ref:n});try{let o=await tt(n,t,{timeoutMs:12e4});return{changed:r.changed,state:o?.state??"deleted",...o?{project:o}:{}}}catch{return{...r,settled:!1,note:`${n} has not finished yet. Call list_projects in a few seconds.`}}},connection:t=>Z(t),pushMigrations:(t,n)=>nr(t,{dir:n.dir,dryRun:n.dryRun})}}function gm(){let e=xt().filter(n=>n.ref!==null).map(n=>({ref:n.ref,name:n.name,folder:n.folder})),t=Ke(process.cwd())?.local;if(t&&!e.some(n=>Di(n.folder)===Di(t)))try{let n=we(t);e.push({ref:n.ref,name:n.name,folder:n.folder})}catch{}return e}var Mi="app_";async function hm(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Et();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await no(t)).map(o=>({name:`${Mi}${o.name}`,description:`[SnoutData Studio, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function Ii(e){Qe(!0);let{tools:t,config:n}=await hm(),r=pm();n&&(r.callBorrowed=(i,d)=>ro(n,i.slice(Mi.length),d));let o={...e,borrowed:t};W(t.length>0?`snoutdata mcp: ${Gt.length} cloud tools, and ${t.length} borrowed from SnoutData Studio on this machine.`:"snoutdata mcp: cloud tools only. SnoutData Studio is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let i=await Pe();W(`snoutdata mcp: signed in as ${i.email??"this account"}.`)}catch(i){return W(`snoutdata mcp: ${i instanceof Error?i.message:String(i)}`),W("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}W(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function u(i){a.add(i),i.finally(()=>a.delete(i))}return new Promise(i=>{process.stdin.on("data",d=>{let l=ws(s+d);s=l.rest;for(let f of l.messages)u(ym(f,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);i(0)})()}),process.stdin.on("error",()=>i(1))})}async function ym(e,t,n){let r;try{r=JSON.parse(e)}catch{W(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await ys(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
610
615
  `)}catch(o){let s=r.id??null;process.stdout.write(`${JSON.stringify({jsonrpc:"2.0",id:s,error:{code:-32603,message:o instanceof Error?o.message:String(o)}})}
611
- `)}}Je();H();q();function Sd(e){return e>=1?"over":e>=.8?"near":"ok"}function ri(e,t){let n=null,r=null,o=0,s=0;for(let u of e)o+=Number.isFinite(u.computeSeconds)?u.computeSeconds:0,s+=Number.isFinite(u.connections)?u.connections:0,!(u.dbBytes===null||!Number.isFinite(u.dbBytes))&&(r=r===null?u.dbBytes:Math.max(r,u.dbBytes),(!n||u.day>=n.day)&&(n={day:u.day,dbBytes:u.dbBytes,repoBytes:u.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:Sd(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function ur(e){if(!Number.isFinite(e)||e<=0)return"none";let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?n>0?`${t}d ${n}h`:`${t}d`:n>0?r>0?`${n}h ${r}m`:`${n}h`:r>0?`${r}m`:`${Math.round(e)}s`}async function oi(e,t={}){let n=await S("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=ri(n.days,n.limit);return g({...n,summary:r},()=>{if(c(P(`${n.name} (${n.ref})`)),!r.latest)c("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${ue(r.storage.usedBytes)} of ${ue(r.storage.limitBytes)} on ${n.limit?.tier}`;c(r.storage.state==="over"?`${s} (${o}%). Over the limit, so writes are refused until it shrinks or the plan changes.`:r.storage.state==="near"?`${s} (${o}%). Close to the limit: at 100% the database goes read-only.`:`${s} (${o}%).`)}else c(`${ue(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&c("This project is currently read-only."),c(`Over ${r.days} day${r.days===1?"":"s"}: ${ur(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&c(b(`Peak in that window was ${ue(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(c(""),c(U([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":ue(o.dbBytes),o.repoBytes===null?"-":ue(o.repoBytes),ur(o.computeSeconds),String(o.connections)])])),c(b("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}var dt="0.10.0",si=`snoutdata ${dt} \u2014 hosted Postgres, from a terminal or an agent
616
+ `)}}Ve();K();q();function wm(e){return e>=1?"over":e>=.8?"near":"ok"}function ji(e,t){let n=null,r=null,o=0,s=0;for(let u of e)o+=Number.isFinite(u.computeSeconds)?u.computeSeconds:0,s+=Number.isFinite(u.connections)?u.connections:0,!(u.dbBytes===null||!Number.isFinite(u.dbBytes))&&(r=r===null?u.dbBytes:Math.max(r,u.dbBytes),(!n||u.day>=n.day)&&(n={day:u.day,dbBytes:u.dbBytes,repoBytes:u.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:wm(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function Nr(e){if(!Number.isFinite(e)||e<=0)return"none";let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?n>0?`${t}d ${n}h`:`${t}d`:n>0?r>0?`${n}h ${r}m`:`${n}h`:r>0?`${r}m`:`${Math.round(e)}s`}async function Ci(e,t={}){let n=await x("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=ji(n.days,n.limit);return g({...n,summary:r},()=>{if(c(v(`${n.name} (${n.ref})`)),!r.latest)c("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${pe(r.storage.usedBytes)} of ${pe(r.storage.limitBytes)} on ${n.limit?.tier}`;c(r.storage.state==="over"?`${s} (${o}%). Over the limit, so writes are refused until it shrinks or the plan changes.`:r.storage.state==="near"?`${s} (${o}%). Close to the limit: at 100% the database goes read-only.`:`${s} (${o}%).`)}else c(`${pe(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&c("This project is currently read-only."),c(`Over ${r.days} day${r.days===1?"":"s"}: ${Nr(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&c(b(`Peak in that window was ${pe(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(c(""),c(U([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":pe(o.dbBytes),o.repoBytes===null?"-":pe(o.repoBytes),Nr(o.computeSeconds),String(o.connections)])])),c(b("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}X();q();Xe();import{spawn as bm}from"node:child_process";import{createHash as Sm}from"node:crypto";import{chmodSync as xm,renameSync as mn,rmSync as Li,writeFileSync as _m}from"node:fs";import{dirname as km,join as vm}from"node:path";var fn=process.env.SNOUTDATA_NPM_REGISTRY??"https://registry.npmjs.org",Em="https://github.com/snoutdata/app/releases/download";function $m(e,t){let n=t==="x64"||t==="amd64"?"x64":t==="arm64"?"arm64":null;return n?e==="linux"||e==="darwin"?`snoutdata-${e}-${n}`:e==="win32"&&n==="x64"?"snoutdata-windows-x64.exe":null:null}function Pm(e,t){for(let n of e.split(/\r?\n/)){let r=/^([0-9a-f]{64})\s+\*?(.+)$/i.exec(n.trim());if(r&&r[2]===t)return r[1].toLowerCase()}return null}async function Rm(){let e;try{e=await fetch(`${fn}/snoutdata/latest`,{headers:{accept:"application/json"}})}catch(n){throw new N("network",`could not reach ${fn} to find the newest version: ${n instanceof Error?n.message:String(n)}`)}if(!e.ok)throw new N("server",`${fn} answered ${e.status} when asked for the newest version`);let t=await e.json();if(typeof t.version!="string")throw new N("server",`${fn} did not say which version is newest`);return t.version}async function Ui(e){let t;try{t=await fetch(e)}catch(n){throw new N("network",`could not download ${e}: ${n instanceof Error?n.message:String(n)}`)}if(!t.ok)throw new N("failed",`download failed (${t.status}): ${e}`);return Buffer.from(await t.arrayBuffer())}function Tm(e,t){let n=vm(km(e),`.snoutdata-upgrade-${process.pid}`);_m(n,t,{mode:493});try{if(process.platform==="win32"){let r=`${e}.old`;Li(r,{force:!0}),mn(e,r);try{mn(n,e)}catch(o){throw mn(r,e),o}}else xm(n,493),mn(n,e)}catch(r){Li(n,{force:!0});let o=r.code;throw o==="EACCES"||o==="EPERM"?new N("forbidden",`${e} is not writable by this user. Run the installer instead: ${process.platform==="win32"?ue:Ye}`):r}}function Am(){return new Promise((e,t)=>{let n=bm(process.platform==="win32"?"npm.cmd":"npm",["install","-g","snoutdata@latest"],{stdio:["ignore","inherit","inherit"],shell:process.platform==="win32"});n.on("error",r=>t(new N("tool-missing",`npm could not be run (${r.message}). Run it yourself: ${ue}`))),n.on("exit",r=>e(r??1))})}async function Fi(e={}){let t=e.kind??Tt(),n=await Rm(),r=so(J,n),o={installed:J,latest:n,upToDate:!r,install:t};if(!r||e.check)return g({...o,upgraded:!1},()=>{c(r?`snoutdata ${J} is installed; ${n} is the newest. \`snoutdata upgrade\` installs it.`:`snoutdata ${J} is the newest version.`)}),0;switch(t){case"npx":return g({...o,upgraded:!1},()=>{c(`This is a copy npx fetched (${J}). Run \`npx snoutdata@latest\` to use ${n}; there is nothing installed to upgrade.`)}),0;case"source":throw new N("usage",`this snoutdata (${J}) is running from source, so there is nothing to replace. Install the published one: ${process.platform==="win32"?ue:Ye}`);case"npm":{c(`Upgrading snoutdata ${J} to ${n} with npm\u2026`);let s=await Am();if(s!==0)throw new N("failed",`npm exited ${s}. Run it yourself: ${ue}`);return g({...o,upgraded:!0},()=>c(`Upgraded to ${n}.`)),0}case"binary":{let s=$m(process.platform,process.arch);if(!s)throw new N("failed",`no snoutdata binary is built for ${process.platform}-${process.arch}. Use npm: ${ue}`);c(`Downloading snoutdata ${n} for ${process.platform}-${process.arch}\u2026`);let a=`${Em}/cli-v${n}`,[u,i]=await Promise.all([Ui(`${a}/${s}`),Ui(`${a}/SHA256SUMS`)]),d=Pm(i.toString("utf8"),s);if(!d)throw new N("failed",`${s} is not listed in the release's SHA256SUMS. Refusing to install.`);let l=Sm("sha256").update(u).digest("hex");if(l!==d)throw new N("failed",`checksum mismatch for ${s}. Refusing to install.
617
+ expected ${d}
618
+ actual ${l}`);return Tm(process.execPath,u),g({...o,upgraded:!0,path:process.execPath},()=>c(`Upgraded ${process.execPath} to ${n}.`)),0}}}jt();he();Xe();var Bi=`snoutdata ${J} \u2014 hosted Postgres, from a terminal or an agent
612
619
 
613
- snoutdata init [--name X] [--env] a database for this folder, linked, ready to use
620
+ snoutdata init [--name X] [--env] a database for this folder, linked, ready to use;
621
+ --env writes DATABASE_URL into ./.env (and adds
622
+ .env to .gitignore) instead of printing it
614
623
  snoutdata login [--provider github] sign in through a browser
624
+ snoutdata login --email you@x.com sign in as that account: the browser offers it,
625
+ and a different account is reported at once
615
626
  snoutdata login --sso [--domain D] sign in with your company's identity provider;
616
627
  D is a work email or a domain, and it is asked
617
628
  for when not given. Only the domain is sent
@@ -619,20 +630,24 @@ ${o.err.trim()}`),c(" ran seed.sql"),!0}var ct="_snoutdata_migrations",pd=`crea
619
630
  snoutdata login --no-browser print the URL instead of opening one
620
631
  snoutdata logout
621
632
  snoutdata whoami
633
+ snoutdata upgrade [--check] install the newest CLI, the way this one was
622
634
 
623
635
  snoutdata tokens create --name ci [--expires DAYS] [--project REF] a credential for CI, shown once
624
636
  snoutdata tokens list
625
637
  snoutdata tokens revoke <id|sdt_prefix>
626
638
 
627
639
  snoutdata projects list
628
- snoutdata projects create --name X [--region R] [--no-wait]
640
+ snoutdata projects create --name X [--region R] [--no-wait] [--show-url]
641
+ prints the ref; the connection string (it holds
642
+ the password) only with --show-url, or from db url
629
643
  snoutdata projects pause|resume|delete [--ref R] [--no-wait]
630
644
  snoutdata link --ref R write .snoutdata/project.json here
631
645
  snoutdata link --local [NAME|FOLDER] use a local project (Studio's, or a snout-stack folder) here
632
646
 
633
647
  snoutdata projects show [--ref R] one project: state, products, functions, domains
634
648
 
635
- snoutdata products [--ref R] auth, storage, the data API and push: on or off
649
+ snoutdata products [--ref R] auth, storage, the data API and push: on or off;
650
+ Realtime is always on (shown, not switched)
636
651
  snoutdata products enable|disable auth|storage|data-api|push [--ref R]
637
652
  snoutdata push credentials [--ref R] push keys: what is set (never the keys)
638
653
  snoutdata push credentials set apns --p8 FILE --key-id ID --team-id ID --topic BUNDLE [--environment E]
@@ -641,10 +656,16 @@ ${o.err.trim()}`),c(" ran seed.sql"),!0}var ct="_snoutdata_migrations",pd=`crea
641
656
  snoutdata auth [--ref R] Google sign-in and redirect addresses
642
657
  \u2026 | snoutdata auth google --client-id ID --stdin your own Google client; secret on stdin
643
658
  snoutdata auth google off
659
+ snoutdata auth anonymous on|off guest sign-in: signInAnonymously(), no email or password
644
660
  snoutdata auth redirects --site-url URL --allow URL,URL
645
661
  snoutdata auth templates the five auth emails, ours or yours
646
662
  snoutdata auth template KIND --file body.html [--subject S] | reset
647
663
 
664
+ snoutdata realtime inspect [--channel C] [--watch] [--ref R]
665
+ channels open now: who, their presence, the last minute
666
+ snoutdata realtime logs [--since 10m] [--channel C] [--ref R]
667
+ connects, joins, leaves and disconnects, with why
668
+
648
669
  snoutdata domains [--ref R] your own domains in front of the project's API
649
670
  snoutdata domains add|verify|remove <hostname> [--ref R]
650
671
 
@@ -690,7 +711,9 @@ ${o.err.trim()}`),c(" ran seed.sql"),!0}var ct="_snoutdata_migrations",pd=`crea
690
711
  Every command takes --json, --quiet and --help, and anything that waits takes --timeout
691
712
  SECONDS. A failure in --json mode is {"ok":false,"code","error"} on stdout, and the exit
692
713
  code says the same thing more coarsely: 2 the command was wrong, 3 the credential, 4 not
693
- ready yet, 5 forbidden, 6 not found, 7 conflict, 8 quota, 9 network, 10 timed out.
714
+ ready yet, 5 forbidden, 6 not found, 7 conflict, 8 quota, 9 network, 10 timed out, 11 this
715
+ CLI is out of date ("code":"outdated", run "snoutdata upgrade"). "<command> --help"
716
+ explains each of a command's flags.
694
717
 
695
718
  With no credential and a person present, sign-in is offered: SnoutData Studio if it is
696
719
  running here, then a pairing code. With no person (not a terminal, --json, CI, or
@@ -698,16 +721,17 @@ SNOUTDATA_NO_INTERACTIVE) nothing is asked and it exits 3 at once.
698
721
 
699
722
  A project comes from --ref, then SNOUTDATA_PROJECT, then
700
723
  .snoutdata/project.json in this folder or a parent. A token comes from
701
- SNOUTDATA_ACCESS_TOKEN, then ~/.snoutdata/auth.json. The session that login writes lasts
702
- an hour, so CI wants "tokens create", which does not expire.
724
+ SNOUTDATA_ACCESS_TOKEN, then ~/.snoutdata/auth.json. The session that login writes renews
725
+ itself as it is used; CI, which has no browser to sign in with, wants "tokens create".
703
726
 
704
- Docs: https://docs.snoutdata.com/cloud/cli
727
+ Docs: https://docs.snoutdata.com/developers/cli
705
728
  Examples: https://github.com/snoutdata/snoutdata (a star helps other people find it)
706
729
  Source: https://github.com/snoutdata/snout-cli
707
- `;async function xd(e){let[t,n,...r]=e.command;if(F(e,"version")||t==="version")return g({version:dt},()=>process.stdout.write(`${dt}
708
- `)),0;if(F(e,"help")&&t&&t!=="help"){let i=Yt.filter(m=>m.name===t||m.name.startsWith(`${t} `));if(i.length>0)return g({commands:i},()=>{for(let m of i){let l=m.flags.length>0?` ${m.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${m.name}${l}
709
- ${m.summary}
730
+ `;async function Om(e){let[t,n,...r]=e.command;if(B(e,"version")||t==="version")return g({version:J},()=>process.stdout.write(`${J}
731
+ `)),0;if(B(e,"help")&&t&&t!=="help"){let i=pn.filter(d=>d.name===t||d.name.startsWith(`${t} `));if(i.length>0)return g({commands:i.map(d=>({...d,flagHelp:Object.fromEntries(d.flags.map(l=>[l,gn(d.name,l)]))}))},()=>{for(let d of i){let l=d.flags.length>0?` ${d.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${d.name}${l}
732
+ ${d.summary}
733
+ `);let f=Math.max(0,...d.flags.map(h=>h.length));for(let h of d.flags)process.stdout.write(` ${h.padEnd(f)} ${gn(d.name,h)}
710
734
  `)}process.stdout.write(`
711
735
  Every command also takes --json, --quiet and --help.
712
- `)}),0}let o=F(e,"help")||t==="help";if(o||!t)return g({version:dt,usage:si.trim(),commands:Yt},()=>process.stdout.write(si)),o?0:ie.usage;let s=()=>{let i=_e(e,"timeout");if(i!==void 0){if(!(i>0))throw new y("--timeout is a number of seconds greater than zero");return i*1e3}},a=()=>{let i=Ke({flag:O(e,"ref")});if(!i)throw new y("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i},u=()=>{let i=Ke({flag:O(e,"ref")});return i?ye(i):null};switch(t){case"login":{if(F(e,"device")){let f=await St({name:wt(),platform:process.platform});g({verifyUrl:f.verifyUrl,code:f.code,expiresAt:f.expiresAt},()=>{c(""),c(` Open ${f.verifyUrl}`),c(` Type ${f.code}`),c(""),c(b("Waiting for that to be approved\u2026"))});let d=await xt(f.secret,{intervalMs:Math.max(1,f.interval)*1e3,expiresAt:f.expiresAt});return ae({accessToken:d.token}),g({ok:!0,name:d.name??null},()=>c("Signed in.")),0}let i=O(e,"domain"),m=F(e,"sso")||i!==void 0;if(m&&O(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let l=await vt({provider:O(e,"provider"),sso:m,domain:i,noBrowser:F(e,"no-browser")||!pe(),timeoutMs:s()});return g({email:l.email??null},()=>c(`Signed in${l.email?` as ${l.email}`:""}.`)),0}case"logout":{xr();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({ok:!0,environmentTokenStillSet:i},()=>{c("Signed out."),i&&(c("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),c(b(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await De();return g(i,()=>{process.stdout.write(`${i.email??i.id}
713
- `),i.token&&c(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await ko(),0;case"create":{let i=O(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await _o({name:i,expiresInDays:_e(e,"expires"),project:O(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new y("tokens revoke needs the token's id or its sdt_ prefix");return await vo(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await yo(),0;case"create":{let i=O(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let m=O(e,"team");return await pn({teamId:m?await xo(m):void 0,name:i,region:O(e,"region"),wait:!F(e,"no-wait"),timeoutMs:s(),link:void 0}),0}case"pause":case"resume":case"delete":return await wo(n,O(e,"ref")??r[0]??a(),{wait:!F(e,"no-wait"),timeoutMs:s()}),0;case"show":{let i=O(e,"ref")??r[0]??a(),m=ye(i);m?eo(m):await es(i)}return 0;default:throw new y(`unknown command: projects ${n}`)}case"functions":{let i=u();if(i){if(n==="deploy"){if(!r[0])throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await lo(i,r[0],{dir:O(e,"dir"),verifyJwt:!F(e,"no-verify-jwt")}),0}if(n===void 0||n==="list")return uo(i),0;if((n==="delete"||n==="remove")&&r[0])return await mo(i,r[0]),0}switch(n){case"deploy":{let m=r[0];if(!m)throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await Ro(a(),m,{dir:O(e,"dir"),entrypoint:O(e,"entrypoint"),verifyJwt:!F(e,"no-verify-jwt")}),0}case void 0:case"list":return await Do(a()),0;case"size":{let m=r[0];if(!m)throw new y("functions size needs a name: snoutdata functions size <name> --memory MB --concurrency N");return await No(a(),m,{memoryMb:_e(e,"memory"),concurrency:_e(e,"concurrency"),reset:F(e,"reset")}),0}case"delete":case"remove":{let m=r[0];if(!m)throw new y("functions delete needs a name");return await Mo(a(),m),0}default:throw new y(`unknown command: functions ${n}`)}}case"secrets":{let i=u();if(i){if(n==="set")return await po(i,await jo(r,{stdin:F(e,"stdin")})),0;if(n===void 0||n==="list")return go(i),0;if((n==="unset"||n==="remove")&&r[0])return await ho(i,r[0]),0}switch(n){case"set":return await Lo(a(),r,{stdin:F(e,"stdin")}),0;case void 0:case"list":return await Fo(a()),0;case"unset":case"remove":{let m=r[0];if(!m)throw new y("secrets unset needs a name");return await Uo(a(),m),0}default:throw new y(`unknown command: secrets ${n}`)}}case"keys":switch(n){case void 0:case"show":case"list":return await Eo(a()),0;case"rotate":return await Po(a(),{force:F(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Vo(a()),0;if(n==="enable"||n==="disable")return await Yo(a(),zo(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${Dt.join("|")}]`)}case"push":{if(n!=="credentials")throw new y("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await xs(a()),0;if(i==="set"){let m=jn(r[1]),l=m==="apns"?await bs({p8:O(e,"p8"),keyId:O(e,"key-id"),teamId:O(e,"team-id"),topic:O(e,"topic"),environment:O(e,"environment")}):await Ss(O(e,"file"));return await _s(a(),m,l),0}if(i==="remove"||i==="unset")return await ks(a(),jn(r[1])),0;throw new y(`unknown command: push credentials ${i}`)}case"auth":return await ps(a(),n,r,{clientId:O(e,"client-id"),stdin:F(e,"stdin"),siteUrl:O(e,"site-url"),allow:O(e,"allow"),file:O(e,"file"),subject:O(e,"subject")}),0;case"domains":return await Xo(a(),n,r[0]),0;case"teams":return await So(),0;case"link":{if(F(e,"local")){let l;try{l=Er(n)}catch(d){throw new L("not-found",d instanceof Error?d.message:String(d))}let f=Ge(process.cwd(),{ref:l.ref,...l.name?{name:l.name}:{},local:l.folder});return g({ref:l.ref,local:l.folder,api:l.apiUrl,path:f},()=>{c(`Linked ${l.name??l.ref}, the local project in ${l.folder} (${f}).`),c(`Its API is ${l.apiUrl}; its database is on 127.0.0.1:${l.dbPort}.`)}),0}let i=O(e,"ref")??n;if(!i)throw new y("link needs --ref");let m=Ge(process.cwd(),{ref:i});return g({ref:i,path:m},()=>c(`Linked ${i} (${m}).`)),0}case"db":switch(n){case"url":return await On(a()),0;case"psql":return is(a(),r);case"reset-password":return await bo(a()),0;case"push":return ys(a(),{dir:O(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return F(e,"window")?(await Zo(a()),0):O(e,"at")?(await Qo(a(),O(e,"at"),O(e,"name")),0):us(a(),{file:O(e,"file"),force:!!e.flags.force});case"export":return ls(a(),{timeoutMs:s(),out:O(e,"out"),statusOnly:!!e.flags.status});default:throw new y(n?`unknown command: db ${n}`:"db needs an action: url, psql, reset-password, export, push, restore")}case"gen":{if(n!=="types")throw new y(n?`unknown command: gen ${n}`:"gen needs an action: types");let i=F(e,"local")?await Qa(await ut()):null,m=i?i.dbUrl:O(e,"db-url");return Ps(r[0],{ref:m?void 0:a(),dbUrl:m,schemas:(O(e,"schema")??"public").split(",").map(l=>l.trim()).filter(Boolean),out:O(e,"out"),sql:i?.sql})}case"start":{let i=u();if(i)return Zr(i)}return Ya(await ut(),{port:_e(e,"port"),dir:O(e,"dir"),noMigrations:F(e,"no-migrations"),outOfOrder:F(e,"out-of-order")});case"stop":{let i=u();return i?Qr(i):Xa(await ut())}case"status":{let i=u();return i?Xr(i):Za(await ut())}case"usage":return oi(a(),{days:_e(e,"days"),history:!!e.flags.history});case"mcp":return ni({version:dt,allowDelete:!!e.flags["allow-delete"]});case"init":return _d(e);default:throw new y(`unknown command: ${t}`)}}async function _d(e){let t=Ke({flag:O(e,"ref")});if(t)return c(`This folder is already linked to ${t}.`),F(e,"env")?(await ai(t),0):(await On(t),0);let n=O(e,"name")??kd(process.cwd());c(`Creating a project called "${n}"\u2026`),await pn({name:n,region:O(e,"region"),wait:!0,link:process.cwd()});let r=Ke({cwd:process.cwd()});return r&&F(e,"env")&&await ai(r),0}async function ai(e){let{connection:t}=await Promise.resolve().then(()=>(H(),we)),n=await t(e),r=cs(process.cwd(),n.uri);c(`${r.replaced?"Updated":"Wrote"} DATABASE_URL in ${r.path}.`),r.ignored&&c("Added .env to .gitignore, since it holds the password.")}function kd(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function vd(e){if(e instanceof L)return ge(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return ge("usage",e.message),ie.usage;if(e instanceof he)return ge("not-signed-in",e.message),ie["not-signed-in"];if(e instanceof oe){let r=gt(e.status);return ge(r,e.message,{status:e.status}),ie[r]}let t=Tr(e);if(t)return ge("usage",t),ie.usage;let n=Ar(e);return ge(n,e instanceof Error?e.message:String(e)),ie[n]}function dr(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function Ed(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function Pd(){ze(Ed(process.argv.slice(2)));let e;try{e=pr(process.argv.slice(2))}catch(t){ge("usage",String(t instanceof Error?t.message:t)),dr(ie.usage);return}ze(e.json),Ur(e.flags.quiet===!0),Rr(ht({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{dr(await xd(e))}catch(t){dr(vd(t))}}Pd();
736
+ `)}),0}let o=B(e,"help")||t==="help";if(o||!t)return g({version:J,usage:Bi.trim(),commands:pn},()=>process.stdout.write(Bi)),o?0:le.usage;let s=()=>{let i=$e(e,"timeout");if(i!==void 0){if(!(i>0))throw new y("--timeout is a number of seconds greater than zero");return i*1e3}},a=()=>{let i=Je({flag:R(e,"ref")});if(!i)throw new y("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i},u=()=>{let i=Je({flag:R(e,"ref")});return i?_e(i):null};switch(t){case"login":{if(B(e,"device")){let f=await At({name:$t(),platform:process.platform});g({verifyUrl:f.verifyUrl,code:f.code,expiresAt:f.expiresAt},()=>{c(""),c(` Open ${f.verifyUrl}`),c(` Type ${f.code}`),c(""),c(b("Waiting for that to be approved\u2026"))});let h=await Ot(f.secret,{intervalMs:Math.max(1,f.interval)*1e3,expiresAt:f.expiresAt});se({accessToken:h.token});let m=R(e,"email"),p=m?(await Pe()).email??void 0:void 0;return m&&Pn(m,{accessToken:h.token,email:p}),g({ok:!0,name:h.name??null,email:p??null},()=>c(`Signed in${p?` as ${p}`:""}.`)),0}let i=R(e,"domain"),d=B(e,"sso")||i!==void 0;if(d&&R(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let l=await It({provider:R(e,"provider"),sso:d,domain:i,noBrowser:B(e,"no-browser")||!be(),timeoutMs:s(),email:R(e,"email")});return g({email:l.email??null},()=>c(`Signed in${l.email?` as ${l.email}`:""}.`)),0}case"logout":{qr();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({ok:!0,environmentTokenStillSet:i},()=>{c("Signed out."),i&&(c("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),c(b(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await Pe(),d=St()?.expectedEmail,l=!!(d&&i.email&&!Rn(d,i.email));return l&&W(`This session is ${i.email}, and login asked for ${d}. Run "snoutdata login --email ${d}" to sign in as that account.`),g({...i,...d?{expectedEmail:d,matchesExpected:!l}:{}},()=>{process.stdout.write(`${i.email??i.id}
737
+ `),i.token&&c(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await zo(),0;case"create":{let i=R(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await Jo({name:i,expiresInDays:$e(e,"expires"),project:R(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new y("tokens revoke needs the token's id or its sdt_ prefix");return await Vo(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await Ho(),0;case"create":{let i=R(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let d=R(e,"team");return await Mn({teamId:d?await Wo(d):void 0,name:i,region:R(e,"region"),wait:!B(e,"no-wait"),timeoutMs:s(),link:void 0,showUrl:B(e,"show-url")}),0}case"pause":case"resume":case"delete":return await qo(n,R(e,"ref")??r[0]??a(),{wait:!B(e,"no-wait"),timeoutMs:s()}),0;case"show":{let i=R(e,"ref")??r[0]??a(),d=_e(i);d?$o(d):await Es(i)}return 0;default:throw new y(`unknown command: projects ${n}`)}case"functions":{let i=u();if(i){if(n==="deploy"){if(!r[0])throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await Io(i,r[0],{dir:R(e,"dir"),verifyJwt:!B(e,"no-verify-jwt")}),0}if(n===void 0||n==="list")return jo(i),0;if((n==="delete"||n==="remove")&&r[0])return await Co(i,r[0]),0}switch(n){case"deploy":{let d=r[0];if(!d)throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await ts(a(),d,{dir:R(e,"dir"),entrypoint:R(e,"entrypoint"),verifyJwt:!B(e,"no-verify-jwt")}),0}case void 0:case"list":return await os(a()),0;case"size":{let d=r[0];if(!d)throw new y("functions size needs a name: snoutdata functions size <name> --memory MB --concurrency N");return await rs(a(),d,{memoryMb:$e(e,"memory"),concurrency:$e(e,"concurrency"),reset:B(e,"reset")}),0}case"delete":case"remove":{let d=r[0];if(!d)throw new y("functions delete needs a name");return await ss(a(),d),0}default:throw new y(`unknown command: functions ${n}`)}}case"secrets":{let i=u();if(i){if(n==="set")return await Uo(i,await cs(r,{stdin:B(e,"stdin")})),0;if(n===void 0||n==="list")return Fo(i),0;if((n==="unset"||n==="remove")&&r[0])return await Bo(i,r[0]),0}switch(n){case"set":return await ls(a(),r,{stdin:B(e,"stdin")}),0;case void 0:case"list":return await ds(a()),0;case"unset":case"remove":{let d=r[0];if(!d)throw new y("secrets unset needs a name");return await us(a(),d),0}default:throw new y(`unknown command: secrets ${n}`)}}case"keys":switch(n){case void 0:case"show":case"list":return await Yo(a()),0;case"rotate":return await Xo(a(),{force:B(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Ss(a()),0;if(n==="enable"||n==="disable")return await xs(a(),bs(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${Kt.join("|")}]`)}case"push":{if(n!=="credentials")throw new y("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await Ys(a()),0;if(i==="set"){let d=rr(r[1]),l=d==="apns"?await zs({p8:R(e,"p8"),keyId:R(e,"key-id"),teamId:R(e,"team-id"),topic:R(e,"topic"),environment:R(e,"environment")}):await Vs(R(e,"file"));return await Xs(a(),d,l),0}if(i==="remove"||i==="unset")return await Zs(a(),rr(r[1])),0;throw new y(`unknown command: push credentials ${i}`)}case"auth":return await qs(a(),n,r,{clientId:R(e,"client-id"),stdin:B(e,"stdin"),siteUrl:R(e,"site-url"),allow:R(e,"allow"),file:R(e,"file"),subject:R(e,"subject")}),0;case"realtime":{if(n===void 0||n==="inspect")return Ls(a(),{channel:R(e,"channel"),watch:B(e,"watch")});if(n==="logs")return Us(a(),{channel:R(e,"channel"),since:R(e,"since")});throw new y(`unknown command: realtime ${n}. realtime inspect|logs`)}case"domains":return await _s(a(),n,r[0]),0;case"teams":return await Ko(),0;case"link":{if(B(e,"local")){let l;try{l=Jr(n)}catch(h){throw new N("not-found",h instanceof Error?h.message:String(h))}let f=We(process.cwd(),{ref:l.ref,...l.name?{name:l.name}:{},local:l.folder});return g({ref:l.ref,local:l.folder,api:l.apiUrl,path:f},()=>{c(`Linked ${l.name??l.ref}, the local project in ${l.folder} (${f}).`),c(`Its API is ${l.apiUrl}; its database is on 127.0.0.1:${l.dbPort}.`)}),0}let i=R(e,"ref")??n;if(!i)throw new y("link needs --ref");let d=We(process.cwd(),{ref:i});return g({ref:i,path:d},()=>c(`Linked ${i} (${d}).`)),0}case"db":switch(n){case"url":return await Xn(a()),0;case"psql":return Ns(a(),r);case"reset-password":return await Go(a()),0;case"push":return Ws(a(),{dir:R(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return B(e,"window")?(await ks(a()),0):R(e,"at")?(await vs(a(),R(e,"at"),R(e,"name")),0):Is(a(),{file:R(e,"file"),force:!!e.flags.force});case"export":return Ms(a(),{timeoutMs:s(),out:R(e,"out"),statusOnly:!!e.flags.status});default:throw new y(n?`unknown command: db ${n}`:"db needs an action: url, psql, reset-password, export, push, restore")}case"gen":{if(n!=="types")throw new y(n?`unknown command: gen ${n}`:"gen needs an action: types");let i=B(e,"local")?await Ni(await wt()):null,d=i?i.dbUrl:R(e,"db-url");return ta(r[0],{ref:d?void 0:a(),dbUrl:d,schemas:(R(e,"schema")??"public").split(",").map(l=>l.trim()).filter(Boolean),out:R(e,"out"),sql:i?.sql})}case"start":{let i=u();if(i)return vo(i)}return Ti(await wt(),{port:$e(e,"port"),dir:R(e,"dir"),noMigrations:B(e,"no-migrations"),outOfOrder:B(e,"out-of-order")});case"stop":{let i=u();return i?Eo(i):Ai(await wt())}case"status":{let i=u();return i?ko(i):Oi(await wt())}case"usage":return Ci(a(),{days:$e(e,"days"),history:!!e.flags.history});case"mcp":return Ii({version:J,allowDelete:!!e.flags["allow-delete"]});case"init":return Nm(e);case"upgrade":return Fi({check:B(e,"check")});default:throw new y(`unknown command: ${t}`)}}async function Nm(e){let t=Je({flag:R(e,"ref")});if(t)return c(`This folder is already linked to ${t}.`),B(e,"env")?(await Hi(t),0):(await Xn(t),0);let n=R(e,"name")??Dm(process.cwd());c(`Creating a project called "${n}"\u2026`),await Mn({name:n,region:R(e,"region"),wait:!0,link:process.cwd(),showUrl:!B(e,"env")});let r=Je({cwd:process.cwd()});return r&&B(e,"env")&&await Hi(r),0}async function Hi(e){let{connection:t}=await Promise.resolve().then(()=>(K(),fe)),n=await t(e),r=Ds(process.cwd(),n.uri);c(`${r.replaced?"Updated":"Wrote"} DATABASE_URL in ${r.path}.`),r.ignored&&c("Added .env to .gitignore, since it holds the password.")}function Dm(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Mm(e){if(e instanceof N)return Se(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return Se("usage",e.message),le.usage;if(e instanceof xe)return Se("not-signed-in",e.message),le["not-signed-in"];if(e instanceof ie){let r=kt(e.status);return Se(r,e.message,{status:e.status}),le[r]}let t=Yr(e);if(t)return Se("usage",t),le.usage;let n=Xr(e);return Se(n,e instanceof Error?e.message:String(e)),le[n]}function Dr(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function Im(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function jm(){Qe(Im(process.argv.slice(2)));let e;try{e=jr(process.argv.slice(2))}catch(t){Se("usage",String(t instanceof Error?t.message:t)),Dr(le.usage);return}Qe(e.json),co(e.flags.quiet===!0),Zr(vt({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{Dr(await Om(e))}catch(t){Dr(Mm(t))}}jm();