snoutdata 0.4.1 → 0.5.0

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.
Files changed (2) hide show
  1. package/dist/snoutdata.mjs +115 -67
  2. package/package.json +1 -1
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var ps=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var bn=(e,t)=>{for(var n in t)ps(e,n,{get:t[n],enumerable:!0})};function Sn(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]=ys(o);if(gs.has(s)){let i=a??e[++r];if(i===void 0)throw new y(`--${s} needs a value`);n[s]=i;continue}if(a!==void 0)throw new y(`--${s} does not take a value`);if(!hs.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function ys(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function $(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function U(e,t){return e.flags[t]===!0}function Ee(e,t){let n=$(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,gs,hs,le=A(()=>{"use strict";y=class extends Error{},gs=new Set(["name","at","region","ref","file","provider","domain","expires","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint"]);hs=new Set(["json","window","help","version","force","no-verify-jwt","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history"])});import{chmodSync as ws,existsSync as kn,mkdirSync as xn,readFileSync as vn,rmSync as bs,writeFileSync as Pn}from"node:fs";import{homedir as Fe}from"node:os";import{dirname as ht,join as En,parse as Ss,resolve as ks}from"node:path";function yt(e=Fe()){return En(e,".snoutdata","auth.json")}function _n(e=Fe()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=yt(e);if(!kn(n))return null;try{let r=JSON.parse(vn(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function z(e,t=Fe()){let n=yt(t);xn(ht(n),{recursive:!0}),Pn(n,`${JSON.stringify(e,null,2)}
3
- `,{mode:384}),ws(n,384)}function Tn(e=Fe()){bs(yt(e),{force:!0})}function An(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function $n(e){return En(e,".snoutdata","project.json")}function xs(e){let t=ks(e),n=Ss(t).root;for(;;){let r=$n(t);if(kn(r))try{let o=JSON.parse(vn(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=ht(t)}}function Be(e,t){let n=$n(e);return xn(ht(n),{recursive:!0}),Pn(n,`${JSON.stringify(t,null,2)}
4
- `),n}function He(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:xs(e.cwd??process.cwd())?.ref??null}var pe=A(()=>{"use strict"});function qe(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 vs){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 ge(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 Rn(e){wt=e}function te(){return(wt??qe({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function he(){return(wt??qe({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var vs,wt,ye=A(()=>{"use strict";vs=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];wt=null});import{existsSync as Ps,readFileSync as Es}from"node:fs";import{homedir as bt,hostname as _s}from"node:os";import{join as we}from"node:path";function Ts(e,t,n){if(t==="win32"){let r=n.APPDATA??we(bt(),"AppData","Roaming");return we(r,e)}return t==="darwin"?we(bt(),"Library","Application Support",e):we(n.XDG_CONFIG_HOME??we(bt(),".config"),e)}function Nn(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>we(Ts(n,e,t),"mcp.json"))}function As(e=process.platform,t=process.env){return Nn(e,t)[0]}function $s(e=As()){if(!Ps(e))return null;try{let t=JSON.parse(Es(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function Rs(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function Ke(e){if(e!==void 0)return On(e);let t="no-config";for(let n of Nn()){let r=$s(n);if(!r)continue;let o=await On(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function On(e){let t=Rs(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(Os)});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 Dn(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??`the app refused (${n.status})`}}function We(){return _s()}async function St(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??"the app refused");return s.result}async function In(e){return await St(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await St(e,"tools/list",{},3e3)).tools??[]}async function jn(e,t,n){return St(e,"tools/call",{name:t,arguments:n},12e4)}var Os,Je=A(()=>{"use strict";Os=1500});function P(e,t,n){throw new q(e,t,n)}function Ve(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 Mn(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(Ns.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 ne,q,Ns,re=A(()=>{"use strict";ne={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},q=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return ne[this.code]}};Ns=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});async function Ln(e,t){let n=await fetch(`${G}/functions/v1/${e}`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return{status:n.status,body:r}}async function Ge(e={}){let{status:t,body:n}=await Ln("cloud-device-start",{client:e});return t===429&&P("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&P("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)&&P("server",n.error??`could not start a pairing (${t})`),n}async function ze(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&P("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 Ln("cloud-device-poll",{secret:e});switch(o===404&&P("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&P("usage",s.error??"that is not a pairing secret"),o!==200&&P("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":P("forbidden",s.error??"that pairing was refused in the browser");case"used":P("conflict",s.error??"that pairing has already been completed");case"expired":P("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 kt=A(()=>{"use strict";M();re()});function _e(e){Ye=e}function Fn(e){Un=e}function x(e){return Bn?`\x1B[2m${e}\x1B[0m`:e}function T(e){return Bn?`\x1B[1m${e}\x1B[0m`:e}function h(e,t){if(Ye){process.stdout.write(`${JSON.stringify(e,null,2)}
5
- `);return}t()}function l(e){!Ye&&!Un&&process.stderr.write(`${e}
6
- `)}function de(e,t,n){process.stderr.write(`${t}
2
+ var Ts=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var Pn=(e,t)=>{for(var n in t)Ts(e,n,{get:t[n],enumerable:!0})};function En(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]=Rs(o);if(As.has(s)){let i=a??e[++r];if(i===void 0)throw new y(`--${s} needs a value`);n[s]=i;continue}if(a!==void 0)throw new y(`--${s} does not take a value`);if(!$s.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function Rs(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function $(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function U(e,t){return e.flags[t]===!0}function Ee(e,t){let n=$(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,As,$s,te=A(()=>{"use strict";y=class extends Error{},As=new Set(["name","at","region","ref","file","provider","domain","expires","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","client-id","site-url","allow","subject"]);$s=new Set(["json","window","help","version","force","no-verify-jwt","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history"])});import{chmodSync as Os,existsSync as _n,mkdirSync as Tn,readFileSync as An,rmSync as Ns,writeFileSync as $n}from"node:fs";import{homedir as Fe}from"node:os";import{dirname as yt,join as Rn,parse as Ds,resolve as Is}from"node:path";function wt(e=Fe()){return Rn(e,".snoutdata","auth.json")}function On(e=Fe()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=wt(e);if(!_n(n))return null;try{let r=JSON.parse(An(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function z(e,t=Fe()){let n=wt(t);Tn(yt(n),{recursive:!0}),$n(n,`${JSON.stringify(e,null,2)}
3
+ `,{mode:384}),Os(n,384)}function Nn(e=Fe()){Ns(wt(e),{force:!0})}function Dn(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function In(e){return Rn(e,".snoutdata","project.json")}function js(e){let t=Is(e),n=Ds(t).root;for(;;){let r=In(t);if(_n(r))try{let o=JSON.parse(An(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=yt(t)}}function Be(e,t){let n=In(e);return Tn(yt(n),{recursive:!0}),$n(n,`${JSON.stringify(t,null,2)}
4
+ `),n}function He(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:js(e.cwd??process.cwd())?.ref??null}var pe=A(()=>{"use strict"});function qe(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 Cs){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 ge(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 jn(e){bt=e}function ne(){return(bt??qe({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function he(){return(bt??qe({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var Cs,bt,ye=A(()=>{"use strict";Cs=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];bt=null});import{existsSync as Ms,readFileSync as Ls}from"node:fs";import{homedir as St,hostname as Us}from"node:os";import{join as we}from"node:path";function Fs(e,t,n){if(t==="win32"){let r=n.APPDATA??we(St(),"AppData","Roaming");return we(r,e)}return t==="darwin"?we(St(),"Library","Application Support",e):we(n.XDG_CONFIG_HOME??we(St(),".config"),e)}function Mn(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>we(Fs(n,e,t),"mcp.json"))}function Bs(e=process.platform,t=process.env){return Mn(e,t)[0]}function Hs(e=Bs()){if(!Ms(e))return null;try{let t=JSON.parse(Ls(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function qs(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function Ke(e){if(e!==void 0)return Cn(e);let t="no-config";for(let n of Mn()){let r=Hs(n);if(!r)continue;let o=await Cn(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function Cn(e){let t=qs(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(Ks)});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 Ln(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??`the app refused (${n.status})`}}function We(){return Us()}async function xt(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??"the app refused");return s.result}async function Un(e){return await xt(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await xt(e,"tools/list",{},3e3)).tools??[]}async function Fn(e,t,n){return xt(e,"tools/call",{name:t,arguments:n},12e4)}var Ks,Je=A(()=>{"use strict";Ks=1500});function E(e,t,n){throw new q(e,t,n)}function Ge(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 Hn(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(Ws.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 re,q,Ws,oe=A(()=>{"use strict";re={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},q=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return re[this.code]}};Ws=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});async function qn(e,t){let n=await fetch(`${V}/functions/v1/${e}`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return{status:n.status,body:r}}async function Ve(e={}){let{status:t,body:n}=await qn("cloud-device-start",{client:e});return t===429&&E("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&E("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)&&E("server",n.error??`could not start a pairing (${t})`),n}async function ze(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&E("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 qn("cloud-device-poll",{secret:e});switch(o===404&&E("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&E("usage",s.error??"that is not a pairing secret"),o!==200&&E("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":E("forbidden",s.error??"that pairing was refused in the browser");case"used":E("conflict",s.error??"that pairing has already been completed");case"expired":E("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 kt=A(()=>{"use strict";M();oe()});function _e(e){Ye=e}function Wn(e){Kn=e}function b(e){return Jn?`\x1B[2m${e}\x1B[0m`:e}function P(e){return Jn?`\x1B[1m${e}\x1B[0m`:e}function h(e,t){if(Ye){process.stdout.write(`${JSON.stringify(e,null,2)}
5
+ `);return}t()}function l(e){!Ye&&!Kn&&process.stderr.write(`${e}
6
+ `)}function ue(e,t,n){process.stderr.write(`${t}
7
7
  `),Ye&&process.stdout.write(`${JSON.stringify({ok:!1,code:e,error:t,...n},null,2)}
8
8
  `)}function K(e){process.stderr.write(`${e}
9
- `)}function L(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 X(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[d,u]of o){if(s<d)break;s=Math.round(s/d),a=u}let i=`${s} ${a}${s===1?"":"s"}`;return n?`in ${i}`:`${i} ago`}var Ye,Un,Bn,F=A(()=>{"use strict";Ye=!1,Un=!1;Bn=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as Kn,randomBytes as xt}from"node:crypto";import{createServer as Is}from"node:http";import{createInterface as js}from"node:readline/promises";import{spawn as Cs}from"node:child_process";function Xe(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Hn(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.":Zs(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
9
+ `)}function j(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 X(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[d,u]of o){if(s<d)break;s=Math.round(s/d),a=u}let i=`${s} ${a}${s===1?"":"s"}`;return n?`in ${i}`:`${i} ago`}var Ye,Kn,Jn,F=A(()=>{"use strict";Ye=!1,Kn=!1;Jn=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as zn,randomBytes as vt}from"node:crypto";import{createServer as Gs}from"node:http";import{createInterface as Vs}from"node:readline/promises";import{spawn as zs}from"node:child_process";function Xe(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Gn(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.":ua(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,11 +19,25 @@ var ps=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var bn=(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 vt(){let e=Xe(xt(32)),t=Xe(Kn("sha256").update(e).digest());return{verifier:e,challenge:t}}function Ms(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 Ls(e){let{command:t,args:n,verbatim:r}=Ms(process.platform,e),o=Cs(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function Hs(e){let t=e??Xe(xt(32));return{raw:t,hashed:Kn("sha256").update(t).digest("hex")}}function qs(e){return`${Us}?${new URLSearchParams({client_id:Wn,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 Ks(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 Pt(e){return new Promise((t,n)=>{let r="",o=Is((a,i)=>{let d=Ks(a.url??"/",e.state);if(d.kind==="ignore"){i.writeHead(404).end();return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(d.kind==="ok"?Hn("ok"):Hn("failed",d.message)),o.close(),d.kind==="ok"?t({code:d.code,redirectUri:r}):n(new Error(d.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(d=>{process.stdout.write(`${d}
23
- `),e.noBrowser?l("Open that in a browser, on any machine, and sign in."):(l("Opening that in your browser. If nothing opens, paste it into one."),Ls(d))},d=>{o.close(),n(d)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Jn(e,t){let n=await fetch(`${G}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:Y,"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 Vn(await n.json())}async function Ws(e,t,n){let r=await fetch(Fs,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Wn,client_secret:Bs,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 Js(e){let{verifier:t,challenge:n}=vt(),{raw:r,hashed:o}=Hs(),s=Xe(xt(16)),{code:a,redirectUri:i}=await Pt({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:m=>qs({redirectUri:m,challenge:n,hashedNonce:o,state:s})}),d=await Ws(a,t,i),u=await fetch(`${G}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:d,nonce:r})});if(!u.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await u.text()).slice(0,200)}`);return Vn(await u.json())}function Vn(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return z(n),n}function qn(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 Vs(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 Gs(e,t,n){let r=Vs(t);return e===404||/no sso provider/i.test(r)?new q("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 q("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new q(Ve(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function zs(e){let t=await fetch(`${G}/auth/v1/sso`,{method:"POST",headers:{apikey:Y,"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 Gs(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new q("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 Ys(e){if(e!==void 0){let r=qn(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!te()){let r=he();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${ge(r)}`:""}`)}let t=js({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=qn(o);if(s)return s;l("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 Xs(e){let t=await Ys(e.domain),{verifier:n,challenge:r}=vt();l(`Signing in with the identity provider for ${T(t)}.`);let{code:o}=await Pt({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>zs({domain:t,redirectTo:s,challenge:r})});return Jn(o,n)}async function Ze(e={}){if(e.sso)return Xs(e);let t=e.provider??"google";if(t==="google")return Js(e);let{verifier:n,challenge:r}=vt(),{code:o}=await Pt({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${G}/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 Jn(o,n)}function Zs(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var Us,Fs,Wn,Bs,Et=A(()=>{"use strict";M();le();pe();re();ye();F();Us="https://accounts.google.com/o/oauth2/v2/auth",Fs="https://oauth2.googleapis.com/token",Wn="1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com",Bs="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"});import{createInterface as Qs}from"node:readline/promises";async function _t(e){let t=Qs({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 ea(){let e=await Ke();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&l(x("The SnoutData app is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(l(""),l(`The SnoutData app is open here, signed in as ${T(t)}.`),l(x("It can create an access token for this terminal. Your sign-in itself is not handed over,")),l(x("and you can revoke the token later with `snoutdata tokens revoke`.")),!await _t("Use it?"))return null;l("Asking the app. Approve it there.");let n=await Dn(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return l(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return z(r),l(`Signed in with "${n.name}".`),r}async function ta(){if(!await _t("Sign in with a browser?"))return null;try{let e=await Ze({});return l(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return l(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function na(){if(!await _t("Sign in by typing a code into a browser on another machine?"))return null;let e=await Ge({name:We(),platform:process.platform});l(""),l(` Open ${T(e.verifyUrl)}`),l(` Type ${T(e.code)}`),l(""),l(x("The code is typed rather than in the link on purpose: it is what proves the person")),l(x("at the browser is the person at this terminal. Waiting\u2026"));let t=await ze(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return z(n),l(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function Gn(){if(!te())return null;let e=await ea();if(e)return e;let t=await ta();return t||na()}var zn=A(()=>{"use strict";pe();Je();kt();ye();Et();F()});var ue={};bn(ue,{ANON_KEY:()=>Y,ApiError:()=>Z,FUNCTIONS:()=>Yn,NotSignedIn:()=>oe,SUPABASE_URL:()=>G,call:()=>S,connection:()=>W,exportStatus:()=>Se,listProjects:()=>V,listTeams:()=>Qe,requestExport:()=>Te,waitForReady:()=>Ae,whoami:()=>be});function ra(){let e=he();return e?`not signed in, and ${ge(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token from \`snoutdata tokens create\`.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function oa(){let e=_n();if(e||(e=await Gn()),!e)throw new oe;if(!An(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${G}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new oe;let n=await t.json(),r={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,email:e.email};return z(r),r.accessToken}async function S(e,t={},n="POST"){let r=await oa(),o=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",s=await fetch(`${Yn}/${e}${o}`,{method:n,headers:{Authorization:`Bearer ${r}`,apikey:Y,"Content-Type":"application/json"},body:n==="GET"?void 0:JSON.stringify(t)}),a=await s.text(),i;try{i=a?JSON.parse(a):{}}catch{throw new Z(s.status,a.slice(0,300)||s.statusText)}if(!s.ok){let d=typeof i=="object"&&i&&"error"in i?String(i.error):`${s.status} ${s.statusText}`;throw s.status===401?new oe(d):new Z(s.status,d)}return i}async function be(){return S("cloud-whoami")}async function V(){return S("cloud-project-list")}async function W(e){return S("cloud-project-connection",{ref:e})}async function Qe(){return S("cloud-team-list")}async function Te(e){return S("cloud-project-export",{ref:e})}async function Se(e){return S("cloud-project-export",{ref:e},"GET")}async function Ae(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await V(),o=r.find(s=>s.ref===e);if(!o)throw new Z(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new Z(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var G,Y,Yn,Z,oe,M=A(()=>{"use strict";pe();ye();zn();G=process.env.SNOUTDATA_SUPABASE_URL??"https://accounts.snoutdata.com",Y=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpmN2c2NW1iOGZhcmEiLCJyb2xlIjoiYW5vbiIsImlhdCI6MTc4OTUxNTUyNCwiZXhwIjoyMTA0ODc1NTI0fQ.TiFJbsmmELR8FIje8lbg-95UJFtoj7IwmC2PZtWPAk8",Yn=`${G}/functions/v1`,Z=class extends Error{status;constructor(t,n){super(n),this.status=t}},oe=class extends Error{constructor(t){super(t??ra())}}});function Xt(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function qr(e,t,n){let r="";for(;r.length<n;)r+=Xt(e,t);return r}var Yt,Hr,Kr=A(()=>{"use strict";Yt="0123456789abcdefghjkmnpqrstvwxyz",Hr="abcdefghjkmnpqrstvwxyz"});function Vr(e){return Xt(e,Wr)+qr(e,Yt,Jr-1)}function bi(e){return wi.test(e)}function J(e){if(!bi(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function D(e,t=Si){return J(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var Wr,Jr,wi,Si,se=A(()=>{"use strict";Kr();Wr=Hr,Jr=13,wi=new RegExp(`^[${Wr}][${Yt}]{${Jr-1}}$`);Si="db.snoutdata.com"});var Gr=A(()=>{"use strict";se()});function Yr(e,t){let n=zr(e,7),r=zr(t,24);return Math.max(2,Math.ceil(n*24/r))}function zr(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var Zt=A(()=>{"use strict"});function Xr(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var Qt=A(()=>{"use strict"});var qd,Zr=A(()=>{"use strict";se();qd=7*24});var Wd,Qr=A(()=>{"use strict";Wd=7*24*60*60*1e3});var ki,eo=A(()=>{"use strict";Qr();ki=60*60*1e3});var to=A(()=>{"use strict"});var no=A(()=>{"use strict"});var Qd,eu,ro=A(()=>{"use strict";Qd=2*1024*1024,eu=64*1024});var oo,so,xi,ao=A(()=>{"use strict";oo="/snoutfn",so="/_snoutpod/health",xi=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
22
+ </body></html>`}function Pt(){let e=Xe(vt(32)),t=Xe(zn("sha256").update(e).digest());return{verifier:e,challenge:t}}function Ys(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 Xs(e){let{command:t,args:n,verbatim:r}=Ys(process.platform,e),o=zs(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function ta(e){let t=e??Xe(vt(32));return{raw:t,hashed:zn("sha256").update(t).digest("hex")}}function na(e){return`${Zs}?${new URLSearchParams({client_id:Yn,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 ra(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 Et(e){return new Promise((t,n)=>{let r="",o=Gs((a,i)=>{let d=ra(a.url??"/",e.state);if(d.kind==="ignore"){i.writeHead(404).end();return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(d.kind==="ok"?Gn("ok"):Gn("failed",d.message)),o.close(),d.kind==="ok"?t({code:d.code,redirectUri:r}):n(new Error(d.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(d=>{process.stdout.write(`${d}
23
+ `),e.noBrowser?l("Open that in a browser, on any machine, and sign in."):(l("Opening that in your browser. If nothing opens, paste it into one."),Xs(d))},d=>{o.close(),n(d)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Xn(e,t){let n=await fetch(`${V}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:Y,"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 Zn(await n.json())}async function oa(e,t,n){let r=await fetch(Qs,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Yn,client_secret:ea,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 sa(e){let{verifier:t,challenge:n}=Pt(),{raw:r,hashed:o}=ta(),s=Xe(vt(16)),{code:a,redirectUri:i}=await Et({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:m=>na({redirectUri:m,challenge:n,hashedNonce:o,state:s})}),d=await oa(a,t,i),u=await fetch(`${V}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:d,nonce:r})});if(!u.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await u.text()).slice(0,200)}`);return Zn(await u.json())}function Zn(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return z(n),n}function Vn(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 aa(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 ia(e,t,n){let r=aa(t);return e===404||/no sso provider/i.test(r)?new q("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 q("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new q(Ge(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function ca(e){let t=await fetch(`${V}/auth/v1/sso`,{method:"POST",headers:{apikey:Y,"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 ia(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new q("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 la(e){if(e!==void 0){let r=Vn(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!ne()){let r=he();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${ge(r)}`:""}`)}let t=Vs({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=Vn(o);if(s)return s;l("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 da(e){let t=await la(e.domain),{verifier:n,challenge:r}=Pt();l(`Signing in with the identity provider for ${P(t)}.`);let{code:o}=await Et({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>ca({domain:t,redirectTo:s,challenge:r})});return Xn(o,n)}async function Ze(e={}){if(e.sso)return da(e);let t=e.provider??"google";if(t==="google")return sa(e);let{verifier:n,challenge:r}=Pt(),{code:o}=await Et({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${V}/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 Xn(o,n)}function ua(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var Zs,Qs,Yn,ea,_t=A(()=>{"use strict";M();te();pe();oe();ye();F();Zs="https://accounts.google.com/o/oauth2/v2/auth",Qs="https://oauth2.googleapis.com/token",Yn="1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com",ea="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"});import{createInterface as ma}from"node:readline/promises";async function Tt(e){let t=ma({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 fa(){let e=await Ke();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&l(b("The SnoutData app is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(l(""),l(`The SnoutData app is open here, signed in as ${P(t)}.`),l(b("It can create an access token for this terminal. Your sign-in itself is not handed over,")),l(b("and you can revoke the token later with `snoutdata tokens revoke`.")),!await Tt("Use it?"))return null;l("Asking the app. Approve it there.");let n=await Ln(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return l(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return z(r),l(`Signed in with "${n.name}".`),r}async function pa(){if(!await Tt("Sign in with a browser?"))return null;try{let e=await Ze({});return l(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return l(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function ga(){if(!await Tt("Sign in by typing a code into a browser on another machine?"))return null;let e=await Ve({name:We(),platform:process.platform});l(""),l(` Open ${P(e.verifyUrl)}`),l(` Type ${P(e.code)}`),l(""),l(b("The code is typed rather than in the link on purpose: it is what proves the person")),l(b("at the browser is the person at this terminal. Waiting\u2026"));let t=await ze(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return z(n),l(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function Qn(){if(!ne())return null;let e=await fa();if(e)return e;let t=await pa();return t||ga()}var er=A(()=>{"use strict";pe();Je();kt();ye();_t();F()});var ae={};Pn(ae,{ANON_KEY:()=>Y,ApiError:()=>Z,FUNCTIONS:()=>tr,NotSignedIn:()=>se,SUPABASE_URL:()=>V,call:()=>w,connection:()=>W,exportStatus:()=>Se,listProjects:()=>G,listTeams:()=>Qe,requestExport:()=>Te,waitForReady:()=>Ae,whoami:()=>be});function ha(){let e=he();return e?`not signed in, and ${ge(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token from \`snoutdata tokens create\`.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function ya(){let e=On();if(e||(e=await Qn()),!e)throw new se;if(!Dn(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${V}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:Y,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new se;let n=await t.json(),r={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,email:e.email};return z(r),r.accessToken}async function w(e,t={},n="POST"){let r=await ya(),o=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",s=await fetch(`${tr}/${e}${o}`,{method:n,headers:{Authorization:`Bearer ${r}`,apikey:Y,"Content-Type":"application/json"},body:n==="GET"?void 0:JSON.stringify(t)}),a=await s.text(),i;try{i=a?JSON.parse(a):{}}catch{throw new Z(s.status,a.slice(0,300)||s.statusText)}if(!s.ok){let d=typeof i=="object"&&i&&"error"in i?String(i.error):`${s.status} ${s.statusText}`;throw s.status===401?new se(d):new Z(s.status,d)}return i}async function be(){return w("cloud-whoami")}async function G(){return w("cloud-project-list")}async function W(e){return w("cloud-project-connection",{ref:e})}async function Qe(){return w("cloud-team-list")}async function Te(e){return w("cloud-project-export",{ref:e})}async function Se(e){return w("cloud-project-export",{ref:e},"GET")}async function Ae(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await G(),o=r.find(s=>s.ref===e);if(!o)throw new Z(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new Z(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var V,Y,tr,Z,se,M=A(()=>{"use strict";pe();ye();er();V=process.env.SNOUTDATA_SUPABASE_URL??"https://accounts.snoutdata.com",Y=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpmN2c2NW1iOGZhcmEiLCJyb2xlIjoiYW5vbiIsImlhdCI6MTc4OTUxNTUyNCwiZXhwIjoyMTA0ODc1NTI0fQ.TiFJbsmmELR8FIje8lbg-95UJFtoj7IwmC2PZtWPAk8",tr=`${V}/functions/v1`,Z=class extends Error{status;constructor(t,n){super(n),this.status=t}},se=class extends Error{constructor(t){super(t??ha())}}});function en(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function Zr(e,t,n){let r="";for(;r.length<n;)r+=en(e,t);return r}var Qt,Xr,Qr=A(()=>{"use strict";Qt="0123456789abcdefghjkmnpqrstvwxyz",Xr="abcdefghjkmnpqrstvwxyz"});function no(e){return en(e,eo)+Zr(e,Qt,to-1)}function Ci(e){return ji.test(e)}function J(e){if(!Ci(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function D(e,t=Mi){return J(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var eo,to,ji,Mi,ie=A(()=>{"use strict";Qr();eo=Xr,to=13,ji=new RegExp(`^[${eo}][${Qt}]{${to-1}}$`);Mi="db.snoutdata.com"});var ro=A(()=>{"use strict";ie()});function so(e,t){let n=oo(e,7),r=oo(t,24);return Math.max(2,Math.ceil(n*24/r))}function oo(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var tn=A(()=>{"use strict"});function ao(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var nn=A(()=>{"use strict"});var du,io=A(()=>{"use strict";ie();du=7*24});var mu,co=A(()=>{"use strict";mu=7*24*60*60*1e3});var Li,lo=A(()=>{"use strict";co();Li=60*60*1e3});var uo=A(()=>{"use strict"});var at,mo,bu,fo=A(()=>{"use strict";at="snoutdata_realtime",mo="supabase_realtime",bu=`do $snoutpod$
24
+ begin
25
+ if exists (select 1 from pg_publication where pubname = '${at}') then
26
+ return;
27
+ end if;
28
+ if exists (select 1 from pg_publication where pubname = '${mo}') then
29
+ alter publication ${mo} rename to ${at};
30
+ return;
31
+ end if;
32
+ create publication ${at};
33
+ execute format('alter publication ${at} owner to %I',
34
+ (select pg_get_userbyid(datdba) from pg_database where datname = current_database()));
35
+ end
36
+ $snoutpod$;
37
+ `});var xu,ku,po=A(()=>{"use strict";xu=2*1024*1024,ku=64*1024});var go,ho,Ui,yo=A(()=>{"use strict";go="/snoutfn",ho="/_snoutpod/health",Ui=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
24
38
  // every time the agent starts, from packages/snoutpod/src/host/functionMain.ts.
25
- const MOUNT = ${JSON.stringify(oo)};
26
- const HEALTH = ${JSON.stringify(so)};
39
+ const MOUNT = ${JSON.stringify(go)};
40
+ const HEALTH = ${JSON.stringify(ho)};
27
41
 
28
42
  /**
29
43
  * Manifests, cached by the file's modification time.
@@ -142,59 +156,87 @@ Deno.serve(async (req) => {
142
156
  return refuse(500, 'This function could not be run.', detail);
143
157
  }
144
158
  });
145
- `});function en(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function at(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 Pi(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 Pi(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(en("the subnet",n,io),en("the sink",r,vi),!lo(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!lo(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",`${en("a denied prefix",s,io)},${r}`);return o.push(e.name),o}function co(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function lo(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(co(e)&s)>>>0===(co(n??"")&s)>>>0}function uo(e){return e?.internal===!0}var io,vi,tn=A(()=>{"use strict";io=/^((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])$/,vi=/^((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 Ei,it=A(()=>{"use strict";Ei=7*24*60*60});var mo=A(()=>{"use strict";se();ro();ao();tn();it()});function po(e){let t=[];for(let n of e.split(`
146
- `)){let r=n.trim();if(!r)continue;let[o,s,a,i]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:i?Number(i):null})}return t}var fo,nn=A(()=>{"use strict";fo="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var xu,vu,go=A(()=>{"use strict";se();it();xu=6*60*60,vu=5*60});var ho=A(()=>{"use strict"});var yo,nm,rm,om,sm,wo=A(()=>{"use strict";Gr();Zt();Qt();Zr();eo();to();no();mo();nn();go();ho();it();yo=120,nm=15*60*1e3,rm=60*60*1e3,om=60*60*1e3,sm=10*60*1e3});import{execFile as Ti}from"node:child_process";function bo(){return(e,t,n={})=>new Promise((r,o)=>{let s=Ti(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,i,d)=>{if(a&&typeof a.code=="string"){o(a);return}let u=a&&typeof a.code=="number"?a.code:0;r({code:u,stdout:String(i),stderr:String(d)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function O(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
147
- `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var ct=A(()=>{"use strict"});import{readdir as Ai,readFile as dm}from"node:fs/promises";async function So(e,t,n={}){let r=n.binary??"podman",o=D(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),d=(Array.isArray(a)?a[0]:a)?.Mountpoint;return d?await(n.look??$i)(d)!=="empty":!1}async function $i(e){try{return(await Ai(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var ko=A(()=>{"use strict";se();ct()});function xo(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 Eo(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function _o(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=${xo("the access key",e.key,Oi)}`),n.push("--env",vo),e.token&&n.push("--env",Po)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${xo("the endpoint",e.endpoint,Ri)}`),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 To(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{[vo]:e.secret,...e.token?{[Po]:e.token}:{}}}var vo,Po,Ri,Oi,Ao=A(()=>{"use strict";vo="SNOUTPOD_S3_SECRET",Po="SNOUTPOD_S3_TOKEN",Ri=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,Oi=/^[A-Za-z0-9/+=_.-]{1,256}$/});import{createHash as Ni,randomBytes as Di}from"node:crypto";import{access as Ii,constants as ji}from"node:fs/promises";import{delimiter as Ci,join as Mi}from"node:path";function an(e){if(!Ui.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function Ie(e){if(!Fi.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function Bi(e,t,n){return`postgres://${Ie(t)}:${n}@127.0.0.1:5432/${J(e)}`}function $o(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=n.get(t.dbUriRole);if(s===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]=Bi(e.ref,t.dbUriRole,s)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),o}function Ro(e,t){return`${D(e).pod}-${an(t)}`}function lt(e){return`snoutpod-host-${an(e)}`}function Hi(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return Ni("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 qi(e,t){let n=[],r=on(t);for(let o of e??[])if(an(o.name),sn(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){sn(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function on(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 sn(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function No(e){let t=[];for(let n of Object.keys(e)){if(!Ki.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function Wi(e){let t=e.healthPath;if(t==null||t==="")return[];if(!jo.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(sn(e.hostPort,`${e.name}: hostPort`),["--label",`${cn}=${e.hostPort}:${t}`])}function Ji(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||!jo.test(r)?null:{port:n,path:r}}async function Gi(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>Ii(o,ji.X_OK));for(let o of n.split(Ci))if(o)try{let s=Mi(o,e);return await r(s),s}catch{}return null}function zi(){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(`
148
- `)}function Xi(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:Do;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,Do)}`),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 Zi(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??Vi;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await Gi(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function Mo(e){let t=e.binary??"podman",n=e.exec,r=Zi(e),o=e.probe??Co;async function s(c){try{await n(t,["exec","--interactive",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"--quiet","--file","-"],{timeoutMs:Yi,stdin:zi()})}catch{}}async function a(){let c=await O(n,t,["ps","--all","--filter",`label=${ve}`,"--format","json"]),f=c.trim()?JSON.parse(c):[],p=new Map;for(let w of f){let g=w.Labels?.[ve],E=w.Labels?.[Ne];if(!g||!E)continue;let I=p.get(g)??[];I.push(E),p.set(g,I)}let k=[];for(let w of f){let g=w.Labels?.[ve];if(!g||w.Labels?.[Ne])continue;let E=Number(w.Labels?.[rn]);k.push({ref:g,status:Qi(w.State),generation:Number.isInteger(E)?E:null,hasData:await e.hasData(g),services:p.get(g)??[]})}return k}async function i(c){let f=D(c.ref),p=e.credentials,k=await Eo(e.repository,p?()=>p.forProject(c.ref,c.bucket):null),w={SNOUTPOD_OWNER_PASSWORD:c.ownerPassword,POSTGRES_PASSWORD:c.adminPassword,...To(k)},g=[...await r(),"run","--detach","--name",f.pod,"--label",`${ve}=${c.ref}`,"--label",`${rn}=${c.generation}`,"--userns=auto","--memory",`${c.memoryMb}m`,...tc(c.cpus),"--restart","no",...e.network?["--network",e.network]:[],...c.port?on(e.podBind).flatMap(E=>["--publish",`${E}:${c.port}:5432`]):[],...qi(c.services,e.podBind),"--volume",`${f.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${c.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${c.bucket}`,"--env",`SNOUTPOD_S3_REGION=${c.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...Xi(c.tuning).flatMap(E=>["--env",E]),"--env",`SNOUTPOD_RETENTION_FULL=${Yr(c.backup?.retentionDays,c.backup?.fullIntervalHours)}`,..._o(k),c.image,"snoutpod-postgres"];await O(n,t,g,{env:w})}async function d(c){let f=await O(n,t,["ps","--all","--filter",`label=${ve}=${J(c)}`,"--filter",`label=${Ne}`,"--format","json"]),p=f.trim()?JSON.parse(f):[],k=[];for(let w of p){let g=w.Labels?.[Ne];g&&k.push({name:g,container:Ro(c,g)})}return k}async function u(c){let f=[];for(let E of c.services??[])E.dbUriEnv&&E.dbUriRole&&!f.includes(E.dbUriRole)&&f.push(Ie(E.dbUriRole));let p=new Map;if(f.length===0)return p;let k={},w=[],g=[];return f.forEach((E,I)=>{let R=Di(24).toString("base64url"),ie=`SNOUTPOD_SERVICE_PASSWORD_${I}`;p.set(E,R),k[ie]=R,w.push("--env",ie),g.push(`\\getenv service_password_${I} ${ie}`),g.push(`\\set service_role_${I} ${E}`),g.push(`alter role :"service_role_${I}" with password :'service_password_${I}';`)}),g.push(""),await O(n,t,["exec","--interactive",...w,D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:k,stdin:g.join(`
149
- `)}),p}async function m(c){for(let k of await d(c.ref))await O(n,t,["rm","--force","--ignore",k.container]);let f=await u(c),p;(c.services??[]).some(k=>k.smtpSecretEnv)&&(p=await e.smtpSecret?.().catch(()=>{}));for(let k of c.services??[]){let w=Ro(c.ref,k.name),g=[...await r(),"run","--detach","--name",w,"--label",`${ve}=${c.ref}`,"--label",`${rn}=${c.generation}`,"--label",`${Ne}=${k.name}`,"--network",`container:${D(c.ref).pod}`,"--userns",`container:${D(c.ref).pod}`,"--memory",`${k.memoryMb}m`,"--restart","no",...Wi(k),...No($o(c,k,f,p)),k.image];await O(n,t,g,{env:$o(c,k,f,p)})}}return{list:a,create:i,createServices:m,async start(c){await O(n,t,["start",D(c).pod])},async startServices(c){for(let f of await d(c))await O(n,t,["start",f.container])},async applyHostServices(c){let f=[],p=await O(n,t,["ps","--all","--filter",`label=${De}`,"--format","json"]),k=p.trim()?JSON.parse(p):[],w=new Map;for(let E of k){let I=E.Labels?.[De];I&&w.set(I,{spec:E.Labels?.[Oo]??"",state:E.State??""})}for(let E of c)try{await g(E)}catch(I){f.push({name:E.name,action:"failed",reason:I instanceof Error?I.message:String(I)})}async function g(E){let I=e.hostServiceLocal?.(E,c),R=I?{...E,...I.env&&Object.keys(I.env).length>0?{env:{...E.env,...I.env}}:{},...I.volumes?{volumes:I.volumes}:{},...I.network?{network:I.network}:{}}:E,ie=lt(R.name),wn=Hi(R),ce=w.get(R.name);if(w.delete(R.name),ce&&ce.spec===wn){ce.state!=="running"&&(await O(n,t,["start",ie]),f.push({name:R.name,action:"started",reason:ce.state}));return}let fs=ce?"the spec changed":"it was not here";ce&&await O(n,t,["rm","--force","--ignore",ie]),await O(n,t,[...await r(),"run","--detach","--name",ie,"--label",`${De}=${R.name}`,"--label",`${Oo}=${wn}`,...R.healthPath?["--label",`${cn}=${R.hostPort}:${R.healthPath}`]:[],"--memory",`${R.memoryMb}m`,"--restart","no",...R.network?["--network",R.network]:e.network?["--network",e.network]:[],...(R.volumes??[]).flatMap(pt=>["--volume",pt]),...on(e.podBind).flatMap(pt=>["--publish",`${pt}:${R.hostPort}:${R.port}`]),...R.adminPort&&R.adminHostPort?["--publish",`127.0.0.1:${R.adminHostPort}:${R.adminPort}`]:[],...No(R.env),R.image,...R.command??[]],{env:{...R.env}}),f.push({name:R.name,action:ce?"recreated":"created",reason:fs})}for(let E of w.keys())await O(n,t,["rm","--force","--ignore",lt(E)]),f.push({name:E,action:"removed",reason:"it is no longer on the list"});return f},async hostServiceHealth(c){let f=await O(n,t,["ps","--all","--filter",`label=${De}`,"--format","json"]),p=(f.trim()?JSON.parse(f):[]).map(w=>w.Labels?.[De]??"").filter(w=>w!=="");if(p.length===0)return[];let k=Date.now()+c;for(;;){let w=[];for(let g of p)w.push(await Io(n,t,g,lt(g),o));if(w.every(g=>g.ok)||Date.now()>=k)return w;await new Promise(g=>setTimeout(g,500))}},async serviceHealth(c,f){let p=await d(c);if(p.length===0)return[];let k=Date.now()+f;for(;;){let w=[];for(let g of p)w.push(await Io(n,t,g.name,g.container,o));if(w.every(g=>g.ok)||Date.now()>=k)return w;await new Promise(g=>setTimeout(g,500))}},async stop(c,f){for(let p of await d(c))await O(n,t,["stop","--time",String(Li),p.container]);await O(n,t,["stop","--time",String(f),D(c).pod])},async remove(c){for(let f of await d(c))await O(n,t,["rm","--force","--ignore",f.container]);await O(n,t,["rm","--force","--ignore",D(c).pod])},async removeData(c){await O(n,t,["volume","rm","--force",D(c).pod])},async applyOwnerPassword(c){let f=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${c.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
150
- `);await O(n,t,["exec","--interactive","--env","SNOUTPOD_OWNER_PASSWORD",D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:c.ownerPassword},stdin:f})},async applyRolePassword(c,f,p){let k=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${Ie(f)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
151
- `);await O(n,t,["exec","--interactive","--env","SNOUTPOD_ROLE_PASSWORD",D(c).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:p},stdin:k})},async ensureHostServiceDatabase(c,f,p){let k=lt(c),w=Ie(f),g=Ie(p),E=R=>["exec",k,"psql","--username",w,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",R];(await O(n,t,E(`select 1 from pg_database where datname = '${g}'`))).trim()!=="1"&&await O(n,t,E(`create database "${g}"`))},async replicationSlots(c){let f=await O(n,t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",fo]);return po(f)},async dropReplicationSlot(c,f){if(!/^[a-z0-9_]{1,63}$/.test(f))throw new Error(`not a replication slot name: ${JSON.stringify(f)}`);await O(n,t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-v","ON_ERROR_STOP=1","-c",`select pg_drop_replication_slot('${f}')`])},async renameProject(c,f){f!==c.ref&&await O(n,t,["exec","--interactive",D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:ec(f,c.ref)})},async waitReady(c,f){let p=D(c).pod,k=Date.now()+f;for(;;){let w=await n(t,["exec",p,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(w.code===0&&w.stdout.trim()==="t")return s(c),!0;if(Date.now()>=k)return!1;await new Promise(g=>setTimeout(g,500))}},async capacity(){let c=await a(),f=await e.capacity();return{pods:c.length,...f}},async databaseBytes(c){let f=await n(t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tAc","select pg_database_size(current_database())"]);return f.code===0?Xr(f.stdout):null},async setReadOnly(c,f){let p=D(c);await O(n,t,["exec",p.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${p.database} set default_transaction_read_only = ${f?"on":"off"}`])},async archiveState(c){let f=await n(t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), failed_count from pg_stat_archiver`]);if(f.code!==0)return null;let[p="",k=""]=f.stdout.trim().split(`
152
- `)[0]?.split("|")??[],w=Number(k);return{lastArchivedAt:p.trim()||null,failures:Number.isInteger(w)&&w>=0?w:0}}}}async function Io(e,t,n,r,o=Co){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,i;try{let c=JSON.parse(s.stdout);a=c[0]?.State,i=c[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let d=a?.Status??"unknown";if(d!=="running")return{name:n,ok:!1,detail:d};let u=Ji(i?.[cn]);if(!u)return{name:n,ok:!0,detail:"running"};let m=await o(u.port,u.path);return m===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${m}`}}function Qi(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function ec(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(`
153
- `)}function tc(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var ve,rn,Ne,Li,Ui,Fi,De,Oo,Ki,jo,cn,Co,Vi,Yi,Do,Lo=A(()=>{"use strict";se();Zt();nn();ct();Ao();Qt();ve="snoutpod.ref",rn="snoutpod.generation",Ne="snoutpod.service",Li=10,Ui=/^[a-z][a-z0-9-]{0,30}$/;Fi=/^[a-z][a-z0-9_]{0,62}$/;De="snoutpod.host-service",Oo="snoutpod.host-service-spec";Ki=/^[A-Za-z_][A-Za-z0-9_]*$/;jo=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,cn="snoutpod.health";Co=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()})},Vi="crun";Yi=3e4,Do=200});var Go={};bn(Go,{DEFAULT_LOCAL_MEMORY_MB:()=>Bo,DEFAULT_LOCAL_PORT:()=>sc,ENGINE:()=>C,ENGINE_NAME:()=>un,IMAGE:()=>fe,NETWORK:()=>ae,NO_BLACKHOLE_ROUTES:()=>qo,PUBLIC_IMAGE:()=>Pe,capacity:()=>Ho,ensureImage:()=>lc,ensureNetwork:()=>pc,exec:()=>H,hasData:()=>dt,localPsql:()=>yc,localRuntime:()=>fc,localSpec:()=>Jo,localStatus:()=>mn,localUri:()=>Wo,networkPolicyInPlace:()=>Ko,newLocalPassword:()=>ic,newLocalRef:()=>ac,podLog:()=>Vo,podName:()=>ut,podmanReady:()=>uc,removeLocal:()=>Sc,startLocal:()=>hc,stopLocal:()=>bc});import{randomBytes as Fo}from"node:crypto";import{existsSync as Uo}from"node:fs";import{platform as dn}from"node:os";import{delimiter as nc,join as rc}from"node:path";function oc(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=dn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(nc))if(t&&e.some(n=>Uo(rc(t,n))))return"podman";return Uo("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function ac(){return Vr(e=>Fo(e))}function ic(){return Fo(18).toString("base64url")}async function lc(e,t=H){if(process.env.SNOUTPOD_IMAGE||(await t(C,["image","exists",fe])).code===0)return null;e?.(`Fetching the database image (${Pe}). This happens once.`);let n=await t(C,["pull",Pe],{timeoutMs:dc}).catch(o=>({code:1,stdout:"",stderr:o instanceof Error?o.message:String(o)}));if(n.code!==0)return`could not fetch the database image ${Pe}: ${n.stderr.trim()||n.stdout.trim()||"the pull failed"}`;let r=await t(C,["tag",Pe,fe]);return r.code!==0?`fetched ${Pe} but could not tag it as ${fe}: ${r.stderr.trim()}`:(e?.("Got it."),null)}async function uc(e=cc){if(un!=="podman")return`SNOUTPOD_ENGINE=${un}: 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 H(C,["--version"])}catch{return`${C} 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`${C} --version failed: ${t.stderr.trim()||t.stdout.trim()}`;if((await H(C,["image","exists",fe])).code!==0)return`the pod image ${fe} is not on this machine. ${e}`;let r=await H(C,["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 mc(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 H(C,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
154
- `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function dt(e){let t=dn()==="darwin"||dn()==="win32";return So(H,e,{binary:C,look:t?mc:void 0})}async function Ho(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function fc(e){return Mo({exec:H,binary:C,network:ae,hasData:dt,capacity:Ho,repository:e})}function ut(e){return D(e).pod}async function pc(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await gc()||await H(C,at({name:ae}));return}let t=at({name:ae,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await Ko())return;await e?.(),await H(C,["network","rm","--force",ae]).catch(()=>{});let o=await H(C,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${qo}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${ae} with egress blocked${n?`: ${n}`:""}`)}async function gc(){return(await H(C,["network","inspect",ae,"--format","json"])).code===0}async function Ko(){let e=await H(C,["network","inspect",ae,"--format","json"]);if(e.code!==0)return!1;try{return uo(JSON.parse(e.stdout)?.[0])}catch{return!1}}function Wo(e){let t=D(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function Jo(e,t={}){return J(e.ref),{ref:e.ref,generation:1,image:t.image??fe,memoryMb:t.memoryMb??Bo,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 ln(e,t){e.onProgress?.(t)}async function hc(e,t,n={}){let r=Jo(t,n),o=n.timeoutMs??12e4,s=n.hasData??dt,a=(await e.list()).find(i=>i.ref===t.ref);if(a?a.status==="stopped"?(ln(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):ln(n,"The database is already running."):(ln(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 wc(t.ref,o,n.podLog??Vo));return await e.applyOwnerPassword(r),await e.createServices(r),mn(e,t,n)}async function yc(e,t){let n=D(e.ref);return H(C,["exec","--interactive","-e","PGPASSWORD",ut(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 Vo(e){let t=await H(C,["logs","--tail","20",ut(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function wc(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${ut(e)} never became ready in ${t}ms
159
+ `});function rn(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function it(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 Bi(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 Bi(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(rn("the subnet",n,wo),rn("the sink",r,Fi),!So(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!So(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",`${rn("a denied prefix",s,wo)},${r}`);return o.push(e.name),o}function bo(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function So(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(bo(e)&s)>>>0===(bo(n??"")&s)>>>0}function xo(e){return e?.internal===!0}var wo,Fi,on=A(()=>{"use strict";wo=/^((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])$/,Fi=/^((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 Hi,ct=A(()=>{"use strict";Hi=7*24*60*60});var ko=A(()=>{"use strict";ie();po();yo();on();ct()});function Po(e){let t=[];for(let n of e.split(`
160
+ `)){let r=n.trim();if(!r)continue;let[o,s,a,i]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:i?Number(i):null})}return t}var vo,sn=A(()=>{"use strict";vo="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var Ku,Wu,Eo=A(()=>{"use strict";ie();ct();Ku=6*60*60,Wu=5*60});var _o=A(()=>{"use strict"});var To,Pm,Em,_m,Tm,Ao=A(()=>{"use strict";ro();tn();nn();io();lo();uo();fo();ko();sn();Eo();_o();ct();To=120,Pm=15*60*1e3,Em=60*60*1e3,_m=60*60*1e3,Tm=10*60*1e3});import{execFile as Ki}from"node:child_process";function $o(){return(e,t,n={})=>new Promise((r,o)=>{let s=Ki(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,i,d)=>{if(a&&typeof a.code=="string"){o(a);return}let u=a&&typeof a.code=="number"?a.code:0;r({code:u,stdout:String(i),stderr:String(d)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function R(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
161
+ `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var lt=A(()=>{"use strict"});import{readdir as Wi,readFile as Nm}from"node:fs/promises";async function Ro(e,t,n={}){let r=n.binary??"podman",o=D(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),d=(Array.isArray(a)?a[0]:a)?.Mountpoint;return d?await(n.look??Ji)(d)!=="empty":!1}async function Ji(e){try{return(await Wi(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var Oo=A(()=>{"use strict";ie();lt()});function No(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 jo(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function Co(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=${No("the access key",e.key,Vi)}`),n.push("--env",Do),e.token&&n.push("--env",Io)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${No("the endpoint",e.endpoint,Gi)}`),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 Mo(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{[Do]:e.secret,...e.token?{[Io]:e.token}:{}}}var Do,Io,Gi,Vi,Lo=A(()=>{"use strict";Do="SNOUTPOD_S3_SECRET",Io="SNOUTPOD_S3_TOKEN",Gi=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,Vi=/^[A-Za-z0-9/+=_.-]{1,256}$/});function Fo(e){let t=[];for(let n of e.split(`
162
+ `)){let r=n.split(cn);if(r.length!==4||!an.includes(r[0]))continue;let[o,s,a,i]=r;t.push(i?{name:o,from:s,error:i}:{name:o,from:s,to:a})}return t}var an,cn,Uo,Bo=A(()=>{"use strict";an=["snouttime"];for(let e of an)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);cn="",Uo=`
163
+ CREATE TEMP TABLE snoutpod_extension_updates (name text, from_version text, to_version text, error text);
164
+ DO $snoutpod$
165
+ DECLARE
166
+ r record;
167
+ BEGIN
168
+ FOR r IN
169
+ SELECT e.extname, e.extversion
170
+ FROM pg_extension e
171
+ JOIN pg_available_extensions a ON a.name = e.extname
172
+ WHERE e.extname = ANY (ARRAY[${an.map(e=>`'${e}'`).join(", ")}])
173
+ AND a.default_version IS DISTINCT FROM e.extversion
174
+ LOOP
175
+ BEGIN
176
+ EXECUTE format('ALTER EXTENSION %I UPDATE', r.extname);
177
+ INSERT INTO snoutpod_extension_updates
178
+ SELECT r.extname, r.extversion, extversion, NULL FROM pg_extension WHERE extname = r.extname;
179
+ EXCEPTION WHEN OTHERS THEN
180
+ INSERT INTO snoutpod_extension_updates
181
+ VALUES (r.extname, r.extversion, NULL, regexp_replace(SQLERRM, '\\s+', ' ', 'g'));
182
+ END;
183
+ END LOOP;
184
+ END
185
+ $snoutpod$;
186
+ SELECT name, from_version, coalesce(to_version, ''), coalesce(error, '') FROM snoutpod_extension_updates ORDER BY name;
187
+ `});import{createHash as zi,randomBytes as Yi}from"node:crypto";import{access as Xi,constants as Zi}from"node:fs/promises";import{delimiter as Qi,join as ec}from"node:path";function mn(e){if(!nc.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function Ie(e){if(!rc.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function oc(e,t,n){return`postgres://${Ie(t)}:${n}@127.0.0.1:5432/${J(e)}`}function Ho(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=n.get(t.dbUriRole);if(s===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]=oc(e.ref,t.dbUriRole,s)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),o}function qo(e,t){return`${D(e).pod}-${mn(t)}`}function dt(e){return`snoutpod-host-${mn(e)}`}function sc(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return zi("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 ac(e,t){let n=[],r=dn(t);for(let o of e??[])if(mn(o.name),un(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){un(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function dn(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 un(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function Wo(e){let t=[];for(let n of Object.keys(e)){if(!ic.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function cc(e){let t=e.healthPath;if(t==null||t==="")return[];if(!Vo.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(un(e.hostPort,`${e.name}: hostPort`),["--label",`${fn}=${e.hostPort}:${t}`])}function lc(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||!Vo.test(r)?null:{port:n,path:r}}async function uc(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>Xi(o,Zi.X_OK));for(let o of n.split(Qi))if(o)try{let s=ec(o,e);return await r(s),s}catch{}return null}function mc(){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(`
188
+ `)}function pc(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:Jo;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,Jo)}`),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 gc(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??dc;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await uc(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function Yo(e){let t=e.binary??"podman",n=e.exec,r=gc(e),o=e.probe??zo;async function s(c){try{await n(t,["exec","--interactive",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"--quiet","--file","-"],{timeoutMs:fc,stdin:mc()})}catch{}}async function a(){let c=await R(n,t,["ps","--all","--filter",`label=${ve}`,"--format","json"]),f=c.trim()?JSON.parse(c):[],p=new Map;for(let S of f){let g=S.Labels?.[ve],_=S.Labels?.[Ne];if(!g||!_)continue;let I=p.get(g)??[];I.push(_),p.set(g,I)}let k=[];for(let S of f){let g=S.Labels?.[ve];if(!g||S.Labels?.[Ne])continue;let _=Number(S.Labels?.[ln]);k.push({ref:g,status:hc(S.State),generation:Number.isInteger(_)?_:null,hasData:await e.hasData(g),services:p.get(g)??[]})}return k}async function i(c){let f=D(c.ref),p=e.credentials,k=await jo(e.repository,p?()=>p.forProject(c.ref,c.bucket):null),S={SNOUTPOD_OWNER_PASSWORD:c.ownerPassword,POSTGRES_PASSWORD:c.adminPassword,...Mo(k)},g=[...await r(),"run","--detach","--name",f.pod,"--label",`${ve}=${c.ref}`,"--label",`${ln}=${c.generation}`,"--userns=auto","--memory",`${c.memoryMb}m`,...wc(c.cpus),"--restart","no",...e.network?["--network",e.network]:[],...c.port?dn(e.podBind).flatMap(_=>["--publish",`${_}:${c.port}:5432`]):[],...ac(c.services,e.podBind),"--volume",`${f.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${c.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${c.bucket}`,"--env",`SNOUTPOD_S3_REGION=${c.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...pc(c.tuning).flatMap(_=>["--env",_]),"--env",`SNOUTPOD_RETENTION_FULL=${so(c.backup?.retentionDays,c.backup?.fullIntervalHours)}`,...Co(k),c.image,"snoutpod-postgres"];await R(n,t,g,{env:S})}async function d(c){let f=await R(n,t,["ps","--all","--filter",`label=${ve}=${J(c)}`,"--filter",`label=${Ne}`,"--format","json"]),p=f.trim()?JSON.parse(f):[],k=[];for(let S of p){let g=S.Labels?.[Ne];g&&k.push({name:g,container:qo(c,g)})}return k}async function u(c){let f=[];for(let _ of c.services??[])_.dbUriEnv&&_.dbUriRole&&!f.includes(_.dbUriRole)&&f.push(Ie(_.dbUriRole));let p=new Map;if(f.length===0)return p;let k={},S=[],g=[];return f.forEach((_,I)=>{let O=Yi(24).toString("base64url"),le=`SNOUTPOD_SERVICE_PASSWORD_${I}`;p.set(_,O),k[le]=O,S.push("--env",le),g.push(`\\getenv service_password_${I} ${le}`),g.push(`\\set service_role_${I} ${_}`),g.push(`alter role :"service_role_${I}" with password :'service_password_${I}';`)}),g.push(""),await R(n,t,["exec","--interactive",...S,D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:k,stdin:g.join(`
189
+ `)}),p}async function m(c){for(let k of await d(c.ref))await R(n,t,["rm","--force","--ignore",k.container]);let f=await u(c),p;(c.services??[]).some(k=>k.smtpSecretEnv)&&(p=await e.smtpSecret?.().catch(()=>{}));for(let k of c.services??[]){let S=qo(c.ref,k.name),g=[...await r(),"run","--detach","--name",S,"--label",`${ve}=${c.ref}`,"--label",`${ln}=${c.generation}`,"--label",`${Ne}=${k.name}`,"--network",`container:${D(c.ref).pod}`,"--userns",`container:${D(c.ref).pod}`,"--memory",`${k.memoryMb}m`,"--restart","no",...cc(k),...Wo(Ho(c,k,f,p)),k.image];await R(n,t,g,{env:Ho(c,k,f,p)})}}return{list:a,create:i,createServices:m,async start(c){await R(n,t,["start",D(c).pod])},async startServices(c){for(let f of await d(c))await R(n,t,["start",f.container])},async applyHostServices(c){let f=[],p=await R(n,t,["ps","--all","--filter",`label=${De}`,"--format","json"]),k=p.trim()?JSON.parse(p):[],S=new Map;for(let _ of k){let I=_.Labels?.[De];I&&S.set(I,{spec:_.Labels?.[Ko]??"",state:_.State??""})}for(let _ of c)try{await g(_)}catch(I){f.push({name:_.name,action:"failed",reason:I instanceof Error?I.message:String(I)})}async function g(_){let I=e.hostServiceLocal?.(_,c),O=I?{..._,...I.env&&Object.keys(I.env).length>0?{env:{..._.env,...I.env}}:{},...I.volumes?{volumes:I.volumes}:{},...I.network?{network:I.network}:{}}:_,le=dt(O.name),vn=sc(O),de=S.get(O.name);if(S.delete(O.name),de&&de.spec===vn){de.state!=="running"&&(await R(n,t,["start",le]),f.push({name:O.name,action:"started",reason:de.state}));return}let _s=de?"the spec changed":"it was not here";de&&await R(n,t,["rm","--force","--ignore",le]),await R(n,t,[...await r(),"run","--detach","--name",le,"--label",`${De}=${O.name}`,"--label",`${Ko}=${vn}`,...O.healthPath?["--label",`${fn}=${O.hostPort}:${O.healthPath}`]:[],"--memory",`${O.memoryMb}m`,"--restart","no",...O.network?["--network",O.network]:e.network?["--network",e.network]:[],...(O.volumes??[]).flatMap(gt=>["--volume",gt]),...dn(e.podBind).flatMap(gt=>["--publish",`${gt}:${O.hostPort}:${O.port}`]),...O.adminPort&&O.adminHostPort?["--publish",`127.0.0.1:${O.adminHostPort}:${O.adminPort}`]:[],...Wo(O.env),O.image,...O.command??[]],{env:{...O.env}}),f.push({name:O.name,action:de?"recreated":"created",reason:_s})}for(let _ of S.keys())await R(n,t,["rm","--force","--ignore",dt(_)]),f.push({name:_,action:"removed",reason:"it is no longer on the list"});return f},async hostServiceHealth(c){let f=await R(n,t,["ps","--all","--filter",`label=${De}`,"--format","json"]),p=(f.trim()?JSON.parse(f):[]).map(S=>S.Labels?.[De]??"").filter(S=>S!=="");if(p.length===0)return[];let k=Date.now()+c;for(;;){let S=[];for(let g of p)S.push(await Go(n,t,g,dt(g),o));if(S.every(g=>g.ok)||Date.now()>=k)return S;await new Promise(g=>setTimeout(g,500))}},async serviceHealth(c,f){let p=await d(c);if(p.length===0)return[];let k=Date.now()+f;for(;;){let S=[];for(let g of p)S.push(await Go(n,t,g.name,g.container,o));if(S.every(g=>g.ok)||Date.now()>=k)return S;await new Promise(g=>setTimeout(g,500))}},async stop(c,f){for(let p of await d(c))await R(n,t,["stop","--time",String(tc),p.container]);await R(n,t,["stop","--time",String(f),D(c).pod])},async remove(c){for(let f of await d(c))await R(n,t,["rm","--force","--ignore",f.container]);await R(n,t,["rm","--force","--ignore",D(c).pod])},async removeData(c){await R(n,t,["volume","rm","--force",D(c).pod])},async applyOwnerPassword(c){let f=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${c.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
190
+ `);await R(n,t,["exec","--interactive","--env","SNOUTPOD_OWNER_PASSWORD",D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:c.ownerPassword},stdin:f})},async applyRolePassword(c,f,p){let k=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${Ie(f)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
191
+ `);await R(n,t,["exec","--interactive","--env","SNOUTPOD_ROLE_PASSWORD",D(c).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:p},stdin:k})},async updateExtensions(c){let f=D(c),p=await R(n,t,["exec","--interactive",f.pod,"psql","--username","snoutpod_admin","--dbname",f.database,"--quiet","-tA","-F",cn,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:Uo});return Fo(p)},async runProjectSql(c,f){let p=D(c);await R(n,t,["exec","--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:f})},async ensureHostServiceDatabase(c,f,p){let k=dt(c),S=Ie(f),g=Ie(p),_=O=>["exec",k,"psql","--username",S,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",O];(await R(n,t,_(`select 1 from pg_database where datname = '${g}'`))).trim()!=="1"&&await R(n,t,_(`create database "${g}"`))},async replicationSlots(c){let f=await R(n,t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",vo]);return Po(f)},async dropReplicationSlot(c,f){if(!/^[a-z0-9_]{1,63}$/.test(f))throw new Error(`not a replication slot name: ${JSON.stringify(f)}`);await R(n,t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-v","ON_ERROR_STOP=1","-c",`select pg_drop_replication_slot('${f}')`])},async renameProject(c,f){f!==c.ref&&await R(n,t,["exec","--interactive",D(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:yc(f,c.ref)})},async waitReady(c,f){let p=D(c).pod,k=Date.now()+f;for(;;){let S=await n(t,["exec",p,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(S.code===0&&S.stdout.trim()==="t")return s(c),!0;if(Date.now()>=k)return!1;await new Promise(g=>setTimeout(g,500))}},async capacity(){let c=await a(),f=await e.capacity();return{pods:c.length,...f}},async databaseBytes(c){let f=await n(t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tAc","select pg_database_size(current_database())"]);return f.code===0?ao(f.stdout):null},async setReadOnly(c,f){let p=D(c);await R(n,t,["exec",p.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${p.database} set default_transaction_read_only = ${f?"on":"off"}`])},async archiveState(c){let f=await n(t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), failed_count from pg_stat_archiver`]);if(f.code!==0)return null;let[p="",k=""]=f.stdout.trim().split(`
192
+ `)[0]?.split("|")??[],S=Number(k);return{lastArchivedAt:p.trim()||null,failures:Number.isInteger(S)&&S>=0?S:0}}}}async function Go(e,t,n,r,o=zo){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,i;try{let c=JSON.parse(s.stdout);a=c[0]?.State,i=c[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let d=a?.Status??"unknown";if(d!=="running")return{name:n,ok:!1,detail:d};let u=lc(i?.[fn]);if(!u)return{name:n,ok:!0,detail:"running"};let m=await o(u.port,u.path);return m===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${m}`}}function hc(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function yc(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(`
193
+ `)}function wc(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var ve,ln,Ne,tc,nc,rc,De,Ko,ic,Vo,fn,zo,dc,fc,Jo,Xo=A(()=>{"use strict";ie();tn();sn();lt();Lo();nn();Bo();ve="snoutpod.ref",ln="snoutpod.generation",Ne="snoutpod.service",tc=10,nc=/^[a-z][a-z0-9-]{0,30}$/;rc=/^[a-z][a-z0-9_]{0,62}$/;De="snoutpod.host-service",Ko="snoutpod.host-service-spec";ic=/^[A-Za-z_][A-Za-z0-9_]*$/;Vo=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,fn="snoutpod.health";zo=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()})},dc="crun";fc=3e4,Jo=200});var is={};Pn(is,{DEFAULT_LOCAL_MEMORY_MB:()=>es,DEFAULT_LOCAL_PORT:()=>kc,ENGINE:()=>L,ENGINE_NAME:()=>hn,IMAGE:()=>fe,NETWORK:()=>ce,NO_BLACKHOLE_ROUTES:()=>ns,PUBLIC_IMAGE:()=>Pe,capacity:()=>ts,ensureImage:()=>_c,ensureNetwork:()=>Oc,exec:()=>H,hasData:()=>ut,localPsql:()=>Ic,localRuntime:()=>Rc,localSpec:()=>ss,localStatus:()=>yn,localUri:()=>os,networkPolicyInPlace:()=>rs,newLocalPassword:()=>Pc,newLocalRef:()=>vc,podLog:()=>as,podName:()=>mt,podmanReady:()=>Ac,removeLocal:()=>Mc,startLocal:()=>Dc,stopLocal:()=>Cc});import{randomBytes as Qo}from"node:crypto";import{existsSync as Zo}from"node:fs";import{platform as gn}from"node:os";import{delimiter as bc,join as Sc}from"node:path";function xc(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=gn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(bc))if(t&&e.some(n=>Zo(Sc(t,n))))return"podman";return Zo("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function vc(){return no(e=>Qo(e))}function Pc(){return Qo(18).toString("base64url")}async function _c(e,t=H){if(process.env.SNOUTPOD_IMAGE||(await t(L,["image","exists",fe])).code===0)return null;e?.(`Fetching the database image (${Pe}). This happens once.`);let n=await t(L,["pull",Pe],{timeoutMs:Tc}).catch(o=>({code:1,stdout:"",stderr:o instanceof Error?o.message:String(o)}));if(n.code!==0)return`could not fetch the database image ${Pe}: ${n.stderr.trim()||n.stdout.trim()||"the pull failed"}`;let r=await t(L,["tag",Pe,fe]);return r.code!==0?`fetched ${Pe} but could not tag it as ${fe}: ${r.stderr.trim()}`:(e?.("Got it."),null)}async function Ac(e=Ec){if(hn!=="podman")return`SNOUTPOD_ENGINE=${hn}: 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 H(L,["--version"])}catch{return`${L} 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`${L} --version failed: ${t.stderr.trim()||t.stdout.trim()}`;if((await H(L,["image","exists",fe])).code!==0)return`the pod image ${fe} is not on this machine. ${e}`;let r=await H(L,["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 $c(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 H(L,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
194
+ `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function ut(e){let t=gn()==="darwin"||gn()==="win32";return Ro(H,e,{binary:L,look:t?$c:void 0})}async function ts(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function Rc(e){return Yo({exec:H,binary:L,network:ce,hasData:ut,capacity:ts,repository:e})}function mt(e){return D(e).pod}async function Oc(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Nc()||await H(L,it({name:ce}));return}let t=it({name:ce,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await rs())return;await e?.(),await H(L,["network","rm","--force",ce]).catch(()=>{});let o=await H(L,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${ns}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${ce} with egress blocked${n?`: ${n}`:""}`)}async function Nc(){return(await H(L,["network","inspect",ce,"--format","json"])).code===0}async function rs(){let e=await H(L,["network","inspect",ce,"--format","json"]);if(e.code!==0)return!1;try{return xo(JSON.parse(e.stdout)?.[0])}catch{return!1}}function os(e){let t=D(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function ss(e,t={}){return J(e.ref),{ref:e.ref,generation:1,image:t.image??fe,memoryMb:t.memoryMb??es,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 pn(e,t){e.onProgress?.(t)}async function Dc(e,t,n={}){let r=ss(t,n),o=n.timeoutMs??12e4,s=n.hasData??ut,a=(await e.list()).find(i=>i.ref===t.ref);if(a?a.status==="stopped"?(pn(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):pn(n,"The database is already running."):(pn(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 jc(t.ref,o,n.podLog??as));return await e.applyOwnerPassword(r),await e.createServices(r),yn(e,t,n)}async function Ic(e,t){let n=D(e.ref);return H(L,["exec","--interactive","-e","PGPASSWORD",mt(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 as(e){let t=await H(L,["logs","--tail","20",mt(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function jc(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${mt(e)} never became ready in ${t}ms
155
195
  --- pod log ---
156
- ${r}`}async function bc(e,t,n={}){J(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,yo),mn(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Sc(e,t,n){J(t),await e.remove(t),n&&await e.removeData(t)}async function mn(e,t,n={}){J(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??dt;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?Wo(t):null}}var un,C,fe,ae,sc,Bo,H,cc,Pe,dc,qo,zo=A(()=>{"use strict";wo();ct();ko();tn();Lo();se();un=process.env.SNOUTPOD_ENGINE??"podman";C=oc(),fe=process.env.SNOUTPOD_IMAGE??"snoutpod/postgres:17",ae=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",sc=54322,Bo=512,H=bo();cc="cd packages/snoutpod && npm run image:build",Pe="ghcr.io/snoutdata/snoutpod-postgres:17";dc=20*60*1e3;qo="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});le();var gt=[{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"]},{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",flags:["--ref"]},{name:"projects resume",summary:"Start a paused project",flags:["--ref"]},{name:"projects delete",summary:"Delete a project",flags:["--ref"]},{name:"projects show",summary:"One project: state, products, functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage and the data API: on or off",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage or data-api on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage or data-api off",flags:["--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 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"]}];M();re();ye();pe();kt();Je();F();Et();M();M();pe();F();async function Xn(){let{projects:e,allowance:t}=await V();h({projects:e,allowance:t},()=>{if(e.length===0){l('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${L([["REF","NAME","STATE","REGION","LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?T("read-only"):n.state==="ready"?n.state:T(n.state),n.region,X(n.lastConnectionAt)])])}
157
- `)})}async function Tt(e){let t=await S("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;l(`Created ${n}.`);let r=t.project;e.wait&&(l("Waiting for it to start\u2026"),r=await Ae(n,{timeoutMs:e.timeoutMs,onTick:o=>l(x(` ${o}`))})),e.link&&Be(e.link,{ref:n,name:r.name}),h({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
158
- `),l(""),l(`Ready. ${T(n)} in ${r.region}.`),l("The password is stored for you: `snoutdata db url` prints this again.")})}async function Zn(e,t){let n=`cloud-project-${e}`,r=await S(n,{ref:t});h(r,()=>{l(r.changed?`${t}: ${e}d.`:`${t} was already ${e==="delete"?"deleted":`${e}d`}.`)})}async function Qn(e){let t=await S("cloud-project-reset-password",{ref:e});h(t,()=>{process.stdout.write(`${t.password}
159
- `),l(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function er(){let{teams:e}=await Qe();h({teams:e},()=>{if(e.length===0){l("You are not on any teams.");return}process.stdout.write(`${L([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
160
- `)})}async function tr(e){let{teams:t}=await Qe(),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}M();F();async function nr(e){let t=await S("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays});h(t,()=>{process.stdout.write(`${t.token}
161
- `),l(""),l(`${T(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),l("This is the only time it is shown: only its hash is stored."),l(x(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function rr(){let{tokens:e}=await S("cloud-token-list");h({tokens:e},()=>{if(e.length===0){l("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${L([["PREFIX","NAME","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?T(t.prefix):t.prefix,t.name,t.revokedAt?"revoked":t.live?"live":"expired",X(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
162
- `)})}async function or(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await S("cloud-token-revoke",t);h(n,()=>{l(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}M();le();F();async function sr(e){let t=await W(e);h({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){l(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${ir(t.anonKey,t.serviceRoleKey)}
163
- `),ia(t.keysIssuedAt)})}async function ar(e,t){if(!t.force)throw l(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),l("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});h(n,()=>{process.stdout.write(`${ir(n.anonKey,n.serviceRoleKey)}
164
- `),l(""),l(n.breaks),l(x(" snoutdata keys --json to read them again later"))})}function ir(e,t){return L([["KEY","VALUE"],["anon",e],["service_role",t]])}function ia(e){l(""),l(`${T("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),l(`${T("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&l(x(` issued ${e.slice(0,10)}`))}M();le();F();import{readdir as ha,readFile as ya,stat as wa}from"node:fs/promises";import{join as Nt,relative as ba,sep as Sa}from"node:path";var la=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function da(e){return la.test(e)}function $t(e){if(!da(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 ua=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function ma(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!==".."&&ua.test(n))}var At=2*1024*1024,cr=200;function lr(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>cr)throw new Error(`a function may hold ${cr} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!ma(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,i)=>a.path<i.path?-1:a.path>i.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=fa(o);if(s.length>At)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${At/1024} KB. A function is source code; put large files in Storage.`);return o}function fa(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 pa=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/,ga=["SUPABASE_URL","SUPABASE_ANON_KEY","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_DB_URL"];function Rt(e){return pa.test(e)?e.startsWith("SNOUT_")?`names beginning with SNOUT_ are set by the platform, so ${e} cannot be one of yours.`:ga.includes(e)?`${e} is set for you from this project's own keys and cannot be overridden.`:null:`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`}var Kl=64*1024;var dr=["functions","supabase/functions"];async function Ot(e){try{return(await wa(e)).isDirectory()}catch{return!1}}async function ka(e,t){if(t){if(!await Ot(t))throw new y(`${t} is not a directory`);return t}for(let n of dr){let r=Nt(n,e);if(await Ot(r))return r}throw new y(`no folder found for ${e} at ${dr[0]}/${e}; use --dir to say where it is.`)}async function ur(e){let t=[];async function n(r){for(let o of await ha(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=Nt(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await ya(s),i=a.toString("utf8");if(Buffer.compare(Buffer.from(i,"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:ba(e,s).split(Sa).join("/"),text:i})}}return await n(e),t}async function xa(e,t,n){let r=await ur(t);if(r.length===0)throw new y(`${t} has no files in it`);let o=n??(r.some(i=>i.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=Nt(t,"..","_shared");if(!await Ot(s))return{entrypoint:o,files:r};let a=(await ur(s)).map(i=>({path:`_shared/${i.path}`,text:i.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(i=>({path:`${e}/${i.path}`,text:i.text})),...a]}}async function Dt(e,t,n){$t(t);let r=await ka(t,n.dir),{entrypoint:o,files:s}=await xa(t,r,n.entrypoint),a=lr({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 mr(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await Dt(e,t,n),i=r.functions.find(d=>d.name===t);h(r,()=>{let d=Math.max(1,Math.round(a/1024));l(`${T(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${d} KB)`),i&&l(` ${i.url}`),n.verifyJwt||(l(""),l(T(" Anybody who knows this URL can run it. No API key is required.")),l(x(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&l(x(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function It(e){return S("cloud-project-functions",{ref:e})}function jt(e,t){return $t(t),S("cloud-project-functions",{ref:e,action:"delete",name:t})}async function fr(e){let t=await It(e);h(t,()=>{if(t.functions.length===0){l(`${e} has no functions.`),l(x(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${L([["NAME","KEY","SIZE","URL"],...t.functions.map(n=>[n.name,n.verifyJwt?"required":T("open"),n.bytes===null?"-":`${Math.max(1,Math.round(n.bytes/1024))} KB`,n.url])])}
165
- `),t.limit.maxFunctions!==null&&l(x(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`))})}async function pr(e,t){let n=await jt(e,t);h(n,()=>{l(`${t} is gone. It stops answering within a few seconds.`)})}M();le();F();function Pa(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function Ea(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function gr(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 Ea()})}else for(let s of t){let a=Pa(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 S("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});h(o,()=>{for(let s of r)l(`${T(s.name)} set`);l(x(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||l(x(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function hr(e,t){let n=Rt(t);if(n)throw new y(n);let r=await S("cloud-project-secrets",{ref:e,action:"unset",name:t});h(r,()=>{l(`${t} is gone.`)})}async function yr(e){let t=await S("cloud-project-secrets",{ref:e});h(t,()=>{if(t.secrets.length===0){l(`${e} has no function secrets.`),l(x(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${L([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,X(n.updatedAt)])])}
166
- `),l(x("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as Lt}from"node:child_process";import{appendFileSync as Ta,existsSync as kr,readFileSync as Aa,writeFileSync as Mt}from"node:fs";import{join as $a}from"node:path";import{open as xr}from"node:fs/promises";function wr(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 br(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 Sr(e){let t=e.split(`
167
- `).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}}M();re();ye();M();F();async function vr(e){try{let{projects:t}=await V(),n=t.find(r=>r.ref===e);n?.readOnly?K(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&K(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function Ut(e){await vr(e);let t=await W(e);h(t,()=>{process.stdout.write(`${t.uri}
168
- `)})}async function Pr(e,t){if(t.length===0&&!te()){let o=he();P("usage",`db psql with no statements opens an interactive session, and ${o?ge(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await vr(e);let n=await W(e);n.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let r=Lt("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,PGSSLMODE:"require"}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new q("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new q("failed",String(a)))}),r.on("close",a=>o(a??0))})}function Er(e,t,n="DATABASE_URL"){let r=$a(e,".env"),o=`${n}=${t}`;if(!kr(r))return Mt(r,`${o}
169
- `),{path:r,replaced:!1};let s=Aa(r,"utf8"),a=new RegExp(`^${n}=.*$`,"m");return a.test(s)?(Mt(r,s.replace(a,o)),{path:r,replaced:!0}):(Ta(r,s.endsWith(`
196
+ ${r}`}async function Cc(e,t,n={}){J(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,To),yn(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Mc(e,t,n){J(t),await e.remove(t),n&&await e.removeData(t)}async function yn(e,t,n={}){J(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??ut;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?os(t):null}}var hn,L,fe,ce,kc,es,H,Ec,Pe,Tc,ns,cs=A(()=>{"use strict";Ao();lt();Oo();on();Xo();ie();hn=process.env.SNOUTPOD_ENGINE??"podman";L=xc(),fe=process.env.SNOUTPOD_IMAGE??"snoutpod/postgres:17",ce=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",kc=54322,es=512,H=$o();Ec="cd packages/snoutpod && npm run image:build",Pe="ghcr.io/snoutdata/snoutpod-postgres:17";Tc=20*60*1e3;ns="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});te();var ht=[{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"]},{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",flags:["--ref"]},{name:"projects resume",summary:"Start a paused project",flags:["--ref"]},{name:"projects delete",summary:"Delete a project",flags:["--ref"]},{name:"projects show",summary:"One project: state, products, functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage and the data API: on or off",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage or data-api on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage or data-api off",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 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"]}];M();oe();ye();pe();kt();Je();F();_t();M();M();pe();F();async function nr(){let{projects:e,allowance:t}=await G();h({projects:e,allowance:t},()=>{if(e.length===0){l('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${j([["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,X(n.lastConnectionAt)])])}
197
+ `)})}async function At(e){let t=await w("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;l(`Created ${n}.`);let r=t.project;e.wait&&(l("Waiting for it to start\u2026"),r=await Ae(n,{timeoutMs:e.timeoutMs,onTick:o=>l(b(` ${o}`))})),e.link&&Be(e.link,{ref:n,name:r.name}),h({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
198
+ `),l(""),l(`Ready. ${P(n)} in ${r.region}.`),l("The password is stored for you: `snoutdata db url` prints this again.")})}async function rr(e,t){let n=`cloud-project-${e}`,r=await w(n,{ref:t});h(r,()=>{l(r.changed?`${t}: ${e}d.`:`${t} was already ${e==="delete"?"deleted":`${e}d`}.`)})}async function or(e){let t=await w("cloud-project-reset-password",{ref:e});h(t,()=>{process.stdout.write(`${t.password}
199
+ `),l(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function sr(){let{teams:e}=await Qe();h({teams:e},()=>{if(e.length===0){l("You are not on any teams.");return}process.stdout.write(`${j([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
200
+ `)})}async function ar(e){let{teams:t}=await Qe(),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}M();F();async function ir(e){let t=await w("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays});h(t,()=>{process.stdout.write(`${t.token}
201
+ `),l(""),l(`${P(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),l("This is the only time it is shown: only its hash is stored."),l(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function cr(){let{tokens:e}=await w("cloud-token-list");h({tokens:e},()=>{if(e.length===0){l("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${j([["PREFIX","NAME","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?P(t.prefix):t.prefix,t.name,t.revokedAt?"revoked":t.live?"live":"expired",X(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
202
+ `)})}async function lr(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await w("cloud-token-revoke",t);h(n,()=>{l(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}M();te();F();async function dr(e){let t=await W(e);h({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){l(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${mr(t.anonKey,t.serviceRoleKey)}
203
+ `),Sa(t.keysIssuedAt)})}async function ur(e,t){if(!t.force)throw l(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),l("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 w("cloud-project-rotate-jwt",{ref:e});h(n,()=>{process.stdout.write(`${mr(n.anonKey,n.serviceRoleKey)}
204
+ `),l(""),l(n.breaks),l(b(" snoutdata keys --json to read them again later"))})}function mr(e,t){return j([["KEY","VALUE"],["anon",e],["service_role",t]])}function Sa(e){l(""),l(`${P("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),l(`${P("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&l(b(` issued ${e.slice(0,10)}`))}M();te();F();import{readdir as $a,readFile as Ra,stat as Oa}from"node:fs/promises";import{join as Dt,relative as Na,sep as Da}from"node:path";var ka=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function va(e){return ka.test(e)}function Rt(e){if(!va(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 Pa=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function Ea(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!==".."&&Pa.test(n))}var $t=2*1024*1024,fr=200;function pr(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>fr)throw new Error(`a function may hold ${fr} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!Ea(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,i)=>a.path<i.path?-1:a.path>i.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=_a(o);if(s.length>$t)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${$t/1024} KB. A function is source code; put large files in Storage.`);return o}function _a(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 Ta=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/,Aa=["SUPABASE_URL","SUPABASE_ANON_KEY","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_DB_URL"];function Ot(e){return Ta.test(e)?e.startsWith("SNOUT_")?`names beginning with SNOUT_ are set by the platform, so ${e} cannot be one of yours.`:Aa.includes(e)?`${e} is set for you from this project's own keys and cannot be overridden.`:null:`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`}var id=64*1024;var gr=["functions","supabase/functions"];async function Nt(e){try{return(await Oa(e)).isDirectory()}catch{return!1}}async function Ia(e,t){if(t){if(!await Nt(t))throw new y(`${t} is not a directory`);return t}for(let n of gr){let r=Dt(n,e);if(await Nt(r))return r}throw new y(`no folder found for ${e} at ${gr[0]}/${e}; use --dir to say where it is.`)}async function hr(e){let t=[];async function n(r){for(let o of await $a(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=Dt(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await Ra(s),i=a.toString("utf8");if(Buffer.compare(Buffer.from(i,"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:Na(e,s).split(Da).join("/"),text:i})}}return await n(e),t}async function ja(e,t,n){let r=await hr(t);if(r.length===0)throw new y(`${t} has no files in it`);let o=n??(r.some(i=>i.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=Dt(t,"..","_shared");if(!await Nt(s))return{entrypoint:o,files:r};let a=(await hr(s)).map(i=>({path:`_shared/${i.path}`,text:i.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(i=>({path:`${e}/${i.path}`,text:i.text})),...a]}}async function It(e,t,n){Rt(t);let r=await Ia(t,n.dir),{entrypoint:o,files:s}=await ja(t,r,n.entrypoint),a=pr({entrypoint:o,files:s});return{answer:await w("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 yr(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await It(e,t,n),i=r.functions.find(d=>d.name===t);h(r,()=>{let d=Math.max(1,Math.round(a/1024));l(`${P(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${d} KB)`),i&&l(` ${i.url}`),n.verifyJwt||(l(""),l(P(" Anybody who knows this URL can run it. No API key is required.")),l(b(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&l(b(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function jt(e){return w("cloud-project-functions",{ref:e})}function Ct(e,t){return Rt(t),w("cloud-project-functions",{ref:e,action:"delete",name:t})}async function wr(e){let t=await jt(e);h(t,()=>{if(t.functions.length===0){l(`${e} has no functions.`),l(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${j([["NAME","KEY","SIZE","URL"],...t.functions.map(n=>[n.name,n.verifyJwt?"required":P("open"),n.bytes===null?"-":`${Math.max(1,Math.round(n.bytes/1024))} KB`,n.url])])}
205
+ `),t.limit.maxFunctions!==null&&l(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`))})}async function br(e,t){let n=await Ct(e,t);h(n,()=>{l(`${t} is gone. It stops answering within a few seconds.`)})}M();te();F();function Ma(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function La(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function Sr(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 La()})}else for(let s of t){let a=Ma(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=Ot(s.name);if(a)throw new y(a)}let o=null;for(let s of r)o=await w("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});h(o,()=>{for(let s of r)l(`${P(s.name)} set`);l(b(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||l(b(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function xr(e,t){let n=Ot(t);if(n)throw new y(n);let r=await w("cloud-project-secrets",{ref:e,action:"unset",name:t});h(r,()=>{l(`${t} is gone.`)})}async function kr(e){let t=await w("cloud-project-secrets",{ref:e});h(t,()=>{if(t.secrets.length===0){l(`${e} has no function secrets.`),l(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${j([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,X(n.updatedAt)])])}
206
+ `),l(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as Ut}from"node:child_process";import{appendFileSync as Fa,existsSync as _r,readFileSync as Ba,writeFileSync as Lt}from"node:fs";import{join as Ha}from"node:path";import{open as Tr}from"node:fs/promises";function vr(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 Pr(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 Er(e){let t=e.split(`
207
+ `).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}}M();oe();ye();M();F();async function Ar(e){try{let{projects:t}=await G(),n=t.find(r=>r.ref===e);n?.readOnly?K(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&K(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function Ft(e){await Ar(e);let t=await W(e);h(t,()=>{process.stdout.write(`${t.uri}
208
+ `)})}async function $r(e,t){if(t.length===0&&!ne()){let o=he();E("usage",`db psql with no statements opens an interactive session, and ${o?ge(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Ar(e);let n=await W(e);n.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let r=Ut("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,PGSSLMODE:"require"}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new q("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new q("failed",String(a)))}),r.on("close",a=>o(a??0))})}function Rr(e,t,n="DATABASE_URL"){let r=Ha(e,".env"),o=`${n}=${t}`;if(!_r(r))return Lt(r,`${o}
209
+ `),{path:r,replaced:!1};let s=Ba(r,"utf8"),a=new RegExp(`^${n}=.*$`,"m");return a.test(s)?(Lt(r,s.replace(a,o)),{path:r,replaced:!0}):(Fa(r,s.endsWith(`
170
210
  `)?`${o}
171
211
  `:`
172
212
  ${o}
173
- `),{path:r,replaced:!1})}async function _r(e,t={}){if(t.statusOnly){let{export:a}=await Se(e);return h({ref:e,export:a},()=>Ct(a)),0}let n=await Te(e);if(!n.export.pending&&n.export.url&&!t.out)return h({ref:e,export:n.export},()=>Ct(n.export)),0;l("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await Oa(e,t.timeoutMs);if(r.error&&P("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return h({ref:e,export:r},()=>Ct(r)),0;r.url||P("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await Na(r.url,t.out),s=r.rolesSql?Ra(t.out):null;return s&&r.rolesSql&&Mt(s,r.rolesSql.endsWith(`
213
+ `),{path:r,replaced:!1})}async function Or(e,t={}){if(t.statusOnly){let{export:a}=await Se(e);return h({ref:e,export:a},()=>Mt(a)),0}let n=await Te(e);if(!n.export.pending&&n.export.url&&!t.out)return h({ref:e,export:n.export},()=>Mt(n.export)),0;l("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await Ka(e,t.timeoutMs);if(r.error&&E("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return h({ref:e,export:r},()=>Mt(r)),0;r.url||E("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await Wa(r.url,t.out),s=r.rolesSql?qa(t.out):null;return s&&r.rolesSql&&Lt(s,r.rolesSql.endsWith(`
174
214
  `)?r.rolesSql:`${r.rolesSql}
175
- `,"utf8"),h({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{l(`Saved ${Q(o)} to ${t.out}.`),s?(l(`Saved the roles it needs to ${s}.`),l(x("Restore it with, in this order:")),l(x(` psql --dbname <your database> -f ${s}`)),l(x(` pg_restore --dbname <your database> ${t.out}`)),l(x("The roles are created without passwords. Set them yourself if anything signs in as one."))):(l(x("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),l(x("Owners and grants are skipped: this export carries no roles file.")))}),0}function Ra(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function Oa(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await Se(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 Na(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 xr(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 Ct(e){if(e.error){K(`The last export did not finish: ${e.error}`);return}if(e.pending){l("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){l("This project has never been exported.");return}l(`A copy taken ${X(e.completedAt)}, ${Q(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
176
- `),e.urlExpiresAt&&l(x(`That link stops working ${X(e.urlExpiresAt)}.`))):l(x("The link has expired. Run the export again to sign a new one; the copy is kept."))}function Q(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 Tr(e,t){let n=t.file;n||P("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),kr(n)||P("usage",`no file at ${n}`,{file:n});let r=await xr(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=wr(o.slice(0,s.bytesRead)),i=await W(e);i.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:d}=await V(),u=d.find(k=>k.ref===e),m=await Da(i);m===null&&P("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 c=br(a,{existingTables:m,readOnly:u?.readOnly===!0},{force:t.force});c.ok||P("conflict",c.reason,{ref:e,file:n,refusal:c.code}),l(`Restoring ${n} into ${e} with ${c.tool} (${c.format} format).`);let f=await Ia(i,c.tool,n),p=Sr(f.stderr);return f.code!==0&&p.fatal.length===0&&p.total>0?(h({ref:e,file:n,ok:!0,tool:c.tool,format:c.format,ignoredErrors:p.harmless},()=>{l(`Restored into ${e}.`),l(`${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):(f.code!==0&&P("failed",`${c.tool} exited ${f.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}),h({ref:e,file:n,ok:!0,tool:c.tool,format:c.format},()=>l(`Restored into ${e}.`)),0)}async function Da(e){return new Promise(t=>{let n=Lt("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')"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{K(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 Ia(e,t,n){let r=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],o=t==="psql"?[...r,"--no-psqlrc","-v","ON_ERROR_STOP=1","-f",n]:[...r,"--no-owner","--no-privileges",n];return new Promise(s=>{let a=Lt(t,o,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),i="";a.stderr?.on("data",d=>{let u=String(d);i+=u,process.stderr.write(u)}),a.on("error",d=>{K(d.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(d)),s({code:127,stderr:i})}),a.on("close",d=>s({code:d??0,stderr:i}))})}M();le();F();var et=["auth","storage","data-api"];function ke(e){return{error:e instanceof Error?e.message:String(e)}}async function tt(e){let[t,n,r]=await Promise.all([S("cloud-project-auth",{ref:e,action:"status"}).then(o=>({enabled:o.enabled===!0,google:o.google?.enabled===!0,saml:o.saml?.enabled===!0}),ke),S("cloud-project-storage",{ref:e}).then(o=>({enabled:o.storage?.enabled===!0,pending:o.storage?.pending===!0,bytes:o.storage?.bytes??null,files:o.storage?.files??null}),ke),S("cloud-project-data-api",{ref:e}).then(o=>({enabled:o.dataApi?.enabled===!0,allowedOnPlan:o.dataApi?.allowed===!0}),ke)]);return{ref:e,auth:t,storage:n,dataApi:r}}function Ar(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!et.includes(t))throw new y(`say which product: ${et.join(", ")}`);return t}async function Ft(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 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 $r(e){let t=await tt(e);h(t,()=>{let n=(s,a,i="")=>[s,a&&typeof a=="object"&&"error"in a?`could not read: ${a.error}`:a.enabled?T("on"):"off",i],r=t.storage,o=t.dataApi;process.stdout.write(`${L([["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":"")])}
177
- `),l(x(` snoutdata products enable storage --ref ${e}`))})}async function Rr(e,t,n){let r=await Ft(e,t,n);h(r,()=>l(r.note))}async function nt(e){let t=await S("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function Bt(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 Ca(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(`${L(n)}
178
- `)}async function Or(e,t,n){if(t===void 0||t==="list"){let o=await nt(e);h(o,()=>{if(o.domains.length===0){l(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${L([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":T("no"),s.certificate?.state??""])])}
179
- `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await Bt(e,t,n??"");h(r,()=>{if(t==="remove"){l(`${n} removed.`);return}r.domain&&(l(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||Ca(r.domain,e)),r.next&&l(r.next)})}async function Ht(e){return S("cloud-project-restore",{ref:e},"GET")}async function qt(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 Nr(e){let t=await Ht(e);h(t,()=>{let n=t.restore;if(!n.available){l(n.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is not part of the ${n.tier} plan.`);return}l(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),l(x(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function Dr(e,t,n){let r=await qt(e,t,n);h(r,()=>{l(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${T(r.project.name)} (${r.project.ref}).`),l(x(" snoutdata projects list to see when it is ready"))})}async function Kt(e){let{projects:t}=await V(),n=t.find(i=>i.ref===e);if(!n)throw new y(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([tt(e),S("cloud-project-functions",{ref:e}).then(i=>(i.functions??[]).map(d=>d.name),ke),S("cloud-project-secrets",{ref:e}).then(i=>(i.secrets??[]).map(d=>d.name),ke),nt(e).then(i=>i.domains.map(d=>({hostname:d.hostname,verified:d.verified})),ke)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Ir(e){let t=await Kt(e);h(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(`${L([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],["auth / storage / data-api",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)}`],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
180
- `)})}M();F();re();import{spawn as La}from"node:child_process";import{createHash as Ua}from"node:crypto";import{existsSync as Fa,readdirSync as Ba,readFileSync as Ha,statSync as qa}from"node:fs";import{isAbsolute as Ka,join as Wt,relative as Wa,resolve as Ja}from"node:path";function Ma(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function rt(e,t,n={}){let r=Ma(e),o=new Map(r.map(u=>[u.name,u])),s=[];for(let u of t){let m=o.get(u.name);if(!m){s.push({code:"missing",name:u.name,reason:`${u.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}m.checksum!==u.checksum&&s.push({code:"changed",name:u.name,reason:`${u.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(u=>u.name).sort((u,m)=>u<m?-1:u>m?1:0)[t.length-1],i=new Set(t.map(u=>u.name)),d=[];for(let u of r)if(!i.has(u.name)){if(a!==void 0&&u.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:u.name,reason:`${u.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}d.push(u)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:d}}function $e(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function Mr(e){let t=Wa(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var Re="_snoutdata_migrations",Va=`create table if not exists ${Re} (
215
+ `,"utf8"),h({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{l(`Saved ${Q(o)} to ${t.out}.`),s?(l(`Saved the roles it needs to ${s}.`),l(b("Restore it with, in this order:")),l(b(` psql --dbname <your database> -f ${s}`)),l(b(` pg_restore --dbname <your database> ${t.out}`)),l(b("The roles are created without passwords. Set them yourself if anything signs in as one."))):(l(b("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),l(b("Owners and grants are skipped: this export carries no roles file.")))}),0}function qa(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function Ka(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await Se(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 Wa(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 Tr(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 Mt(e){if(e.error){K(`The last export did not finish: ${e.error}`);return}if(e.pending){l("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){l("This project has never been exported.");return}l(`A copy taken ${X(e.completedAt)}, ${Q(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
216
+ `),e.urlExpiresAt&&l(b(`That link stops working ${X(e.urlExpiresAt)}.`))):l(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function Q(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 Nr(e,t){let n=t.file;n||E("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),_r(n)||E("usage",`no file at ${n}`,{file:n});let r=await Tr(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=vr(o.slice(0,s.bytesRead)),i=await W(e);i.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:d}=await G(),u=d.find(k=>k.ref===e),m=await Ja(i);m===null&&E("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 c=Pr(a,{existingTables:m,readOnly:u?.readOnly===!0},{force:t.force});c.ok||E("conflict",c.reason,{ref:e,file:n,refusal:c.code}),l(`Restoring ${n} into ${e} with ${c.tool} (${c.format} format).`);let f=await Ga(i,c.tool,n),p=Er(f.stderr);return f.code!==0&&p.fatal.length===0&&p.total>0?(h({ref:e,file:n,ok:!0,tool:c.tool,format:c.format,ignoredErrors:p.harmless},()=>{l(`Restored into ${e}.`),l(`${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):(f.code!==0&&E("failed",`${c.tool} exited ${f.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}),h({ref:e,file:n,ok:!0,tool:c.tool,format:c.format},()=>l(`Restored into ${e}.`)),0)}async function Ja(e){return new Promise(t=>{let n=Ut("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')"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{K(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 Ga(e,t,n){let r=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],o=t==="psql"?[...r,"--no-psqlrc","-v","ON_ERROR_STOP=1","-f",n]:[...r,"--no-owner","--no-privileges",n];return new Promise(s=>{let a=Ut(t,o,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),i="";a.stderr?.on("data",d=>{let u=String(d);i+=u,process.stderr.write(u)}),a.on("error",d=>{K(d.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(d)),s({code:127,stderr:i})}),a.on("close",d=>s({code:d??0,stderr:i}))})}M();te();F();var et=["auth","storage","data-api"];function xe(e){return{error:e instanceof Error?e.message:String(e)}}async function tt(e){let[t,n,r]=await Promise.all([w("cloud-project-auth",{ref:e,action:"status"}).then(o=>({enabled:o.enabled===!0,google:o.google?.enabled===!0,saml:o.saml?.enabled===!0}),xe),w("cloud-project-storage",{ref:e}).then(o=>({enabled:o.storage?.enabled===!0,pending:o.storage?.pending===!0,bytes:o.storage?.bytes??null,files:o.storage?.files??null}),xe),w("cloud-project-data-api",{ref:e}).then(o=>({enabled:o.dataApi?.enabled===!0,allowedOnPlan:o.dataApi?.allowed===!0}),xe)]);return{ref:e,auth:t,storage:n,dataApi:r}}function Dr(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!et.includes(t))throw new y(`say which product: ${et.join(", ")}`);return t}async function Bt(e,t,n){if(t==="auth"){let r=await w("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 await w(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 Ir(e){let t=await tt(e);h(t,()=>{let n=(s,a,i="")=>[s,a&&typeof a=="object"&&"error"in a?`could not read: ${a.error}`:a.enabled?P("on"):"off",i],r=t.storage,o=t.dataApi;process.stdout.write(`${j([["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":"")])}
217
+ `),l(b(` snoutdata products enable storage --ref ${e}`))})}async function jr(e,t,n){let r=await Bt(e,t,n);h(r,()=>l(r.note))}async function nt(e){let t=await w("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function Ht(e,t,n){if(!n)throw new y(`domains ${t} needs a hostname, e.g. api.example.com`);let r=await w("cloud-project-domain",{ref:e,action:t,hostname:n});return{ref:e,domain:r.domain??null,next:r.next??null}}function za(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(`${j(n)}
218
+ `)}async function Cr(e,t,n){if(t===void 0||t==="list"){let o=await nt(e);h(o,()=>{if(o.domains.length===0){l(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${j([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":P("no"),s.certificate?.state??""])])}
219
+ `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await Ht(e,t,n??"");h(r,()=>{if(t==="remove"){l(`${n} removed.`);return}r.domain&&(l(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||za(r.domain,e)),r.next&&l(r.next)})}async function qt(e){return w("cloud-project-restore",{ref:e},"GET")}async function Kt(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 w("cloud-project-restore",{ref:e,at:new Date(t).toISOString(),...n?{name:n}:{}})}async function Mr(e){let t=await qt(e);h(t,()=>{let n=t.restore;if(!n.available){l(n.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is not part of the ${n.tier} plan.`);return}l(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),l(b(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function Lr(e,t,n){let r=await Kt(e,t,n);h(r,()=>{l(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${P(r.project.name)} (${r.project.ref}).`),l(b(" snoutdata projects list to see when it is ready"))})}async function Wt(e){let{projects:t}=await G(),n=t.find(i=>i.ref===e);if(!n)throw new y(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([tt(e),w("cloud-project-functions",{ref:e}).then(i=>(i.functions??[]).map(d=>d.name),xe),w("cloud-project-secrets",{ref:e}).then(i=>(i.secrets??[]).map(d=>d.name),xe),nt(e).then(i=>i.domains.map(d=>({hostname:d.hostname,verified:d.verified})),xe)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Ur(e){let t=await Wt(e);h(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(`${j([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],["auth / storage / data-api",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)}`],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
220
+ `)})}M();te();F();import{readFile as Ya}from"node:fs/promises";var Xa="api.snoutdata.com";function qr(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}.${Xa}/auth/v1/callback`}}async function Za(e){return qr(e,await w("cloud-project-auth",{ref:e,action:"status"}))}async function Jt(e,t){return qr(e,await w("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function Gt(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(`${j([["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]])}
221
+ `),e.enabled||l(b(` snoutdata products enable auth --ref ${e.ref}`))}async function Qa(){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 Kr(e,t,n,r){if(t===void 0||t==="show"){let o=await Za(e);h(o,()=>Gt(o));return}if(t==="google"){if(n[0]==="off"){let a=await Jt(e,{googleEnabled:!1});h(a,()=>l("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 Qa();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await Jt(e,o);h(s,()=>{s.google.ready?l("Google sign-in is on. Your auth service restarts within about a minute."):Gt(s),l(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 Jt(e,o);h(s,()=>Gt(s));return}if(t==="templates"){let o=await w("cloud-project-auth",{ref:e,action:"templates"});h(o,()=>Hr(o.templates));return}if(t==="template"){let o=n[0];if(!o||!Br.includes(o))throw new y(`auth template needs a kind: ${Br.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 i=await Ya(r.file,"utf8"),d=await w("cloud-project-auth",{ref:e,action:"templates"}),u=r.subject??d.templates.find(m=>m.kind===o)?.subject;s={kind:o,subject:u,body:i}}let a=await w("cloud-project-auth",{ref:e,action:"template",template:s});h(a,()=>{Hr(a.templates),l(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 Br=["confirmation","recovery","magic_link","invite","email_change"];function Hr(e){process.stdout.write(`${j([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?P("yours"):"ours",t.subject])])}
222
+ `)}M();F();oe();import{spawn as ti}from"node:child_process";import{createHash as ni}from"node:crypto";import{existsSync as ri,readdirSync as oi,readFileSync as si,statSync as ai}from"node:fs";import{isAbsolute as ii,join as Vt,relative as ci,resolve as li}from"node:path";function ei(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function rt(e,t,n={}){let r=ei(e),o=new Map(r.map(u=>[u.name,u])),s=[];for(let u of t){let m=o.get(u.name);if(!m){s.push({code:"missing",name:u.name,reason:`${u.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}m.checksum!==u.checksum&&s.push({code:"changed",name:u.name,reason:`${u.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(u=>u.name).sort((u,m)=>u<m?-1:u>m?1:0)[t.length-1],i=new Set(t.map(u=>u.name)),d=[];for(let u of r)if(!i.has(u.name)){if(a!==void 0&&u.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:u.name,reason:`${u.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}d.push(u)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:d}}function $e(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function Jr(e){let t=ci(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var Re="_snoutdata_migrations",di=`create table if not exists ${Re} (
181
223
  name text primary key,
182
224
  checksum text not null,
183
225
  applied_at timestamptz not null default now()
184
- );`;function Ga(e){if(!Fa(e)||!qa(e).isDirectory())throw new Error(`no migrations folder at ${Mr(e)}. Put your .sql files there, or pass --dir.`);return Ba(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=Wt(e,t),r=Ha(n,"utf8");return{name:t,path:n,sql:r,checksum:Ua("sha256").update(r,"utf8").digest("hex")}})}function Jt(e,t,n=[]){return new Promise(r=>{let o=La("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:"require"}}),s="",a="";o.stdout.on("data",i=>s+=String(i)),o.stderr.on("data",i=>a+=String(i)),o.on("error",i=>{r({code:127,out:"",err:i.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(i)})}),o.on("close",i=>r({code:i??0,out:s,err:a})),o.stdin.end(t)})}async function za(e){let t=await Jt(e,Va);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${Re}`);let n=await Jt(e,`select name || E'\\t' || checksum from ${Re};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${Re}`);return n.out.split(`
185
- `).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 Cr(e){return`'${e.replace(/'/g,"''")}'`}async function Lr(e,t={}){let n=await Vt(e,t,r=>l(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
186
- `);P("conflict",`Refused. This folder and ${e} do not agree:
187
- ${r}`,{ref:e,problems:n.problems})}P("failed",`${n.failed} failed, and nothing after it was tried.
188
- ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return h(n,()=>{if(n.wouldApply){l(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)l(` ${r}`);return}if(n.upToDate){l(`${e} is up to date.`);return}l(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function Vt(e,t={},n=()=>{}){let r=Ja(t.dir===void 0?Wt(process.cwd(),"migrations"):Ka(t.dir)?t.dir:Wt(process.cwd(),t.dir)),o=[];function s(p){o.push(p),n(p)}let a=Ga(r);if(a.length===0)return s(`No .sql files in ${Mr(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let i=await W(e);i.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let d=await za(i),u=rt(a,d,{outOfOrder:t.outOfOrder});if(!u.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...u.problems],notes:o};let m=new Map(a.map(p=>[p.name,p])),c=u.pending.map(p=>m.get(p.name));if(c.length===0)return s(`${e} is up to date: ${d.length} migration${d.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let p of c)$e(p.sql)||s(`${p.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:c.map(p=>p.name),notes:o}}let f=[];for(let p of c){let k=`insert into ${Re} (name, checksum) values (${Cr(p.name)}, ${Cr(p.checksum)});`,w=$e(p.sql);w||s(`${p.name} runs outside a transaction, so an interruption can leave it half applied.`);let g=w?`begin;
226
+ );`;function ui(e){if(!ri(e)||!ai(e).isDirectory())throw new Error(`no migrations folder at ${Jr(e)}. Put your .sql files there, or pass --dir.`);return oi(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=Vt(e,t),r=si(n,"utf8");return{name:t,path:n,sql:r,checksum:ni("sha256").update(r,"utf8").digest("hex")}})}function zt(e,t,n=[]){return new Promise(r=>{let o=ti("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:"require"}}),s="",a="";o.stdout.on("data",i=>s+=String(i)),o.stderr.on("data",i=>a+=String(i)),o.on("error",i=>{r({code:127,out:"",err:i.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(i)})}),o.on("close",i=>r({code:i??0,out:s,err:a})),o.stdin.end(t)})}async function mi(e){let t=await zt(e,di);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${Re}`);let n=await zt(e,`select name || E'\\t' || checksum from ${Re};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${Re}`);return n.out.split(`
227
+ `).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 Wr(e){return`'${e.replace(/'/g,"''")}'`}async function Gr(e,t={}){let n=await Yt(e,t,r=>l(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
228
+ `);E("conflict",`Refused. This folder and ${e} do not agree:
229
+ ${r}`,{ref:e,problems:n.problems})}E("failed",`${n.failed} failed, and nothing after it was tried.
230
+ ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return h(n,()=>{if(n.wouldApply){l(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)l(` ${r}`);return}if(n.upToDate){l(`${e} is up to date.`);return}l(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function Yt(e,t={},n=()=>{}){let r=li(t.dir===void 0?Vt(process.cwd(),"migrations"):ii(t.dir)?t.dir:Vt(process.cwd(),t.dir)),o=[];function s(p){o.push(p),n(p)}let a=ui(r);if(a.length===0)return s(`No .sql files in ${Jr(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let i=await W(e);i.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let d=await mi(i),u=rt(a,d,{outOfOrder:t.outOfOrder});if(!u.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...u.problems],notes:o};let m=new Map(a.map(p=>[p.name,p])),c=u.pending.map(p=>m.get(p.name));if(c.length===0)return s(`${e} is up to date: ${d.length} migration${d.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let p of c)$e(p.sql)||s(`${p.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:c.map(p=>p.name),notes:o}}let f=[];for(let p of c){let k=`insert into ${Re} (name, checksum) values (${Wr(p.name)}, ${Wr(p.checksum)});`,S=$e(p.sql);S||s(`${p.name} runs outside a transaction, so an interruption can leave it half applied.`);let g=S?`begin;
189
231
  ${p.sql}
190
232
  ${k}
191
233
  commit;
192
234
  `:`${p.sql}
193
235
  ${k}
194
- `,E=await Jt(i,g);if(E.code!==0)return w&&s("It ran inside a transaction, so nothing from it is in the database."),{ref:e,directory:r,ok:!1,applied:f,failed:p.name,error:E.err.trim(),notes:o};f.push(p.name),s(` applied ${p.name}`)}return{ref:e,directory:r,ok:!0,applied:f,notes:o}}M();re();F();import{spawn as ci}from"node:child_process";import{writeFileSync as li}from"node:fs";import{isAbsolute as di,resolve as ui}from"node:path";var Ya=" ";function b(e){return Ya.repeat(e)}function B(e){return JSON.stringify(e)}function ee(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:B(e)}function me(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function Xa(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 Za={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"},Qa={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function ei(e,t){let n=Qa[e]?.[t];return n||(Za[t]??"unknown")}function ot(e,t){let{base:n,array:r}=Xa(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=ei(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function ti(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function Gt(e,t,n,r){let o=ot(e,t.dataType),s=t.nullable?`${o} | null`:o;return`${b(n)}${ee(t.name)}${r?"?":""}: ${s}`}function ni(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,i)=>a===n[i])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function ri(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(`${b(t)}Relationships: []`),n;n.push(`${b(t)}Relationships: [`);for(let o of r)n.push(`${b(t+1)}{`),n.push(`${b(t+2)}foreignKeyName: ${B(o.constraintName)}`),n.push(`${b(t+2)}columns: [${o.columns.map(B).join(", ")}]`),n.push(`${b(t+2)}isOneToOne: ${ni(e,o)?"true":"false"}`),n.push(`${b(t+2)}referencedRelation: ${B(o.referencedTable)}`),n.push(`${b(t+2)}referencedColumns: [${o.referencedColumns.map(B).join(", ")}]`),n.push(`${b(t+1)}},`);return n.push(`${b(t)}]`),n}function Ur(e,t,n){let r=me(t.columns),o=[];o.push(`${b(n)}${ee(t.name)}: {`),o.push(`${b(n+1)}Row: {`);for(let s of r)o.push(Gt(e,s,n+2,!1));if(o.push(`${b(n+1)}}`),t.kind!=="view"){o.push(`${b(n+1)}Insert: {`);for(let s of r)o.push(Gt(e,s,n+2,ti(s)));o.push(`${b(n+1)}}`),o.push(`${b(n+1)}Update: {`);for(let s of r)o.push(Gt(e,s,n+2,!0));o.push(`${b(n+1)}}`)}return o.push(...ri(t,n+1)),o.push(`${b(n)}}`),o}function Oe(e,t){return[`${b(t)}${e}: {`,`${b(t+1)}[_ in never]: never`,`${b(t)}}`]}function oi(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}[]`:`${ot(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??ot(e,n)}function si(e,t,n){let r=[],o=me((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${b(n)}${ee(t.name)}: {`),o.length===0)r.push(`${b(n+1)}Args: never`);else{r.push(`${b(n+1)}Args: {`);for(let s of o)r.push(`${b(n+2)}${ee(s.name)}: ${ot(e,s.dataType)}`);r.push(`${b(n+1)}}`)}return r.push(`${b(n+1)}Returns: ${oi(e,t)}`),r.push(`${b(n)}}`),r}function xe(e,t){return e.schema&&e.schema.length>0?e.schema:t}function Fr(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let c of o){let f=c.schema&&c.schema.length>0?c.schema:n;s.set(c.name.toLowerCase(),`Database[${B(f)}][${B("Enums")}][${B(c.name)}]`)}let a=new Map;for(let c of e.tables){let f=xe(c,n),p=c.kind==="view"?"Views":"Tables";a.set(c.name.toLowerCase(),`Database[${B(f)}][${B(p)}][${B(c.name)}][${B("Row")}]`)}let i={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},d=new Set([n]);for(let c of e.tables)d.add(xe(c,n));for(let c of e.routines??[])d.add(xe(c,n));for(let c of o)d.add(c.schema&&c.schema.length>0?c.schema:n);let u=[...d].sort(),m=[];m.push("export type Json ="),m.push(" | string"),m.push(" | number"),m.push(" | boolean"),m.push(" | null"),m.push(" | { [key: string]: Json | undefined }"),m.push(" | Json[]"),m.push(""),m.push("export type Database = {"),t.postgrestVersion&&(m.push(`${b(1)}__InternalSupabase: {`),m.push(`${b(2)}PostgrestVersion: ${B(t.postgrestVersion)}`),m.push(`${b(1)}}`));for(let c of u){let f=me(e.tables.filter(g=>xe(g,n)===c&&g.kind!=="view")),p=me(e.tables.filter(g=>xe(g,n)===c&&g.kind==="view")),k=me((e.routines??[]).filter(g=>xe(g,n)===c)),w=me(o.filter(g=>(g.schema&&g.schema.length>0?g.schema:n)===c));if(m.push(`${b(1)}${ee(c)}: {`),f.length===0)m.push(...Oe("Tables",2));else{m.push(`${b(2)}Tables: {`);for(let g of f)m.push(...Ur(i,g,3));m.push(`${b(2)}}`)}if(p.length===0)m.push(...Oe("Views",2));else{m.push(`${b(2)}Views: {`);for(let g of p)m.push(...Ur(i,g,3));m.push(`${b(2)}}`)}if(k.length===0)m.push(...Oe("Functions",2));else{m.push(`${b(2)}Functions: {`);for(let g of k)m.push(...si(i,g,3));m.push(`${b(2)}}`)}if(w.length===0)m.push(...Oe("Enums",2));else{m.push(`${b(2)}Enums: {`);for(let g of w)m.push(`${b(3)}${ee(g.name)}: ${g.values.map(B).join(" | ")}`);m.push(`${b(2)}}`)}m.push(...Oe("CompositeTypes",2)),m.push(`${b(1)}}`)}return m.push("}"),t.helpers!==!1&&(m.push(""),m.push(ii(n))),t.constants!==!1&&(m.push(""),m.push(ai(u,o,n))),`${m.join(`
236
+ `,_=await zt(i,g);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:f,failed:p.name,error:_.err.trim(),notes:o};f.push(p.name),s(` applied ${p.name}`)}return{ref:e,directory:r,ok:!0,applied:f,notes:o}}M();oe();F();import{spawn as Ei}from"node:child_process";import{writeFileSync as _i}from"node:fs";import{isAbsolute as Ti,resolve as Ai}from"node:path";var fi=" ";function x(e){return fi.repeat(e)}function B(e){return JSON.stringify(e)}function ee(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:B(e)}function me(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function pi(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 gi={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"},hi={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function yi(e,t){let n=hi[e]?.[t];return n||(gi[t]??"unknown")}function ot(e,t){let{base:n,array:r}=pi(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=yi(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function wi(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function Xt(e,t,n,r){let o=ot(e,t.dataType),s=t.nullable?`${o} | null`:o;return`${x(n)}${ee(t.name)}${r?"?":""}: ${s}`}function bi(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,i)=>a===n[i])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function Si(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(`${x(t)}Relationships: []`),n;n.push(`${x(t)}Relationships: [`);for(let o of r)n.push(`${x(t+1)}{`),n.push(`${x(t+2)}foreignKeyName: ${B(o.constraintName)}`),n.push(`${x(t+2)}columns: [${o.columns.map(B).join(", ")}]`),n.push(`${x(t+2)}isOneToOne: ${bi(e,o)?"true":"false"}`),n.push(`${x(t+2)}referencedRelation: ${B(o.referencedTable)}`),n.push(`${x(t+2)}referencedColumns: [${o.referencedColumns.map(B).join(", ")}]`),n.push(`${x(t+1)}},`);return n.push(`${x(t)}]`),n}function Vr(e,t,n){let r=me(t.columns),o=[];o.push(`${x(n)}${ee(t.name)}: {`),o.push(`${x(n+1)}Row: {`);for(let s of r)o.push(Xt(e,s,n+2,!1));if(o.push(`${x(n+1)}}`),t.kind!=="view"){o.push(`${x(n+1)}Insert: {`);for(let s of r)o.push(Xt(e,s,n+2,wi(s)));o.push(`${x(n+1)}}`),o.push(`${x(n+1)}Update: {`);for(let s of r)o.push(Xt(e,s,n+2,!0));o.push(`${x(n+1)}}`)}return o.push(...Si(t,n+1)),o.push(`${x(n)}}`),o}function Oe(e,t){return[`${x(t)}${e}: {`,`${x(t+1)}[_ in never]: never`,`${x(t)}}`]}function xi(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}[]`:`${ot(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??ot(e,n)}function ki(e,t,n){let r=[],o=me((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${x(n)}${ee(t.name)}: {`),o.length===0)r.push(`${x(n+1)}Args: never`);else{r.push(`${x(n+1)}Args: {`);for(let s of o)r.push(`${x(n+2)}${ee(s.name)}: ${ot(e,s.dataType)}`);r.push(`${x(n+1)}}`)}return r.push(`${x(n+1)}Returns: ${xi(e,t)}`),r.push(`${x(n)}}`),r}function ke(e,t){return e.schema&&e.schema.length>0?e.schema:t}function zr(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let c of o){let f=c.schema&&c.schema.length>0?c.schema:n;s.set(c.name.toLowerCase(),`Database[${B(f)}][${B("Enums")}][${B(c.name)}]`)}let a=new Map;for(let c of e.tables){let f=ke(c,n),p=c.kind==="view"?"Views":"Tables";a.set(c.name.toLowerCase(),`Database[${B(f)}][${B(p)}][${B(c.name)}][${B("Row")}]`)}let i={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},d=new Set([n]);for(let c of e.tables)d.add(ke(c,n));for(let c of e.routines??[])d.add(ke(c,n));for(let c of o)d.add(c.schema&&c.schema.length>0?c.schema:n);let u=[...d].sort(),m=[];m.push("export type Json ="),m.push(" | string"),m.push(" | number"),m.push(" | boolean"),m.push(" | null"),m.push(" | { [key: string]: Json | undefined }"),m.push(" | Json[]"),m.push(""),m.push("export type Database = {"),t.postgrestVersion&&(m.push(`${x(1)}__InternalSupabase: {`),m.push(`${x(2)}PostgrestVersion: ${B(t.postgrestVersion)}`),m.push(`${x(1)}}`));for(let c of u){let f=me(e.tables.filter(g=>ke(g,n)===c&&g.kind!=="view")),p=me(e.tables.filter(g=>ke(g,n)===c&&g.kind==="view")),k=me((e.routines??[]).filter(g=>ke(g,n)===c)),S=me(o.filter(g=>(g.schema&&g.schema.length>0?g.schema:n)===c));if(m.push(`${x(1)}${ee(c)}: {`),f.length===0)m.push(...Oe("Tables",2));else{m.push(`${x(2)}Tables: {`);for(let g of f)m.push(...Vr(i,g,3));m.push(`${x(2)}}`)}if(p.length===0)m.push(...Oe("Views",2));else{m.push(`${x(2)}Views: {`);for(let g of p)m.push(...Vr(i,g,3));m.push(`${x(2)}}`)}if(k.length===0)m.push(...Oe("Functions",2));else{m.push(`${x(2)}Functions: {`);for(let g of k)m.push(...ki(i,g,3));m.push(`${x(2)}}`)}if(S.length===0)m.push(...Oe("Enums",2));else{m.push(`${x(2)}Enums: {`);for(let g of S)m.push(`${x(3)}${ee(g.name)}: ${g.values.map(B).join(" | ")}`);m.push(`${x(2)}}`)}m.push(...Oe("CompositeTypes",2)),m.push(`${x(1)}}`)}return m.push("}"),t.helpers!==!1&&(m.push(""),m.push(Pi(n))),t.constants!==!1&&(m.push(""),m.push(vi(u,o,n))),`${m.join(`
195
237
  `)}
196
- `}function ai(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=me(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${b(1)}${ee(o)}: {`),r.push(`${b(2)}Enums: {},`),r.push(`${b(1)}},`);continue}r.push(`${b(1)}${ee(o)}: {`),r.push(`${b(2)}Enums: {`);for(let a of s)r.push(`${b(3)}${ee(a.name)}: [${a.values.map(B).join(", ")}],`);r.push(`${b(2)}},`),r.push(`${b(1)}},`)}return r.push("} as const"),r.join(`
197
- `)}function ii(e){return`type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
238
+ `}function vi(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=me(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${x(1)}${ee(o)}: {`),r.push(`${x(2)}Enums: {},`),r.push(`${x(1)}},`);continue}r.push(`${x(1)}${ee(o)}: {`),r.push(`${x(2)}Enums: {`);for(let a of s)r.push(`${x(3)}${ee(a.name)}: [${a.values.map(B).join(", ")}],`);r.push(`${x(2)}},`),r.push(`${x(1)}},`)}return r.push("} as const"),r.join(`
239
+ `)}function Pi(e){return`type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
198
240
 
199
241
  type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${B(e)}>]
200
242
 
@@ -309,7 +351,7 @@ export type CompositeTypes<
309
351
  ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
310
352
  : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
311
353
  ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
312
- : never`}function mi(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:"require",label:e.ref}}function st(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 zt(e,t){return new Promise(n=>{let r=ci("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 fi=`
354
+ : never`}function $i(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:"require",label:e.ref}}function st(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 Zt(e,t){return new Promise(n=>{let r=Ei("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 Ri=`
313
355
  with wanted as (select unnest($SCHEMAS$::text[]) as nspname)
314
356
  select json_build_object(
315
357
  'tables', coalesce((
@@ -412,28 +454,28 @@ select json_build_object(
412
454
  where t.typtype = 'e' and n.nspname in (select nspname from wanted)
413
455
  ), '[]'::json)
414
456
  )::text
415
- `;function pi(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function gi(e,t,n=0){let r=e.tables.map(a=>{let i=a.primaryKey??[],d=new Set(i),u=a.columns.map(m=>({name:m.name,dataType:m.dataType,nullable:m.nullable,isPrimaryKey:d.has(m.name),default:m.default,autoIncrement:m.autoIncrement}));return{name:a.name,schema:a.schema,kind:a.kind,columns:u,primaryKey:i,foreignKeys:(a.foreignKeys??[]).map(m=>({constraintName:m.constraintName,columns:m.columns??[],referencedTable:m.referencedTable,referencedColumns:m.referencedColumns??[]})),indexes:(a.indexes??[]).filter(m=>Array.isArray(m.columns)&&m.columns.every(c=>typeof c=="string")).map(m=>({name:m.name,unique:m.unique,columns:m.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(i=>({name:i.name,dataType:i.dataType,mode:i.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 hi(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?st(e.dbUrl):mi(await W(yi(e))),r=fi.replace("$SCHEMAS$",pi(t)),o=await(e.sql?e.sql(r):zt(n,r));o.code===127&&P("tool-missing",o.err.trim()),o.code!==0&&P("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||P("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{P("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:i,enums:d}=gi(a,n.database,Date.now()),u=Fr(i,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:d,postgrestVersion:e.dataApiVersion}),m=null;return e.out&&(m=di(e.out)?e.out:ui(process.cwd(),e.out),li(m,u,"utf8")),{target:n.label,schemas:t,tables:i.tables.filter(c=>c.kind!=="view").length,views:i.tables.filter(c=>c.kind==="view").length,functions:(i.routines??[]).length,enums:d.length,path:m,types:u}}function yi(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 Br(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&P("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await hi(t);return r.path&&l(`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}.`),h(r,()=>{r.path||process.stdout.write(r.types)}),0}re();import{createHash as kc}from"node:crypto";import{existsSync as fn,mkdirSync as xc,readFileSync as pn,readdirSync as vc,statSync as Pc,writeFileSync as Ec}from"node:fs";import{dirname as _c,isAbsolute as Xo,join as Ce,resolve as Zo}from"node:path";import{pathToFileURL as Tc}from"node:url";F();function Ac(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:Xo(e)||e.startsWith("./")||e.startsWith("../")?Tc(Zo(e)).href:e}function $c(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(Ac(e)):Promise.resolve().then(()=>(zo(),Go))}function Rc(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 Oc="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 Me(){let e;try{e=await $c()}catch(t){P("tool-missing",Rc(t))}return{ready:()=>e.podmanReady(Oc),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 Qo(e){return Ce(e,".snoutdata","local.json")}function mt(e){let t=Qo(e);if(!fn(t))return null;try{let n=JSON.parse(pn(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 Nc(e,t){let n=Qo(e);return xc(_c(n),{recursive:!0}),Ec(n,`${JSON.stringify(t,null,2)}
416
- `,{encoding:"utf8",mode:384}),n}var Dc=54322;async function es(e){let t=await e.ensureImage(r=>l(r));t&&P("tool-missing",t);let n=await e.ready();n&&P("tool-missing",n),await e.prepare()}function ts(e,t,n,r=zt){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 Ic(e,t={}){let n=t.cwd??process.cwd();await es(e);let r=mt(n),o=r??e.mint(t.port??Dc),s=r?null:Nc(n,o),a=await e.start(o,c=>l(c)),i=e.uri(o);if(t.noMigrations)return{status:a,uri:i,applied:[],seeded:!1,created:s};let d=ts(e,o,st(i)),u=await jc(d,n,t),m=await Cc(d,n,u.length>0||!!s);return{status:a,uri:i,applied:u,seeded:m,created:s}}async function jc(e,t,n){let r=Zo(n.dir===void 0?Ce(t,"migrations"):Xo(n.dir)?n.dir:Ce(t,n.dir));if(!fn(r)||!Pc(r).isDirectory())return[];let o=vc(r).filter(c=>c.endsWith(".sql")).map(c=>{let f=pn(Ce(r,c),"utf8");return{name:c,sql:f,checksum:kc("sha256").update(f,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(Mc);s.code!==0&&P(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${je}`);let a=await e(`select name || E'\\t' || checksum from ${je};`);a.code!==0&&P("failed",a.err.trim()||`could not read ${je}`);let i=a.out.split(`
417
- `).map(c=>c.trim()).filter(c=>c.length>0).map(c=>{let f=c.indexOf(" ");return{name:c.slice(0,f),checksum:c.slice(f+1)}}),d=rt(o,i,{outOfOrder:n.outOfOrder});d.ok||P("conflict",`Refused. This folder and the local database do not agree:
457
+ `;function Oi(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Ni(e,t,n=0){let r=e.tables.map(a=>{let i=a.primaryKey??[],d=new Set(i),u=a.columns.map(m=>({name:m.name,dataType:m.dataType,nullable:m.nullable,isPrimaryKey:d.has(m.name),default:m.default,autoIncrement:m.autoIncrement}));return{name:a.name,schema:a.schema,kind:a.kind,columns:u,primaryKey:i,foreignKeys:(a.foreignKeys??[]).map(m=>({constraintName:m.constraintName,columns:m.columns??[],referencedTable:m.referencedTable,referencedColumns:m.referencedColumns??[]})),indexes:(a.indexes??[]).filter(m=>Array.isArray(m.columns)&&m.columns.every(c=>typeof c=="string")).map(m=>({name:m.name,unique:m.unique,columns:m.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(i=>({name:i.name,dataType:i.dataType,mode:i.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 Di(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?st(e.dbUrl):$i(await W(Ii(e))),r=Ri.replace("$SCHEMAS$",Oi(t)),o=await(e.sql?e.sql(r):Zt(n,r));o.code===127&&E("tool-missing",o.err.trim()),o.code!==0&&E("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||E("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{E("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:i,enums:d}=Ni(a,n.database,Date.now()),u=zr(i,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:d,postgrestVersion:e.dataApiVersion}),m=null;return e.out&&(m=Ti(e.out)?e.out:Ai(process.cwd(),e.out),_i(m,u,"utf8")),{target:n.label,schemas:t,tables:i.tables.filter(c=>c.kind!=="view").length,views:i.tables.filter(c=>c.kind==="view").length,functions:(i.routines??[]).length,enums:d.length,path:m,types:u}}function Ii(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 Yr(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&E("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await Di(t);return r.path&&l(`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}.`),h(r,()=>{r.path||process.stdout.write(r.types)}),0}oe();import{createHash as Lc}from"node:crypto";import{existsSync as wn,mkdirSync as Uc,readFileSync as bn,readdirSync as Fc,statSync as Bc,writeFileSync as Hc}from"node:fs";import{dirname as qc,isAbsolute as ds,join as Ce,resolve as us}from"node:path";import{pathToFileURL as Kc}from"node:url";F();function Wc(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:ds(e)||e.startsWith("./")||e.startsWith("../")?Kc(us(e)).href:e}function Jc(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(Wc(e)):Promise.resolve().then(()=>(cs(),is))}function Gc(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 Vc="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 Me(){let e;try{e=await Jc()}catch(t){E("tool-missing",Gc(t))}return{ready:()=>e.podmanReady(Vc),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 ms(e){return Ce(e,".snoutdata","local.json")}function ft(e){let t=ms(e);if(!wn(t))return null;try{let n=JSON.parse(bn(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 zc(e,t){let n=ms(e);return Uc(qc(n),{recursive:!0}),Hc(n,`${JSON.stringify(t,null,2)}
458
+ `,{encoding:"utf8",mode:384}),n}var Yc=54322;async function fs(e){let t=await e.ensureImage(r=>l(r));t&&E("tool-missing",t);let n=await e.ready();n&&E("tool-missing",n),await e.prepare()}function ps(e,t,n,r=Zt){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 Xc(e,t={}){let n=t.cwd??process.cwd();await fs(e);let r=ft(n),o=r??e.mint(t.port??Yc),s=r?null:zc(n,o),a=await e.start(o,c=>l(c)),i=e.uri(o);if(t.noMigrations)return{status:a,uri:i,applied:[],seeded:!1,created:s};let d=ps(e,o,st(i)),u=await Zc(d,n,t),m=await Qc(d,n,u.length>0||!!s);return{status:a,uri:i,applied:u,seeded:m,created:s}}async function Zc(e,t,n){let r=us(n.dir===void 0?Ce(t,"migrations"):ds(n.dir)?n.dir:Ce(t,n.dir));if(!wn(r)||!Bc(r).isDirectory())return[];let o=Fc(r).filter(c=>c.endsWith(".sql")).map(c=>{let f=bn(Ce(r,c),"utf8");return{name:c,sql:f,checksum:Lc("sha256").update(f,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(el);s.code!==0&&E(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${je}`);let a=await e(`select name || E'\\t' || checksum from ${je};`);a.code!==0&&E("failed",a.err.trim()||`could not read ${je}`);let i=a.out.split(`
459
+ `).map(c=>c.trim()).filter(c=>c.length>0).map(c=>{let f=c.indexOf(" ");return{name:c.slice(0,f),checksum:c.slice(f+1)}}),d=rt(o,i,{outOfOrder:n.outOfOrder});d.ok||E("conflict",`Refused. This folder and the local database do not agree:
418
460
  ${d.problems.map(c=>` ${c.reason}`).join(`
419
- `)}`,{problems:d.problems});let u=new Map(o.map(c=>[c.name,c])),m=[];for(let c of d.pending){let f=u.get(c.name),p=`insert into ${je} (name, checksum) values (${Yo(f.name)}, ${Yo(f.checksum)});`,k=$e(f.sql);k||l(`${f.name} runs outside a transaction, so an interruption can leave it half applied.`);let w=k?`begin;
461
+ `)}`,{problems:d.problems});let u=new Map(o.map(c=>[c.name,c])),m=[];for(let c of d.pending){let f=u.get(c.name),p=`insert into ${je} (name, checksum) values (${ls(f.name)}, ${ls(f.checksum)});`,k=$e(f.sql);k||l(`${f.name} runs outside a transaction, so an interruption can leave it half applied.`);let S=k?`begin;
420
462
  ${f.sql}
421
463
  ${p}
422
464
  commit;
423
465
  `:`${f.sql}
424
466
  ${p}
425
- `,g=await e(w);g.code!==0&&P("failed",`${f.name} failed, and nothing after it was tried.
426
- ${g.err.trim()}`,{failed:f.name,applied:m}),m.push(f.name),l(` applied ${f.name}`)}return m}async function Cc(e,t,n){let r=Ce(t,"seed.sql");if(!n||!fn(r))return!1;let o=await e(pn(r,"utf8"));return o.code!==0&&P("failed",`seed.sql failed.
427
- ${o.err.trim()}`),l(" ran seed.sql"),!0}var je="_snoutdata_migrations",Mc=`create table if not exists ${je} (
467
+ `,g=await e(S);g.code!==0&&E("failed",`${f.name} failed, and nothing after it was tried.
468
+ ${g.err.trim()}`,{failed:f.name,applied:m}),m.push(f.name),l(` applied ${f.name}`)}return m}async function Qc(e,t,n){let r=Ce(t,"seed.sql");if(!n||!wn(r))return!1;let o=await e(bn(r,"utf8"));return o.code!==0&&E("failed",`seed.sql failed.
469
+ ${o.err.trim()}`),l(" ran seed.sql"),!0}var je="_snoutdata_migrations",el=`create table if not exists ${je} (
428
470
  name text primary key,
429
471
  checksum text not null,
430
472
  applied_at timestamptz not null default now()
431
- );`;function Yo(e){return`'${e.replace(/'/g,"''")}'`}function Lc(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 ns(e,t={}){let n=await Ic(e,t);return h(n,()=>{n.created&&(l(""),l(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),l(""),l(` ${T("Database")} running on port ${n.status.port}`),n.applied.length>0&&l(` ${T("Migrations")} ${n.applied.length} applied`),n.seeded&&l(` ${T("Seed")} seed.sql ran`),l(""),process.stdout.write(`${n.uri}
432
- `),l(x(" snoutdata gen types typescript --local > database.types.ts")),l(x(" snoutdata stop"))}),0}async function rs(e,t={}){let n=t.cwd??process.cwd(),r=mt(n);r||P("not-found","there is no local database for this folder. `snoutdata start` makes one."),await es(e);let o=await e.stop(r.ref);return h(o,()=>l("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function os(e,t={}){let n=t.cwd??process.cwd(),r=mt(n);if(!r)return h({exists:!1,ref:null,status:"absent"},()=>l("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready();if(o)return h({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>l(o)),0;let s=await e.status(r);return h({...s,uri:s.uri},()=>{l(` ${T("Project")} ${s.ref}`),l(` ${T("State")} ${s.ready?"running":s.status}`),l(` ${T("Port")} ${s.port}`),l(` ${T("Size")} ${Lc(s.databaseBytes)}`),s.services.length>0&&l(` ${T("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
433
- `)}),0}async function ss(e,t=process.cwd()){let n=mt(t);n||P("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:ts(e,n,st(r))}}M();F();var Uc="2024-11-05";function N(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var _={type:"string"},ft=[{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:N({})},{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.",inputSchema:N({})},{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:N({name:_,region:_,teamId:_},["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:N({ref:_},["ref"])},{name:"pause_project",description:"Stop a project. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:N({ref:_},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it.",inputSchema:N({ref:_},["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:N({ref:_,dir:_,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:N({ref:_,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:N({ref:_},["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:N({ref:_},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING IMMEDIATELY, including any DATABASE_URL already written into a file or a running process. Returns the new connection URL. Use it when a credential has leaked, not routinely.",inputSchema:N({ref:_},["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:N({})},{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:N({})},{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. It is as capable as this account, so hand it over deliberately. Optionally expires, as an ISO date.",inputSchema:N({name:_,expires:_},["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:N({id:_},["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:N({ref:_,name:_,dir:_,entrypoint:_,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, sizes, and whether each one needs an API key. Costs nothing.",inputSchema:N({ref:_},["ref"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:N({ref:_,name:_},["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:N({ref:_},["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 or a key.",inputSchema:N({ref:_},["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:N({ref:_},["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:N({ref:_,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:N({ref:_},["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:N({ref:_,hostname:_},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:N({ref:_,hostname:_},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:N({ref:_,hostname:_},["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:N({ref:_},["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:N({ref:_,at:_,name:_},["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:N({ref:_},["ref"])}],Fc=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"]),Bc=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]);function Hc(e){return{...e,annotations:Fc.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:Bc.has(e.name)}}}function qc(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function Le(e,t){return{jsonrpc:"2.0",id:e,result:t}}function gn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function v(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return Le(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function j(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function as(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 Le(r,{protocolVersion:Uc,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return Le(r,{});case"tools/list":return Le(r,{tools:qc([...ft.map(Hc),...n.borrowed??[]])});case"tools/call":return Kc(r,s,t,n);default:return gn(r,-32601,`unknown method: ${o}`)}}async function Kc(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(ft.some(u=>u.name===o)||(r.borrowed?.some(u=>u.name===o)??!1)))return gn(e,-32602,`unknown tool: ${o}`);let i=["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"],d=j(s,"ref");if(i.includes(o)&&!d)return v(e,`${o} needs a project ref. Call list_projects to see them.`,!0);try{switch(o){case"whoami":return v(e,await n.whoami());case"list_projects":return v(e,await n.listProjects());case"create_project":{let u=j(s,"name");return u?v(e,await n.createProject({name:u,region:j(s,"region")??void 0,teamId:j(s,"teamId")??void 0})):v(e,"create_project needs a name.",!0)}case"get_connection_url":{let u=await n.connection(d);return v(e,{url:u.uri,state:u.state,wakesInstantly:u.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 v(e,await n.setProjectState("pause",d));case"resume_project":return v(e,await n.setProjectState("resume",d));case"usage":{let u=typeof s.days=="number"?s.days:void 0;return v(e,await n.usage(d,u))}case"export_status":return v(e,await n.exportStatus(d));case"start_export":return v(e,await n.requestExport(d));case"reset_password":return v(e,await n.resetPassword(d));case"list_teams":return v(e,await n.listTeams());case"get_project":return v(e,await n.getProject(d));case"list_products":return v(e,await n.getProducts(d));case"set_product":{let u=j(s,"product");return u!=="auth"&&u!=="storage"&&u!=="data-api"?v(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?v(e,"set_product needs enabled: true or false.",!0):v(e,await n.setProduct(d,u,s.enabled))}case"list_domains":return v(e,await n.listDomains(d));case"add_domain":case"verify_domain":case"remove_domain":{let u=j(s,"hostname");return u?v(e,await n.domainAction(d,o.split("_")[0],u)):v(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":return v(e,await n.restoreWindow(d));case"restore_to_point":{let u=j(s,"at");return u?v(e,await n.restoreTo(d,u,j(s,"name")??void 0)):v(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let u=j(s,"ref"),m=j(s,"name");return!u||!m?v(e,"deploy_function needs a project ref and a function name.",!0):v(e,await n.deployFunction(u,m,{dir:j(s,"dir")??void 0,entrypoint:j(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let u=j(s,"ref");return u?v(e,await n.listFunctions(u)):v(e,"list_functions needs a project ref.",!0)}case"delete_function":{let u=j(s,"ref"),m=j(s,"name");return!u||!m?v(e,"delete_function needs a project ref and a function name.",!0):v(e,await n.deleteFunction(u,m))}case"list_function_secrets":{let u=j(s,"ref");return u?v(e,await n.listFunctionSecrets(u)):v(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return v(e,await n.listTokens());case"create_token":{let u=j(s,"name");return u?v(e,await n.createToken(u,j(s,"expires")??void 0)):v(e,"create_token needs a name, so somebody can tell later what it was for.",!0)}case"revoke_token":{let u=j(s,"id");return u?v(e,await n.revokeToken(u)):v(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return v(e,await n.pushMigrations(d,{dir:j(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?v(e,await n.setProjectState("delete",d)):v(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(u=>u.name===o)?n.callBorrowed?Le(e,await n.callBorrowed(o,s)):v(e,`${o} needs the SnoutData app, which is no longer answering.`,!0):gn(e,-32602,`unknown tool: ${o}`)}}catch(u){return v(e,u instanceof Error?u.message:String(u),!0)}}function is(e){let t=e.split(`
434
- `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}Je();function Wc(){return{whoami:()=>be(),listProjects:()=>V(),createProject:async e=>{let t=await S("cloud-project-create",{name:e.name,...e.region?{region:e.region}:{},...e.teamId?{teamId:e.teamId}:{}});try{return await Ae(t.project.ref)}catch{return{...t.project,ready:!1,note:`${t.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:(e,t)=>S("cloud-project-usage",{ref:e,...t?{days:t}:{}}),exportStatus:e=>Se(e),requestExport:e=>Te(e),resetPassword:e=>S("cloud-project-reset-password",{ref:e}),listTeams:()=>S("cloud-team-list",{}),deployFunction:(e,t,n)=>Dt(e,t,n).then(r=>r.answer),listFunctions:e=>It(e),deleteFunction:(e,t)=>jt(e,t),listFunctionSecrets:e=>S("cloud-project-secrets",{ref:e}),listTokens:()=>S("cloud-token-list",{}),createToken:(e,t)=>S("cloud-token-create",{name:e,...t?{expires:t}:{}}),revokeToken:e=>S("cloud-token-revoke",{id:e}),getProject:e=>Kt(e),getProducts:e=>tt(e),setProduct:(e,t,n)=>Ft(e,t,n),listDomains:e=>nt(e),domainAction:(e,t,n)=>Bt(e,t,n),restoreWindow:e=>Ht(e),restoreTo:(e,t,n)=>qt(e,t,n),setProjectState:(e,t)=>S(`cloud-project-${e}`,{ref:t}),connection:e=>W(e),pushMigrations:(e,t)=>Vt(e,{dir:t.dir,dryRun:t.dryRun})}}var cs="app_";async function Jc(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Ke();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await In(t)).map(o=>({name:`${cs}${o.name}`,description:`[SnoutData app, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function ls(e){_e(!0);let{tools:t,config:n}=await Jc(),r=Wc();n&&(r.callBorrowed=(d,u)=>jn(n,d.slice(cs.length),u));let o={...e,borrowed:t};K(t.length>0?`snoutdata mcp: ${ft.length} cloud tools, and ${t.length} borrowed from the SnoutData app on this machine.`:"snoutdata mcp: cloud tools only. The SnoutData app is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let d=await be();K(`snoutdata mcp: signed in as ${d.email??"this account"}.`)}catch(d){return K(`snoutdata mcp: ${d instanceof Error?d.message:String(d)}`),K("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}K(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function i(d){a.add(d),d.finally(()=>a.delete(d))}return new Promise(d=>{process.stdin.on("data",u=>{let m=is(s+u);s=m.rest;for(let c of m.messages)i(Vc(c,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);d(0)})()}),process.stdin.on("error",()=>d(1))})}async function Vc(e,t,n){let r;try{r=JSON.parse(e)}catch{K(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await as(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
473
+ );`;function ls(e){return`'${e.replace(/'/g,"''")}'`}function tl(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 gs(e,t={}){let n=await Xc(e,t);return h(n,()=>{n.created&&(l(""),l(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),l(""),l(` ${P("Database")} running on port ${n.status.port}`),n.applied.length>0&&l(` ${P("Migrations")} ${n.applied.length} applied`),n.seeded&&l(` ${P("Seed")} seed.sql ran`),l(""),process.stdout.write(`${n.uri}
474
+ `),l(b(" snoutdata gen types typescript --local > database.types.ts")),l(b(" snoutdata stop"))}),0}async function hs(e,t={}){let n=t.cwd??process.cwd(),r=ft(n);r||E("not-found","there is no local database for this folder. `snoutdata start` makes one."),await fs(e);let o=await e.stop(r.ref);return h(o,()=>l("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function ys(e,t={}){let n=t.cwd??process.cwd(),r=ft(n);if(!r)return h({exists:!1,ref:null,status:"absent"},()=>l("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready();if(o)return h({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>l(o)),0;let s=await e.status(r);return h({...s,uri:s.uri},()=>{l(` ${P("Project")} ${s.ref}`),l(` ${P("State")} ${s.ready?"running":s.status}`),l(` ${P("Port")} ${s.port}`),l(` ${P("Size")} ${tl(s.databaseBytes)}`),s.services.length>0&&l(` ${P("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
475
+ `)}),0}async function ws(e,t=process.cwd()){let n=ft(t);n||E("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:ps(e,n,st(r))}}M();F();var nl="2024-11-05";function N(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var T={type:"string"},pt=[{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:N({})},{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.",inputSchema:N({})},{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:N({name:T,region:T,teamId:T},["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:N({ref:T},["ref"])},{name:"pause_project",description:"Stop a project. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:N({ref:T},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it.",inputSchema:N({ref:T},["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:N({ref:T,dir:T,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:N({ref:T,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:N({ref:T},["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:N({ref:T},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING IMMEDIATELY, including any DATABASE_URL already written into a file or a running process. Returns the new connection URL. Use it when a credential has leaked, not routinely.",inputSchema:N({ref:T},["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:N({})},{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:N({})},{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. It is as capable as this account, so hand it over deliberately. Optionally expires, as an ISO date.",inputSchema:N({name:T,expires:T},["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:N({id:T},["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:N({ref:T,name:T,dir:T,entrypoint:T,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, sizes, and whether each one needs an API key. Costs nothing.",inputSchema:N({ref:T},["ref"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:N({ref:T,name:T},["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:N({ref:T},["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 or a key.",inputSchema:N({ref:T},["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:N({ref:T},["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:N({ref:T,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:N({ref:T},["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:N({ref:T,hostname:T},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:N({ref:T,hostname:T},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:N({ref:T,hostname:T},["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:N({ref:T},["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:N({ref:T,at:T,name:T},["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:N({ref:T},["ref"])}],rl=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"]),ol=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]);function sl(e){return{...e,annotations:rl.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:ol.has(e.name)}}}function al(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function Le(e,t){return{jsonrpc:"2.0",id:e,result:t}}function Sn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function v(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return Le(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function C(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function bs(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 Le(r,{protocolVersion:nl,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return Le(r,{});case"tools/list":return Le(r,{tools:al([...pt.map(sl),...n.borrowed??[]])});case"tools/call":return il(r,s,t,n);default:return Sn(r,-32601,`unknown method: ${o}`)}}async function il(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(pt.some(u=>u.name===o)||(r.borrowed?.some(u=>u.name===o)??!1)))return Sn(e,-32602,`unknown tool: ${o}`);let i=["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"],d=C(s,"ref");if(i.includes(o)&&!d)return v(e,`${o} needs a project ref. Call list_projects to see them.`,!0);try{switch(o){case"whoami":return v(e,await n.whoami());case"list_projects":return v(e,await n.listProjects());case"create_project":{let u=C(s,"name");return u?v(e,await n.createProject({name:u,region:C(s,"region")??void 0,teamId:C(s,"teamId")??void 0})):v(e,"create_project needs a name.",!0)}case"get_connection_url":{let u=await n.connection(d);return v(e,{url:u.uri,state:u.state,wakesInstantly:u.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 v(e,await n.setProjectState("pause",d));case"resume_project":return v(e,await n.setProjectState("resume",d));case"usage":{let u=typeof s.days=="number"?s.days:void 0;return v(e,await n.usage(d,u))}case"export_status":return v(e,await n.exportStatus(d));case"start_export":return v(e,await n.requestExport(d));case"reset_password":return v(e,await n.resetPassword(d));case"list_teams":return v(e,await n.listTeams());case"get_project":return v(e,await n.getProject(d));case"list_products":return v(e,await n.getProducts(d));case"set_product":{let u=C(s,"product");return u!=="auth"&&u!=="storage"&&u!=="data-api"?v(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?v(e,"set_product needs enabled: true or false.",!0):v(e,await n.setProduct(d,u,s.enabled))}case"list_domains":return v(e,await n.listDomains(d));case"add_domain":case"verify_domain":case"remove_domain":{let u=C(s,"hostname");return u?v(e,await n.domainAction(d,o.split("_")[0],u)):v(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":return v(e,await n.restoreWindow(d));case"restore_to_point":{let u=C(s,"at");return u?v(e,await n.restoreTo(d,u,C(s,"name")??void 0)):v(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let u=C(s,"ref"),m=C(s,"name");return!u||!m?v(e,"deploy_function needs a project ref and a function name.",!0):v(e,await n.deployFunction(u,m,{dir:C(s,"dir")??void 0,entrypoint:C(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let u=C(s,"ref");return u?v(e,await n.listFunctions(u)):v(e,"list_functions needs a project ref.",!0)}case"delete_function":{let u=C(s,"ref"),m=C(s,"name");return!u||!m?v(e,"delete_function needs a project ref and a function name.",!0):v(e,await n.deleteFunction(u,m))}case"list_function_secrets":{let u=C(s,"ref");return u?v(e,await n.listFunctionSecrets(u)):v(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return v(e,await n.listTokens());case"create_token":{let u=C(s,"name");return u?v(e,await n.createToken(u,C(s,"expires")??void 0)):v(e,"create_token needs a name, so somebody can tell later what it was for.",!0)}case"revoke_token":{let u=C(s,"id");return u?v(e,await n.revokeToken(u)):v(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return v(e,await n.pushMigrations(d,{dir:C(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?v(e,await n.setProjectState("delete",d)):v(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(u=>u.name===o)?n.callBorrowed?Le(e,await n.callBorrowed(o,s)):v(e,`${o} needs the SnoutData app, which is no longer answering.`,!0):Sn(e,-32602,`unknown tool: ${o}`)}}catch(u){return v(e,u instanceof Error?u.message:String(u),!0)}}function Ss(e){let t=e.split(`
476
+ `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}Je();function cl(){return{whoami:()=>be(),listProjects:()=>G(),createProject:async e=>{let t=await w("cloud-project-create",{name:e.name,...e.region?{region:e.region}:{},...e.teamId?{teamId:e.teamId}:{}});try{return await Ae(t.project.ref)}catch{return{...t.project,ready:!1,note:`${t.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:(e,t)=>w("cloud-project-usage",{ref:e,...t?{days:t}:{}}),exportStatus:e=>Se(e),requestExport:e=>Te(e),resetPassword:e=>w("cloud-project-reset-password",{ref:e}),listTeams:()=>w("cloud-team-list",{}),deployFunction:(e,t,n)=>It(e,t,n).then(r=>r.answer),listFunctions:e=>jt(e),deleteFunction:(e,t)=>Ct(e,t),listFunctionSecrets:e=>w("cloud-project-secrets",{ref:e}),listTokens:()=>w("cloud-token-list",{}),createToken:(e,t)=>w("cloud-token-create",{name:e,...t?{expires:t}:{}}),revokeToken:e=>w("cloud-token-revoke",{id:e}),getProject:e=>Wt(e),getProducts:e=>tt(e),setProduct:(e,t,n)=>Bt(e,t,n),listDomains:e=>nt(e),domainAction:(e,t,n)=>Ht(e,t,n),restoreWindow:e=>qt(e),restoreTo:(e,t,n)=>Kt(e,t,n),setProjectState:(e,t)=>w(`cloud-project-${e}`,{ref:t}),connection:e=>W(e),pushMigrations:(e,t)=>Yt(e,{dir:t.dir,dryRun:t.dryRun})}}var xs="app_";async function ll(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Ke();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await Un(t)).map(o=>({name:`${xs}${o.name}`,description:`[SnoutData app, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function ks(e){_e(!0);let{tools:t,config:n}=await ll(),r=cl();n&&(r.callBorrowed=(d,u)=>Fn(n,d.slice(xs.length),u));let o={...e,borrowed:t};K(t.length>0?`snoutdata mcp: ${pt.length} cloud tools, and ${t.length} borrowed from the SnoutData app on this machine.`:"snoutdata mcp: cloud tools only. The SnoutData app is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let d=await be();K(`snoutdata mcp: signed in as ${d.email??"this account"}.`)}catch(d){return K(`snoutdata mcp: ${d instanceof Error?d.message:String(d)}`),K("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}K(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function i(d){a.add(d),d.finally(()=>a.delete(d))}return new Promise(d=>{process.stdin.on("data",u=>{let m=Ss(s+u);s=m.rest;for(let c of m.messages)i(dl(c,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);d(0)})()}),process.stdin.on("error",()=>d(1))})}async function dl(e,t,n){let r;try{r=JSON.parse(e)}catch{K(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await bs(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
435
477
  `)}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)}})}
436
- `)}}M();F();function Gc(e){return e>=1?"over":e>=.8?"near":"ok"}function ds(e,t){let n=null,r=null,o=0,s=0;for(let i of e)o+=Number.isFinite(i.computeSeconds)?i.computeSeconds:0,s+=Number.isFinite(i.connections)?i.connections:0,!(i.dbBytes===null||!Number.isFinite(i.dbBytes))&&(r=r===null?i.dbBytes:Math.max(r,i.dbBytes),(!n||i.day>=n.day)&&(n={day:i.day,dbBytes:i.dbBytes,repoBytes:i.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:Gc(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function hn(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 us(e,t={}){let n=await S("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=ds(n.days,n.limit);return h({...n,summary:r},()=>{if(l(T(`${n.name} (${n.ref})`)),!r.latest)l("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${Q(r.storage.usedBytes)} of ${Q(r.storage.limitBytes)} on ${n.limit?.tier}`;l(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 l(`${Q(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&l("This project is currently read-only."),l(`Over ${r.days} day${r.days===1?"":"s"}: ${hn(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&l(x(`Peak in that window was ${Q(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(l(""),l(L([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":Q(o.dbBytes),o.repoBytes===null?"-":Q(o.repoBytes),hn(o.computeSeconds),String(o.connections)])])),l(x("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}var Ue="0.4.1",ms=`snoutdata ${Ue} \u2014 hosted Postgres, from a terminal or an agent
478
+ `)}}M();F();function ul(e){return e>=1?"over":e>=.8?"near":"ok"}function vs(e,t){let n=null,r=null,o=0,s=0;for(let i of e)o+=Number.isFinite(i.computeSeconds)?i.computeSeconds:0,s+=Number.isFinite(i.connections)?i.connections:0,!(i.dbBytes===null||!Number.isFinite(i.dbBytes))&&(r=r===null?i.dbBytes:Math.max(r,i.dbBytes),(!n||i.day>=n.day)&&(n={day:i.day,dbBytes:i.dbBytes,repoBytes:i.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:ul(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function xn(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 Ps(e,t={}){let n=await w("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=vs(n.days,n.limit);return h({...n,summary:r},()=>{if(l(P(`${n.name} (${n.ref})`)),!r.latest)l("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${Q(r.storage.usedBytes)} of ${Q(r.storage.limitBytes)} on ${n.limit?.tier}`;l(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 l(`${Q(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&l("This project is currently read-only."),l(`Over ${r.days} day${r.days===1?"":"s"}: ${xn(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&l(b(`Peak in that window was ${Q(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(l(""),l(j([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":Q(o.dbBytes),o.repoBytes===null?"-":Q(o.repoBytes),xn(o.computeSeconds),String(o.connections)])])),l(b("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}var Ue="0.5.0",Es=`snoutdata ${Ue} \u2014 hosted Postgres, from a terminal or an agent
437
479
 
438
480
  snoutdata init [--name X] [--env] a database for this folder, linked, ready to use
439
481
  snoutdata login [--provider github] sign in through a browser
@@ -458,6 +500,12 @@ ${o.err.trim()}`),l(" ran seed.sql"),!0}var je="_snoutdata_migrations",Mc=`crea
458
500
 
459
501
  snoutdata products [--ref R] auth, storage and the data API: on or off
460
502
  snoutdata products enable|disable auth|storage|data-api [--ref R]
503
+ snoutdata auth [--ref R] Google sign-in and redirect addresses
504
+ \u2026 | snoutdata auth google --client-id ID --stdin your own Google client; secret on stdin
505
+ snoutdata auth google off
506
+ snoutdata auth redirects --site-url URL --allow URL,URL
507
+ snoutdata auth templates the five auth emails, ours or yours
508
+ snoutdata auth template KIND --file body.html [--subject S] | reset
461
509
 
462
510
  snoutdata domains [--ref R] your own domains in front of the project's API
463
511
  snoutdata domains add|verify|remove <hostname> [--ref R]
@@ -512,10 +560,10 @@ A project comes from --ref, then SNOUTDATA_PROJECT, then
512
560
  .snoutdata/project.json in this folder or a parent. A token comes from
513
561
  SNOUTDATA_ACCESS_TOKEN, then ~/.snoutdata/auth.json. The session that login writes lasts
514
562
  an hour, so CI wants "tokens create", which does not expire.
515
- `;async function zc(e){let[t,n,...r]=e.command;if(U(e,"version")||t==="version")return h({version:Ue},()=>process.stdout.write(`${Ue}
516
- `)),0;if(U(e,"help")&&t&&t!=="help"){let i=gt.filter(d=>d.name===t||d.name.startsWith(`${t} `));if(i.length>0)return h({commands:i},()=>{for(let d of i){let u=d.flags.length>0?` ${d.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${d.name}${u}
563
+ `;async function ml(e){let[t,n,...r]=e.command;if(U(e,"version")||t==="version")return h({version:Ue},()=>process.stdout.write(`${Ue}
564
+ `)),0;if(U(e,"help")&&t&&t!=="help"){let i=ht.filter(d=>d.name===t||d.name.startsWith(`${t} `));if(i.length>0)return h({commands:i},()=>{for(let d of i){let u=d.flags.length>0?` ${d.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${d.name}${u}
517
565
  ${d.summary}
518
566
  `)}process.stdout.write(`
519
567
  Every command also takes --json, --quiet and --help.
520
- `)}),0}let o=U(e,"help")||t==="help";if(o||!t)return h({version:Ue,usage:ms.trim(),commands:gt},()=>process.stdout.write(ms)),o?0:ne.usage;let s=()=>{let i=Ee(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=He({flag:$(e,"ref")});if(!i)throw new y("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i};switch(t){case"login":{if(U(e,"device")){let m=await Ge({name:We(),platform:process.platform});h({verifyUrl:m.verifyUrl,code:m.code,expiresAt:m.expiresAt},()=>{l(""),l(` Open ${m.verifyUrl}`),l(` Type ${m.code}`),l(""),l(x("Waiting for that to be approved\u2026"))});let c=await ze(m.secret,{intervalMs:Math.max(1,m.interval)*1e3,expiresAt:m.expiresAt});return z({accessToken:c.token}),h({ok:!0,name:c.name??null},()=>l("Signed in.")),0}let i=$(e,"domain"),d=U(e,"sso")||i!==void 0;if(d&&$(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let u=await Ze({provider:$(e,"provider"),sso:d,domain:i,noBrowser:U(e,"no-browser")||!te(),timeoutMs:s()});return h({email:u.email??null},()=>l(`Signed in${u.email?` as ${u.email}`:""}.`)),0}case"logout":{Tn();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return h({ok:!0,environmentTokenStillSet:i},()=>{l("Signed out."),i&&(l("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),l(x(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await be();return h(i,()=>{process.stdout.write(`${i.email??i.id}
521
- `),i.token&&l(`via access token ${i.token.prefix} (${i.token.name}).`)}),0}case"tokens":switch(n){case void 0:case"list":return await rr(),0;case"create":{let i=$(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await nr({name:i,expiresInDays:Ee(e,"expires")}),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 or(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await Xn(),0;case"create":{let i=$(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let d=$(e,"team");return await Tt({teamId:d?await tr(d):void 0,name:i,region:$(e,"region"),wait:!U(e,"no-wait"),timeoutMs:s(),link:void 0}),0}case"pause":case"resume":case"delete":return await Zn(n,$(e,"ref")??r[0]??a()),0;case"show":return await Ir($(e,"ref")??r[0]??a()),0;default:throw new y(`unknown command: projects ${n}`)}case"functions":switch(n){case"deploy":{let i=r[0];if(!i)throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await mr(a(),i,{dir:$(e,"dir"),entrypoint:$(e,"entrypoint"),verifyJwt:!U(e,"no-verify-jwt")}),0}case void 0:case"list":return await fr(a()),0;case"delete":case"remove":{let i=r[0];if(!i)throw new y("functions delete needs a name");return await pr(a(),i),0}default:throw new y(`unknown command: functions ${n}`)}case"secrets":switch(n){case"set":return await gr(a(),r,{stdin:U(e,"stdin")}),0;case void 0:case"list":return await yr(a()),0;case"unset":case"remove":{let i=r[0];if(!i)throw new y("secrets unset needs a name");return await hr(a(),i),0}default:throw new y(`unknown command: secrets ${n}`)}case"keys":switch(n){case void 0:case"show":case"list":return await sr(a()),0;case"rotate":return await ar(a(),{force:U(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await $r(a()),0;if(n==="enable"||n==="disable")return await Rr(a(),Ar(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${et.join("|")}]`)}case"domains":return await Or(a(),n,r[0]),0;case"teams":return await er(),0;case"link":{let i=$(e,"ref")??n;if(!i)throw new y("link needs --ref");let d=Be(process.cwd(),{ref:i});return h({ref:i,path:d},()=>l(`Linked ${i} (${d}).`)),0}case"db":switch(n){case"url":return await Ut(a()),0;case"psql":return Pr(a(),r);case"reset-password":return await Qn(a()),0;case"push":return Lr(a(),{dir:$(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return U(e,"window")?(await Nr(a()),0):$(e,"at")?(await Dr(a(),$(e,"at"),$(e,"name")),0):Tr(a(),{file:$(e,"file"),force:!!e.flags.force});case"export":return _r(a(),{timeoutMs:s(),out:$(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=U(e,"local")?await ss(await Me()):null,d=i?i.dbUrl:$(e,"db-url");return Br(r[0],{ref:d?void 0:a(),dbUrl:d,schemas:($(e,"schema")??"public").split(",").map(u=>u.trim()).filter(Boolean),out:$(e,"out"),dataApiVersion:$(e,"data-api-version")??$(e,"postgrest-version"),sql:i?.sql})}case"start":return ns(await Me(),{port:Ee(e,"port"),dir:$(e,"dir"),noMigrations:U(e,"no-migrations"),outOfOrder:U(e,"out-of-order")});case"stop":return rs(await Me());case"status":return os(await Me());case"usage":return us(a(),{days:Ee(e,"days"),history:!!e.flags.history});case"mcp":return ls({version:Ue,allowDelete:!!e.flags["allow-delete"]});case"init":return Yc(e);default:throw new y(`unknown command: ${t}`)}}async function Yc(e){let t=He({flag:$(e,"ref")});if(t)return l(`This folder is already linked to ${t}.`),await Ut(t),0;let n=$(e,"name")??Xc(process.cwd());l(`Creating a project called "${n}"\u2026`),await Tt({name:n,region:$(e,"region"),wait:!0,link:process.cwd()});let r=He({cwd:process.cwd()});if(r&&U(e,"env")){let{connection:o}=await Promise.resolve().then(()=>(M(),ue)),s=await o(r),a=Er(process.cwd(),s.uri);l(`${a.replaced?"Updated":"Wrote"} DATABASE_URL in ${a.path}.`)}return 0}function Xc(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Zc(e){if(e instanceof q)return de(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return de("usage",e.message),ne.usage;if(e instanceof oe)return de("not-signed-in",e.message),ne["not-signed-in"];if(e instanceof Z){let n=Ve(e.status);return de(n,e.message,{status:e.status}),ne[n]}let t=Mn(e);return de(t,e instanceof Error?e.message:String(e)),ne[t]}function yn(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function Qc(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function el(){_e(Qc(process.argv.slice(2)));let e;try{e=Sn(process.argv.slice(2))}catch(t){de("usage",String(t instanceof Error?t.message:t)),yn(ne.usage);return}_e(e.json),Fn(e.flags.quiet===!0),Rn(qe({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{yn(await zc(e))}catch(t){yn(Zc(t))}}el();
568
+ `)}),0}let o=U(e,"help")||t==="help";if(o||!t)return h({version:Ue,usage:Es.trim(),commands:ht},()=>process.stdout.write(Es)),o?0:re.usage;let s=()=>{let i=Ee(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=He({flag:$(e,"ref")});if(!i)throw new y("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i};switch(t){case"login":{if(U(e,"device")){let m=await Ve({name:We(),platform:process.platform});h({verifyUrl:m.verifyUrl,code:m.code,expiresAt:m.expiresAt},()=>{l(""),l(` Open ${m.verifyUrl}`),l(` Type ${m.code}`),l(""),l(b("Waiting for that to be approved\u2026"))});let c=await ze(m.secret,{intervalMs:Math.max(1,m.interval)*1e3,expiresAt:m.expiresAt});return z({accessToken:c.token}),h({ok:!0,name:c.name??null},()=>l("Signed in.")),0}let i=$(e,"domain"),d=U(e,"sso")||i!==void 0;if(d&&$(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let u=await Ze({provider:$(e,"provider"),sso:d,domain:i,noBrowser:U(e,"no-browser")||!ne(),timeoutMs:s()});return h({email:u.email??null},()=>l(`Signed in${u.email?` as ${u.email}`:""}.`)),0}case"logout":{Nn();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return h({ok:!0,environmentTokenStillSet:i},()=>{l("Signed out."),i&&(l("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),l(b(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await be();return h(i,()=>{process.stdout.write(`${i.email??i.id}
569
+ `),i.token&&l(`via access token ${i.token.prefix} (${i.token.name}).`)}),0}case"tokens":switch(n){case void 0:case"list":return await cr(),0;case"create":{let i=$(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await ir({name:i,expiresInDays:Ee(e,"expires")}),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 lr(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await nr(),0;case"create":{let i=$(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let d=$(e,"team");return await At({teamId:d?await ar(d):void 0,name:i,region:$(e,"region"),wait:!U(e,"no-wait"),timeoutMs:s(),link:void 0}),0}case"pause":case"resume":case"delete":return await rr(n,$(e,"ref")??r[0]??a()),0;case"show":return await Ur($(e,"ref")??r[0]??a()),0;default:throw new y(`unknown command: projects ${n}`)}case"functions":switch(n){case"deploy":{let i=r[0];if(!i)throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await yr(a(),i,{dir:$(e,"dir"),entrypoint:$(e,"entrypoint"),verifyJwt:!U(e,"no-verify-jwt")}),0}case void 0:case"list":return await wr(a()),0;case"delete":case"remove":{let i=r[0];if(!i)throw new y("functions delete needs a name");return await br(a(),i),0}default:throw new y(`unknown command: functions ${n}`)}case"secrets":switch(n){case"set":return await Sr(a(),r,{stdin:U(e,"stdin")}),0;case void 0:case"list":return await kr(a()),0;case"unset":case"remove":{let i=r[0];if(!i)throw new y("secrets unset needs a name");return await xr(a(),i),0}default:throw new y(`unknown command: secrets ${n}`)}case"keys":switch(n){case void 0:case"show":case"list":return await dr(a()),0;case"rotate":return await ur(a(),{force:U(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Ir(a()),0;if(n==="enable"||n==="disable")return await jr(a(),Dr(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${et.join("|")}]`)}case"auth":return await Kr(a(),n,r,{clientId:$(e,"client-id"),stdin:U(e,"stdin"),siteUrl:$(e,"site-url"),allow:$(e,"allow"),file:$(e,"file"),subject:$(e,"subject")}),0;case"domains":return await Cr(a(),n,r[0]),0;case"teams":return await sr(),0;case"link":{let i=$(e,"ref")??n;if(!i)throw new y("link needs --ref");let d=Be(process.cwd(),{ref:i});return h({ref:i,path:d},()=>l(`Linked ${i} (${d}).`)),0}case"db":switch(n){case"url":return await Ft(a()),0;case"psql":return $r(a(),r);case"reset-password":return await or(a()),0;case"push":return Gr(a(),{dir:$(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return U(e,"window")?(await Mr(a()),0):$(e,"at")?(await Lr(a(),$(e,"at"),$(e,"name")),0):Nr(a(),{file:$(e,"file"),force:!!e.flags.force});case"export":return Or(a(),{timeoutMs:s(),out:$(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=U(e,"local")?await ws(await Me()):null,d=i?i.dbUrl:$(e,"db-url");return Yr(r[0],{ref:d?void 0:a(),dbUrl:d,schemas:($(e,"schema")??"public").split(",").map(u=>u.trim()).filter(Boolean),out:$(e,"out"),dataApiVersion:$(e,"data-api-version")??$(e,"postgrest-version"),sql:i?.sql})}case"start":return gs(await Me(),{port:Ee(e,"port"),dir:$(e,"dir"),noMigrations:U(e,"no-migrations"),outOfOrder:U(e,"out-of-order")});case"stop":return hs(await Me());case"status":return ys(await Me());case"usage":return Ps(a(),{days:Ee(e,"days"),history:!!e.flags.history});case"mcp":return ks({version:Ue,allowDelete:!!e.flags["allow-delete"]});case"init":return fl(e);default:throw new y(`unknown command: ${t}`)}}async function fl(e){let t=He({flag:$(e,"ref")});if(t)return l(`This folder is already linked to ${t}.`),await Ft(t),0;let n=$(e,"name")??pl(process.cwd());l(`Creating a project called "${n}"\u2026`),await At({name:n,region:$(e,"region"),wait:!0,link:process.cwd()});let r=He({cwd:process.cwd()});if(r&&U(e,"env")){let{connection:o}=await Promise.resolve().then(()=>(M(),ae)),s=await o(r),a=Rr(process.cwd(),s.uri);l(`${a.replaced?"Updated":"Wrote"} DATABASE_URL in ${a.path}.`)}return 0}function pl(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function gl(e){if(e instanceof q)return ue(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return ue("usage",e.message),re.usage;if(e instanceof se)return ue("not-signed-in",e.message),re["not-signed-in"];if(e instanceof Z){let n=Ge(e.status);return ue(n,e.message,{status:e.status}),re[n]}let t=Hn(e);return ue(t,e instanceof Error?e.message:String(e)),re[t]}function kn(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function hl(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function yl(){_e(hl(process.argv.slice(2)));let e;try{e=En(process.argv.slice(2))}catch(t){ue("usage",String(t instanceof Error?t.message:t)),kn(re.usage);return}_e(e.json),Wn(e.flags.quiet===!0),jn(qe({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{kn(await ml(e))}catch(t){kn(gl(t))}}yl();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snoutdata",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Hosted Postgres from a terminal or an agent. Create a database, get a DATABASE_URL, run migrations, and serve the same operations to an AI agent over MCP.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "SnoutData",