snoutdata 0.7.0 → 0.9.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 +179 -97
  2. package/package.json +1 -1
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var Cs=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var Pn=(e,t)=>{for(var n in t)Cs(e,n,{get:t[n],enumerable:!0})};function Tn(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]=Us(o);if(Ms.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(!Ls.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function Us(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 F(e,t){return e.flags[t]===!0}function Pe(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,Ms,Ls,z=A(()=>{"use strict";y=class extends Error{},Ms=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment"]);Ls=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 Fs,existsSync as $n,mkdirSync as An,readFileSync as Rn,rmSync as Bs,writeFileSync as On}from"node:fs";import{homedir as He}from"node:os";import{dirname as wt,join as Nn,parse as Hs,resolve as qs}from"node:path";function bt(e=He()){return Nn(e,".snoutdata","auth.json")}function Dn(e=He()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=bt(e);if(!$n(n))return null;try{let r=JSON.parse(Rn(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function Y(e,t=He()){let n=bt(t);An(wt(n),{recursive:!0}),On(n,`${JSON.stringify(e,null,2)}
3
- `,{mode:384}),Fs(n,384)}function In(e=He()){Bs(bt(e),{force:!0})}function jn(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function Cn(e){return Nn(e,".snoutdata","project.json")}function Ks(e){let t=qs(e),n=Hs(t).root;for(;;){let r=Cn(t);if($n(r))try{let o=JSON.parse(Rn(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=wt(t)}}function qe(e,t){let n=Cn(e);return An(wt(n),{recursive:!0}),On(n,`${JSON.stringify(t,null,2)}
4
- `),n}function Ke(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:Ks(e.cwd??process.cwd())?.ref??null}var ge=A(()=>{"use strict"});function We(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 Ws){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 he(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 Mn(e){St=e}function ne(){return(St??We({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function ye(){return(St??We({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var Ws,St,we=A(()=>{"use strict";Ws=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];St=null});import{existsSync as Js,readFileSync as Gs}from"node:fs";import{homedir as xt,hostname as Vs}from"node:os";import{join as be}from"node:path";function zs(e,t,n){if(t==="win32"){let r=n.APPDATA??be(xt(),"AppData","Roaming");return be(r,e)}return t==="darwin"?be(xt(),"Library","Application Support",e):be(n.XDG_CONFIG_HOME??be(xt(),".config"),e)}function Un(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>be(zs(n,e,t),"mcp.json"))}function Ys(e=process.platform,t=process.env){return Un(e,t)[0]}function Xs(e=Ys()){if(!Js(e))return null;try{let t=JSON.parse(Gs(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function Zs(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function Je(e){if(e!==void 0)return Ln(e);let t="no-config";for(let n of Un()){let r=Xs(n);if(!r)continue;let o=await Ln(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function Ln(e){let t=Zs(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(Qs)});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 Fn(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 Ge(){return Vs()}async function kt(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 Bn(e){return await kt(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await kt(e,"tools/list",{},3e3)).tools??[]}async function Hn(e,t,n){return kt(e,"tools/call",{name:t,arguments:n},12e4)}var Qs,Ve=A(()=>{"use strict";Qs=1500});function P(e,t,n){throw new q(e,t,n)}function ze(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 Kn(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(ea.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,ea,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]}};ea=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 Wn(e,t){let n=await fetch(`${V}/functions/v1/${e}`,{method:"POST",headers:{apikey:X,"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return{status:n.status,body:r}}async function Ye(e={}){let{status:t,body:n}=await Wn("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 Xe(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 Wn("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 vt=A(()=>{"use strict";M();oe()});function Te(e){Ze=e}function Gn(e){Jn=e}function b(e){return Vn?`\x1B[2m${e}\x1B[0m`:e}function E(e){return Vn?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(Ze){process.stdout.write(`${JSON.stringify(e,null,2)}
5
- `);return}t()}function l(e){!Ze&&!Jn&&process.stderr.write(`${e}
6
- `)}function de(e,t,n){process.stderr.write(`${t}
7
- `),Ze&&process.stdout.write(`${JSON.stringify({ok:!1,code:e,error:t,...n},null,2)}
2
+ var Ks=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var Rn=(e,t)=>{for(var n in t)Ks(e,n,{get:t[n],enumerable:!0})};function On(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]=Js(o);if(zs.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(!Ws.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function Js(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function P(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function U(e,t){return e.flags[t]===!0}function fe(e,t){let n=P(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,zs,Ws,V=A(()=>{"use strict";y=class extends Error{},zs=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","memory","concurrency","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment"]);Ws=new Set(["json","window","help","version","force","no-verify-jwt","reset","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history"])});import{chmodSync as Gs,existsSync as Nn,mkdirSync as Dn,readFileSync as Mn,rmSync as Vs,writeFileSync as In}from"node:fs";import{homedir as Ke}from"node:os";import{dirname as xt,join as Cn,parse as Ys,resolve as Xs}from"node:path";function kt(e=Ke()){return Cn(e,".snoutdata","auth.json")}function jn(e=Ke()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=kt(e);if(!Nn(n))return null;try{let r=JSON.parse(Mn(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function Y(e,t=Ke()){let n=kt(t);Dn(xt(n),{recursive:!0}),In(n,`${JSON.stringify(e,null,2)}
3
+ `,{mode:384}),Gs(n,384)}function Ln(e=Ke()){Vs(kt(e),{force:!0})}function Un(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function Fn(e){return Cn(e,".snoutdata","project.json")}function Zs(e){let t=Xs(e),n=Ys(t).root;for(;;){let r=Fn(t);if(Nn(r))try{let o=JSON.parse(Mn(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=xt(t)}}function ze(e,t){let n=Fn(e);return Dn(xt(n),{recursive:!0}),In(n,`${JSON.stringify(t,null,2)}
4
+ `),n}function We(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:Zs(e.cwd??process.cwd())?.ref??null}var we=A(()=>{"use strict"});function Je(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 Qs){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 be(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 Bn(e){_t=e}function re(){return(_t??Je({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function Se(){return(_t??Je({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var Qs,_t,xe=A(()=>{"use strict";Qs=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];_t=null});import{existsSync as ea,readFileSync as ta}from"node:fs";import{homedir as vt,hostname as na}from"node:os";import{join as ke}from"node:path";function ra(e,t,n){if(t==="win32"){let r=n.APPDATA??ke(vt(),"AppData","Roaming");return ke(r,e)}return t==="darwin"?ke(vt(),"Library","Application Support",e):ke(n.XDG_CONFIG_HOME??ke(vt(),".config"),e)}function qn(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>ke(ra(n,e,t),"mcp.json"))}function oa(e=process.platform,t=process.env){return qn(e,t)[0]}function sa(e=oa()){if(!ea(e))return null;try{let t=JSON.parse(ta(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function aa(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}async function Ge(e){if(e!==void 0)return Hn(e);let t="no-config";for(let n of qn()){let r=sa(n);if(!r)continue;let o=await Hn(r);if(o.available)return o;(o.skip!=="not-running"||t==="no-config")&&(t=o.skip)}return{available:!1,skip:t}}async function Hn(e){let t=aa(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(ia)});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 Kn(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 Ve(){return na()}async function Et(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 zn(e){return await Et(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await Et(e,"tools/list",{},3e3)).tools??[]}async function Wn(e,t,n){return Et(e,"tools/call",{name:t,arguments:n},12e4)}var ia,Ye=A(()=>{"use strict";ia=1500});function T(e,t,n){throw new q(e,t,n)}function Xe(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 Gn(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(ca.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 oe,q,ca,se=A(()=>{"use strict";oe={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 oe[this.code]}};ca=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 Vn(e,t){let n=await fetch(`${G}/functions/v1/${e}`,{method:"POST",headers:{apikey:X,"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return{status:n.status,body:r}}async function Ze(e={}){let{status:t,body:n}=await Vn("cloud-device-start",{client:e});return t===429&&T("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&T("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)&&T("server",n.error??`could not start a pairing (${t})`),n}async function Qe(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&T("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 Vn("cloud-device-poll",{secret:e});switch(o===404&&T("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&T("usage",s.error??"that is not a pairing secret"),o!==200&&T("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":T("forbidden",s.error??"that pairing was refused in the browser");case"used":T("conflict",s.error??"that pairing has already been completed");case"expired":T("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 Pt=A(()=>{"use strict";j();se()});function Ae(e){et=e}function Xn(e){Yn=e}function b(e){return Zn?`\x1B[2m${e}\x1B[0m`:e}function v(e){return Zn?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(et){process.stdout.write(`${JSON.stringify(e,null,2)}
5
+ `);return}t()}function l(e){!et&&!Yn&&process.stderr.write(`${e}
6
+ `)}function pe(e,t,n){process.stderr.write(`${t}
7
+ `),et&&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 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 Z(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[u,d]of o){if(s<u)break;s=Math.round(s/u),a=d}let i=`${s} ${a}${s===1?"":"s"}`;return n?`in ${i}`:`${i} ago`}var Ze,Jn,Vn,U=A(()=>{"use strict";Ze=!1,Jn=!1;Vn=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as Xn,randomBytes as _t}from"node:crypto";import{createServer as na}from"node:http";import{createInterface as ra}from"node:readline/promises";import{spawn as oa}from"node:child_process";function Qe(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function zn(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.":Sa(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
9
+ `)}function I(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 Z(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[u,d]of o){if(s<u)break;s=Math.round(s/u),a=d}let i=`${s} ${a}${s===1?"":"s"}`;return n?`in ${i}`:`${i} ago`}var et,Yn,Zn,F=A(()=>{"use strict";et=!1,Yn=!1;Zn=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as tr,randomBytes as Tt}from"node:crypto";import{createServer as ua}from"node:http";import{createInterface as da}from"node:readline/promises";import{spawn as ma}from"node:child_process";function tt(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Qn(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.":$a(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,35 +19,94 @@ var Cs=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var Pn=(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 Et(){let e=Qe(_t(32)),t=Qe(Xn("sha256").update(e).digest());return{verifier:e,challenge:t}}function sa(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 aa(e){let{command:t,args:n,verbatim:r}=sa(process.platform,e),o=oa(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function Zn(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function la(){return"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"}function ua(e){let t=e??Qe(_t(32));return{raw:t,hashed:Xn("sha256").update(t).digest("hex")}}function da(e){return`${ia}?${new URLSearchParams({client_id:Zn(),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 ma(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=na((a,i)=>{let u=ma(a.url??"/",e.state);if(u.kind==="ignore"){i.writeHead(404).end();return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(u.kind==="ok"?zn("ok"):zn("failed",u.message)),o.close(),u.kind==="ok"?t({code:u.code,redirectUri:r}):n(new Error(u.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(u=>{process.stdout.write(`${u}
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."),aa(u))},u=>{o.close(),n(u)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Qn(e,t){let n=await fetch(`${V}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:X,"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 er(await n.json())}async function pa(e,t,n){let r=await fetch(ca,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Zn(),client_secret:la(),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 fa(e){let{verifier:t,challenge:n}=Et(),{raw:r,hashed:o}=ua(),s=Qe(_t(16)),{code:a,redirectUri:i}=await Pt({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:m=>da({redirectUri:m,challenge:n,hashedNonce:o,state:s})}),u=await pa(a,t,i),d=await fetch(`${V}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:X,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:u,nonce:r})});if(!d.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await d.text()).slice(0,200)}`);return er(await d.json())}function er(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return Y(n),n}function Yn(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 ga(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 ha(e,t,n){let r=ga(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(ze(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function ya(e){let t=await fetch(`${V}/auth/v1/sso`,{method:"POST",headers:{apikey:X,"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 ha(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 wa(e){if(e!==void 0){let r=Yn(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=ye();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${he(r)}`:""}`)}let t=ra({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=Yn(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 ba(e){let t=await wa(e.domain),{verifier:n,challenge:r}=Et();l(`Signing in with the identity provider for ${E(t)}.`);let{code:o}=await Pt({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>ya({domain:t,redirectTo:s,challenge:r})});return Qn(o,n)}async function et(e={}){if(e.sso)return ba(e);let t=e.provider??"google";if(t==="google")return fa(e);let{verifier:n,challenge:r}=Et(),{code:o}=await Pt({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 Qn(o,n)}function Sa(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var ia,ca,Tt=A(()=>{"use strict";M();z();ge();oe();we();U();ia="https://accounts.google.com/o/oauth2/v2/auth",ca="https://oauth2.googleapis.com/token"});import{createInterface as xa}from"node:readline/promises";async function $t(e){let t=xa({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 ka(){let e=await Je();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 ${E(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 $t("Use it?"))return null;l("Asking the app. Approve it there.");let n=await Fn(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 Y(r),l(`Signed in with "${n.name}".`),r}async function va(){if(!await $t("Sign in with a browser?"))return null;try{let e=await et({});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 _a(){if(!await $t("Sign in by typing a code into a browser on another machine?"))return null;let e=await Ye({name:Ge(),platform:process.platform});l(""),l(` Open ${E(e.verifyUrl)}`),l(` Type ${E(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 Xe(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return Y(n),l(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function tr(){if(!ne())return null;let e=await ka();if(e)return e;let t=await va();return t||_a()}var nr=A(()=>{"use strict";ge();Ve();vt();we();Tt();U()});var ae={};Pn(ae,{ANON_KEY:()=>X,ApiError:()=>Q,FUNCTIONS:()=>rr,NotSignedIn:()=>se,SUPABASE_URL:()=>V,call:()=>w,connection:()=>W,exportStatus:()=>xe,listProjects:()=>G,listTeams:()=>tt,requestExport:()=>$e,waitForReady:()=>Ae,whoami:()=>Se});function Ea(){let e=ye();return e?`not signed in, and ${he(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 Pa(){let e=Dn();if(e||(e=await tr()),!e)throw new se;if(!jn(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${V}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:X,"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 Y(r),r.accessToken}async function w(e,t={},n="POST"){let r=await Pa(),o=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",s=await fetch(`${rr}/${e}${o}`,{method:n,headers:{Authorization:`Bearer ${r}`,apikey:X,"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 Q(s.status,a.slice(0,300)||s.statusText)}if(!s.ok){let u=typeof i=="object"&&i&&"error"in i?String(i.error):`${s.status} ${s.statusText}`;throw s.status===401?new se(u):new Q(s.status,u)}return i}async function Se(){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 tt(){return w("cloud-team-list")}async function $e(e){return w("cloud-project-export",{ref:e})}async function xe(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 Q(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new Q(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var V,X,rr,Q,se,M=A(()=>{"use strict";ge();we();nr();V=process.env.SNOUTDATA_SUPABASE_URL??"https://accounts.snoutdata.com",X=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpmN2c2NW1iOGZhcmEiLCJyb2xlIjoiYW5vbiIsImlhdCI6MTc4OTUxNTUyNCwiZXhwIjoyMTA0ODc1NTI0fQ.TiFJbsmmELR8FIje8lbg-95UJFtoj7IwmC2PZtWPAk8",rr=`${V}/functions/v1`,Q=class extends Error{status;constructor(t,n){super(n),this.status=t}},se=class extends Error{constructor(t){super(t??Ea())}}});function nn(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function ao(e,t,n){let r="";for(;r.length<n;)r+=nn(e,t);return r}var tn,so,io=A(()=>{"use strict";tn="0123456789abcdefghjkmnpqrstvwxyz",so="abcdefghjkmnpqrstvwxyz"});function uo(e){return nn(e,co)+ao(e,tn,lo-1)}function zi(e){return Vi.test(e)}function J(e){if(!zi(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function D(e,t=Yi){return J(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var co,lo,Vi,Yi,ie=A(()=>{"use strict";io();co=so,lo=13,Vi=new RegExp(`^[${co}][${tn}]{${lo-1}}$`);Yi="db.snoutdata.com"});var mo=A(()=>{"use strict";ie()});function fo(e,t){let n=po(e,7),r=po(t,24);return Math.max(2,Math.ceil(n*24/r))}function po(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var rn=A(()=>{"use strict"});function go(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var on=A(()=>{"use strict"});var ho=A(()=>{"use strict"});var Ad,yo=A(()=>{"use strict";ie();Ad=7*24});var Od,wo=A(()=>{"use strict";Od=7*24*60*60*1e3});var Xi,bo=A(()=>{"use strict";wo();Xi=60*60*1e3});var ve,Md,So=A(()=>{"use strict";ve="snout_storage_admin",Md=`do $$ begin
22
+ </body></html>`}function $t(){let e=tt(Tt(32)),t=tt(tr("sha256").update(e).digest());return{verifier:e,challenge:t}}function fa(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 pa(e){let{command:t,args:n,verbatim:r}=fa(process.platform,e),o=ma(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function nr(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function ya(){return"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"}function wa(e){let t=e??tt(Tt(32));return{raw:t,hashed:tr("sha256").update(t).digest("hex")}}function ba(e){return`${ga}?${new URLSearchParams({client_id:nr(),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 Sa(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 At(e){return new Promise((t,n)=>{let r="",o=ua((a,i)=>{let u=Sa(a.url??"/",e.state);if(u.kind==="ignore"){i.writeHead(404).end();return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(u.kind==="ok"?Qn("ok"):Qn("failed",u.message)),o.close(),u.kind==="ok"?t({code:u.code,redirectUri:r}):n(new Error(u.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(u=>{process.stdout.write(`${u}
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."),pa(u))},u=>{o.close(),n(u)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function rr(e,t){let n=await fetch(`${G}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:X,"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 or(await n.json())}async function xa(e,t,n){let r=await fetch(ha,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:nr(),client_secret:ya(),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 ka(e){let{verifier:t,challenge:n}=$t(),{raw:r,hashed:o}=wa(),s=tt(Tt(16)),{code:a,redirectUri:i}=await At({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:m=>ba({redirectUri:m,challenge:n,hashedNonce:o,state:s})}),u=await xa(a,t,i),d=await fetch(`${G}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:X,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:u,nonce:r})});if(!d.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await d.text()).slice(0,200)}`);return or(await d.json())}function or(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return Y(n),n}function er(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 _a(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 va(e,t,n){let r=_a(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(Xe(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function Ea(e){let t=await fetch(`${G}/auth/v1/sso`,{method:"POST",headers:{apikey:X,"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 va(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 Pa(e){if(e!==void 0){let r=er(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!re()){let r=Se();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${be(r)}`:""}`)}let t=da({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=er(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 Ta(e){let t=await Pa(e.domain),{verifier:n,challenge:r}=$t();l(`Signing in with the identity provider for ${v(t)}.`);let{code:o}=await At({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>Ea({domain:t,redirectTo:s,challenge:r})});return rr(o,n)}async function nt(e={}){if(e.sso)return Ta(e);let t=e.provider??"google";if(t==="google")return ka(e);let{verifier:n,challenge:r}=$t(),{code:o}=await At({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 rr(o,n)}function $a(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var ga,ha,Rt=A(()=>{"use strict";j();V();we();se();xe();F();ga="https://accounts.google.com/o/oauth2/v2/auth",ha="https://oauth2.googleapis.com/token"});import{createInterface as Aa}from"node:readline/promises";async function Ot(e){let t=Aa({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 Ra(){let e=await Ge();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 ${v(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 Ot("Use it?"))return null;l("Asking the app. Approve it there.");let n=await Kn(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 Y(r),l(`Signed in with "${n.name}".`),r}async function Oa(){if(!await Ot("Sign in with a browser?"))return null;try{let e=await nt({});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 Ot("Sign in by typing a code into a browser on another machine?"))return null;let e=await Ze({name:Ve(),platform:process.platform});l(""),l(` Open ${v(e.verifyUrl)}`),l(` Type ${v(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 Qe(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return Y(n),l(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function sr(){if(!re())return null;let e=await Ra();if(e)return e;let t=await Oa();return t||Na()}var ar=A(()=>{"use strict";we();Ye();Pt();xe();Rt();F()});var ie={};Rn(ie,{ACCOUNTS_URL:()=>G,ANON_KEY:()=>X,ApiError:()=>Q,FUNCTIONS:()=>ir,NotSignedIn:()=>ae,call:()=>w,connection:()=>z,exportStatus:()=>ve,listProjects:()=>J,listTeams:()=>rt,requestExport:()=>Re,waitForReady:()=>Oe,whoami:()=>_e});function Da(){let e=Se();return e?`not signed in, and ${be(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 Ma(){let e=jn();if(e||(e=await sr()),!e)throw new ae;if(!Un(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${G}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:X,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new ae;let n=await t.json(),r={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,email:e.email};return Y(r),r.accessToken}async function w(e,t={},n="POST"){let r=await Ma(),o=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",s=await fetch(`${ir}/${e}${o}`,{method:n,headers:{Authorization:`Bearer ${r}`,apikey:X,"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 Q(s.status,a.slice(0,300)||s.statusText)}if(!s.ok){let u=typeof i=="object"&&i&&"error"in i?String(i.error):`${s.status} ${s.statusText}`;throw s.status===401?new ae(u):new Q(s.status,u)}return i}async function _e(){return w("cloud-whoami")}async function J(){return w("cloud-project-list")}async function z(e){return w("cloud-project-connection",{ref:e})}async function rt(){return w("cloud-team-list")}async function Re(e){return w("cloud-project-export",{ref:e})}async function ve(e){return w("cloud-project-export",{ref:e},"GET")}async function Oe(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await J(),o=r.find(s=>s.ref===e);if(!o)throw new Q(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new Q(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var G,X,ir,Q,ae,j=A(()=>{"use strict";we();xe();ar();G=process.env.SNOUTDATA_ACCOUNTS_URL??"https://accounts.snoutdata.com",X=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzbm91dGRhdGEiLCJyZWYiOiJ6ZjdnNjVtYjhmYXJhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTA4MDcwNDcsImV4cCI6MjEwNDg3NTUyNH0.M-Cb0wFEhJXU21cXtQxZAn-WnRcq4rK2uNSkeHdLucY",ir=`${G}/functions/v1`,Q=class extends Error{status;constructor(t,n){super(n),this.status=t}},ae=class extends Error{constructor(t){super(t??Da())}}});function sn(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function fo(e,t,n){let r="";for(;r.length<n;)r+=sn(e,t);return r}var on,mo,po=A(()=>{"use strict";on="0123456789abcdefghjkmnpqrstvwxyz",mo="abcdefghjkmnpqrstvwxyz"});function yo(e){return sn(e,go)+fo(e,on,ho-1)}function oc(e){return rc.test(e)}function W(e){if(!oc(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function M(e,t=sc){return W(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var go,ho,rc,sc,ce=A(()=>{"use strict";po();go=mo,ho=13,rc=new RegExp(`^[${go}][${on}]{${ho-1}}$`);sc="db.snoutdata.com"});var wo=A(()=>{"use strict";ce()});function So(e,t){let n=bo(e,7),r=bo(t,24);return Math.max(2,Math.ceil(n*24/r))}function bo(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var an=A(()=>{"use strict"});function xo(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var cn=A(()=>{"use strict"});var ko=A(()=>{"use strict"});var Hd,_o=A(()=>{"use strict";ce();Hd=7*24});var Kd,vo=A(()=>{"use strict";Kd=7*24*60*60*1e3});var ac,Eo=A(()=>{"use strict";vo();ac=60*60*1e3});var Pe,Yd,Po=A(()=>{"use strict";Pe="snout_storage_admin",Yd=`do $$ begin
24
24
  if exists (select from pg_roles where rolname = 'supabase_storage_admin') then
25
- if exists (select from pg_roles where rolname = '${ve}') then
26
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${ve}';
27
- reassign owned by ${ve} to supabase_storage_admin;
28
- drop owned by ${ve};
29
- drop role ${ve};
25
+ if exists (select from pg_roles where rolname = '${Pe}') then
26
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Pe}';
27
+ reassign owned by ${Pe} to supabase_storage_admin;
28
+ drop owned by ${Pe};
29
+ drop role ${Pe};
30
30
  end if;
31
- alter role supabase_storage_admin rename to ${ve};
31
+ alter role supabase_storage_admin rename to ${Pe};
32
32
  end if;
33
- end $$;`});var ct,xo,Ud,ko=A(()=>{"use strict";ct="snoutdata_realtime",xo="supabase_realtime",Ud=`do $snoutpod$
33
+ end $$;`});var le,Zd,dt,To,Qd,$o=A(()=>{"use strict";le="snout_realtime_admin",Zd=`do $snoutpod$
34
34
  begin
35
- if exists (select 1 from pg_publication where pubname = '${ct}') then
35
+ if exists (select from pg_roles where rolname = 'supabase_admin') then
36
+ if exists (select from pg_roles where rolname = '${le}') then
37
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${le}';
38
+ reassign owned by ${le} to supabase_admin;
39
+ drop owned by ${le};
40
+ drop role ${le};
41
+ end if;
42
+ alter role supabase_admin rename to ${le};
43
+ end if;
44
+ if exists (select from pg_roles where rolname = 'supabase_realtime_admin')
45
+ and exists (select from pg_roles where rolname = '${le}') then
46
+ begin
47
+ reassign owned by supabase_realtime_admin to ${le};
48
+ drop owned by supabase_realtime_admin;
49
+ drop role supabase_realtime_admin;
50
+ exception when others then
51
+ raise warning 'supabase_realtime_admin could not be removed: %', sqlerrm;
52
+ end;
53
+ end if;
54
+ end
55
+ $snoutpod$;
56
+ `,dt="snoutdata_realtime",To="supabase_realtime",Qd=`do $snoutpod$
57
+ begin
58
+ if exists (select 1 from pg_publication where pubname = '${dt}') then
36
59
  return;
37
60
  end if;
38
- if exists (select 1 from pg_publication where pubname = '${xo}') then
39
- alter publication ${xo} rename to ${ct};
61
+ if exists (select 1 from pg_publication where pubname = '${To}') then
62
+ alter publication ${To} rename to ${dt};
40
63
  return;
41
64
  end if;
42
- create publication ${ct};
43
- execute format('alter publication ${ct} owner to %I',
65
+ create publication ${dt};
66
+ execute format('alter publication ${dt} owner to %I',
44
67
  (select pg_get_userbyid(datdba) from pg_database where datname = current_database()));
45
68
  end
46
69
  $snoutpod$;
47
- `});var Bd,Hd,vo=A(()=>{"use strict";Bd=2*1024*1024,Hd=64*1024});var _o,Eo,Zi,Po=A(()=>{"use strict";_o="/snoutfn",Eo="/_snoutpod/health",Zi=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
70
+ `});var tm,nm,Ao=A(()=>{"use strict";tm=2*1024*1024,nm=64*1024});var Ro,Oo,ic,cc,lc,No=A(()=>{"use strict";Ro="/snoutfn",Oo="/_snoutpod/health",ic="x-snoutdata-door",cc="SNOUT_FUNCTIONS_DOOR_SECRET",lc=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
48
71
  // every time the agent starts, from packages/snoutpod/src/host/functionMain.ts.
49
- const MOUNT = ${JSON.stringify(_o)};
50
- const HEALTH = ${JSON.stringify(Eo)};
72
+ const MOUNT = ${JSON.stringify(Ro)};
73
+ const HEALTH = ${JSON.stringify(Oo)};
74
+ const DOOR_HEADER = ${JSON.stringify(ic)};
75
+ const DOOR = new TextEncoder().encode(Deno.env.get(${JSON.stringify(cc)}) ?? '');
76
+ if (DOOR.length === 0) {
77
+ console.error('WARNING: no door secret is set, so a request is not asked to prove it came through the front door');
78
+ }
79
+
80
+ /** Whether a request carries the door's secret, compared in constant time. */
81
+ function throughDoor(req) {
82
+ const presented = new TextEncoder().encode(req.headers.get(DOOR_HEADER) ?? '');
83
+ if (presented.length !== DOOR.length) {
84
+ return false;
85
+ }
86
+ let differ = 0;
87
+ for (let i = 0; i < DOOR.length; i++) {
88
+ differ |= presented[i] ^ DOOR[i];
89
+ }
90
+ return differ === 0;
91
+ }
92
+
93
+ /**
94
+ * The request without the door's secret: the one thing a user worker must never be handed.
95
+ *
96
+ * A copy, since a served request's headers cannot be changed, and the copy carries the
97
+ * original's tag: \`worker.fetch\` reads the connection from it and fails without it
98
+ * ("reading 'streamRid'"), which is what \`EdgeRuntime.applySupabaseTag\` is for.
99
+ */
100
+ function withoutDoor(req) {
101
+ if (!req.headers.has(DOOR_HEADER)) {
102
+ return req;
103
+ }
104
+ const headers = new Headers(req.headers);
105
+ headers.delete(DOOR_HEADER);
106
+ const copy = new Request(req, { headers });
107
+ EdgeRuntime.applySupabaseTag(req, copy);
108
+ return copy;
109
+ }
51
110
 
52
111
  /**
53
112
  * Manifests, cached by the file's modification time.
@@ -113,6 +172,12 @@ Deno.serve(async (req) => {
113
172
  return new Response('ok\\n', { headers: { 'content-type': 'text/plain' } });
114
173
  }
115
174
 
175
+ // Before the project is read: a request that did not come through the front door names
176
+ // whichever project it likes, so nothing it names may be believed.
177
+ if (DOOR.length > 0 && !throughDoor(req)) {
178
+ return refuse(403, 'This request did not arrive through the front door.');
179
+ }
180
+
116
181
  // Whose request this is, written by the front door from the hostname it resolved.
117
182
  // NOT from anything the client sent: a shared server that took the tenant from a
118
183
  // client's own header would let anybody address anybody (the Phase B finding).
@@ -140,7 +205,9 @@ Deno.serve(async (req) => {
140
205
  try {
141
206
  const worker = await EdgeRuntime.userWorkers.create({
142
207
  servicePath,
143
- memoryLimitMb: limits.memoryMb ?? 128,
208
+ // The function's own memory within its plan (sql/100), else the project's. Its
209
+ // concurrency is snout-functions' to honour; edge-runtime pools as it pools.
210
+ memoryLimitMb: deployed.memoryMb ?? limits.memoryMb ?? 128,
144
211
  workerTimeoutMs: limits.wallMs ?? 10000,
145
212
  cpuTimeSoftLimitMs: limits.cpuMs ?? 2000,
146
213
  cpuTimeHardLimitMs: limits.hardCpuMs ?? 3500,
@@ -156,7 +223,7 @@ Deno.serve(async (req) => {
156
223
  // bundle is stored per project (migration 041).
157
224
  forceCreate: false
158
225
  });
159
- return await worker.fetch(req);
226
+ return await worker.fetch(withoutDoor(req));
160
227
  } catch (error) {
161
228
  const detail = error?.message ?? String(error);
162
229
  console.error(ref + '/' + name + ': ' + detail);
@@ -166,10 +233,23 @@ Deno.serve(async (req) => {
166
233
  return refuse(500, 'This function could not be run.', detail);
167
234
  }
168
235
  });
169
- `});function sn(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function lt(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 ec(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 ec(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(sn("the subnet",n,To),sn("the sink",r,Qi),!Ao(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!Ao(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",`${sn("a denied prefix",s,To)},${r}`);return o.push(e.name),o}function $o(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function Ao(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return($o(e)&s)>>>0===($o(n??"")&s)>>>0}function Ro(e){return e?.internal===!0}var To,Qi,an=A(()=>{"use strict";To=/^((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])$/,Qi=/^((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 tc,ut=A(()=>{"use strict";tc=7*24*60*60});var Oo=A(()=>{"use strict";ie();vo();Po();an();ut()});function Do(e){let t=[];for(let n of e.split(`
170
- `)){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 No,cn=A(()=>{"use strict";No="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var um,dm,Io=A(()=>{"use strict";ie();ut();um=6*60*60,dm=5*60});var jo=A(()=>{"use strict"});var Co,Gm,Vm,zm,Ym,Mo=A(()=>{"use strict";mo();rn();on();ho();yo();bo();So();ko();Oo();cn();Io();jo();ut();Co=120,Gm=15*60*1e3,Vm=60*60*1e3,zm=60*60*1e3,Ym=10*60*1e3});import{execFile as rc}from"node:child_process";function Lo(){return(e,t,n={})=>new Promise((r,o)=>{let s=rc(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,i,u)=>{if(a&&typeof a.code=="string"){o(a);return}let d=a&&typeof a.code=="number"?a.code:0;r({code:d,stdout:String(i),stderr:String(u)})});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(`
171
- `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var dt=A(()=>{"use strict"});import{readdir as oc,readFile as tp}from"node:fs/promises";async function Uo(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||"[]"),u=(Array.isArray(a)?a[0]:a)?.Mountpoint;return u?await(n.look??sc)(u)!=="empty":!1}async function sc(e){try{return(await oc(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var Fo=A(()=>{"use strict";ie();dt()});function Bo(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 Ko(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function Wo(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=${Bo("the access key",e.key,ic)}`),n.push("--env",Ho),e.token&&n.push("--env",qo)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${Bo("the endpoint",e.endpoint,ac)}`),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 Jo(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{[Ho]:e.secret,...e.token?{[qo]:e.token}:{}}}var Ho,qo,ac,ic,Go=A(()=>{"use strict";Ho="SNOUTPOD_S3_SECRET",qo="SNOUTPOD_S3_TOKEN",ac=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,ic=/^[A-Za-z0-9/+=_.-]{1,256}$/});function zo(e){let t=[];for(let n of e.split(`
172
- `)){let r=n.split(un);if(r.length!==4||!ln.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 ln,un,Vo,Yo=A(()=>{"use strict";ln=["snouttime"];for(let e of ln)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);un="",Vo=`
236
+ `});function ln(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function mt(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 dc(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 dc(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(ln("the subnet",n,Do),ln("the sink",r,uc),!Io(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!Io(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",`${ln("a denied prefix",s,Do)},${r}`);return o.push(e.name),o}function Mo(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function Io(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(Mo(e)&s)>>>0===(Mo(n??"")&s)>>>0}function Co(e){return e?.internal===!0}var Do,uc,un=A(()=>{"use strict";Do=/^((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])$/,uc=/^((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 mc,ft=A(()=>{"use strict";mc=7*24*60*60});var jo=A(()=>{"use strict";ce();Ao();No();un();ft()});function Uo(e){let t=[];for(let n of e.split(`
237
+ `)){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 Lo,dn=A(()=>{"use strict";Lo="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var vm,Em,Fo=A(()=>{"use strict";ce();ft();vm=6*60*60,Em=5*60});var Bo=A(()=>{"use strict"});var Ho,cf,lf,uf,df,qo=A(()=>{"use strict";wo();an();cn();ko();_o();Eo();Po();$o();jo();dn();Fo();Bo();ft();Ho=120,cf=15*60*1e3,lf=60*60*1e3,uf=60*60*1e3,df=10*60*1e3});import{execFile as pc}from"node:child_process";function Ko(){return(e,t,n={})=>new Promise((r,o)=>{let s=pc(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,i,u)=>{if(a&&typeof a.code=="string"){o(a);return}let d=a&&typeof a.code=="number"?a.code:0;r({code:d,stdout:String(i),stderr:String(u)})});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(`
238
+ `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var pt=A(()=>{"use strict"});import{readdir as gc,readFile as hf}from"node:fs/promises";async function zo(e,t,n={}){let r=n.binary??"podman",o=M(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),u=(Array.isArray(a)?a[0]:a)?.Mountpoint;return u?await(n.look??hc)(u)!=="empty":!1}async function hc(e){try{return(await gc(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var Wo=A(()=>{"use strict";ce();pt()});function Jo(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 Yo(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function Xo(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=${Jo("the access key",e.key,wc)}`),n.push("--env",Go),e.token&&n.push("--env",Vo)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${Jo("the endpoint",e.endpoint,yc)}`),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 Zo(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{[Go]:e.secret,...e.token?{[Vo]:e.token}:{}}}var Go,Vo,yc,wc,Qo=A(()=>{"use strict";Go="SNOUTPOD_S3_SECRET",Vo="SNOUTPOD_S3_TOKEN",yc=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,wc=/^[A-Za-z0-9/+=_.-]{1,256}$/});function mn(e){return bc[e]??e}var ne,bc,es,ts=A(()=>{"use strict";ne="snout_auth_admin",bc={supabase_auth_admin:ne};es=`do $snoutpod$
239
+ begin
240
+ if exists (select from pg_roles where rolname = 'supabase_auth_admin') then
241
+ if exists (select from pg_roles where rolname = '${ne}') then
242
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${ne}';
243
+ reassign owned by ${ne} to supabase_auth_admin;
244
+ drop owned by ${ne};
245
+ drop role ${ne};
246
+ end if;
247
+ alter role supabase_auth_admin rename to ${ne};
248
+ end if;
249
+ end
250
+ $snoutpod$;
251
+ `});function rs(e){let t=[];for(let n of e.split(`
252
+ `)){let r=n.split(pn);if(r.length!==4||!fn.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 fn,pn,ns,os=A(()=>{"use strict";fn=["snouttime"];for(let e of fn)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);pn="",ns=`
173
253
  CREATE TEMP TABLE snoutpod_extension_updates (name text, from_version text, to_version text, error text);
174
254
  DO $snoutpod$
175
255
  DECLARE
@@ -179,7 +259,7 @@ BEGIN
179
259
  SELECT e.extname, e.extversion
180
260
  FROM pg_extension e
181
261
  JOIN pg_available_extensions a ON a.name = e.extname
182
- WHERE e.extname = ANY (ARRAY[${ln.map(e=>`'${e}'`).join(", ")}])
262
+ WHERE e.extname = ANY (ARRAY[${fn.map(e=>`'${e}'`).join(", ")}])
183
263
  AND a.default_version IS DISTINCT FROM e.extversion
184
264
  LOOP
185
265
  BEGIN
@@ -194,60 +274,60 @@ BEGIN
194
274
  END
195
275
  $snoutpod$;
196
276
  SELECT name, from_version, coalesce(to_version, ''), coalesce(error, '') FROM snoutpod_extension_updates ORDER BY name;
197
- `});import{createHash as cc,randomBytes as lc}from"node:crypto";import{access as uc,constants as dc}from"node:fs/promises";import{delimiter as mc,join as pc}from"node:path";function fn(e){if(!gc.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function je(e){if(!hc.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function yc(e,t,n){return`postgres://${je(t)}:${n}@127.0.0.1:5432/${J(e)}`}function Xo(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]=yc(e.ref,t.dbUriRole,s)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),o}function Zo(e,t){return`${D(e).pod}-${fn(t)}`}function mt(e){return`snoutpod-host-${fn(e)}`}function wc(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return cc("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 bc(e,t){let n=[],r=mn(t);for(let o of e??[])if(fn(o.name),pn(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){pn(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function mn(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 pn(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function es(e){let t=[];for(let n of Object.keys(e)){if(!Sc.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function xc(e){let t=e.healthPath;if(t==null||t==="")return[];if(!rs.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(pn(e.hostPort,`${e.name}: hostPort`),["--label",`${gn}=${e.hostPort}:${t}`])}function kc(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||!rs.test(r)?null:{port:n,path:r}}async function _c(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>uc(o,dc.X_OK));for(let o of n.split(mc))if(o)try{let s=pc(o,e);return await r(s),s}catch{}return null}function Ec(){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(`
198
- `)}function Tc(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:ts;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,ts)}`),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 $c(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??vc;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await _c(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function ss(e){let t=e.binary??"podman",n=e.exec,r=$c(e),o=e.probe??os;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:Pc,stdin:Ec()})}catch{}}async function a(){let c=await R(n,t,["ps","--all","--filter",`label=${_e}`,"--format","json"]),p=c.trim()?JSON.parse(c):[],f=new Map;for(let S of p){let h=S.Labels?.[_e],$=S.Labels?.[De];if(!h||!$)continue;let I=f.get(h)??[];I.push($),f.set(h,I)}let k=[];for(let S of p){let h=S.Labels?.[_e];if(!h||S.Labels?.[De])continue;let $=Number(S.Labels?.[dn]);k.push({ref:h,status:Ac(S.State),generation:Number.isInteger($)?$:null,hasData:await e.hasData(h),services:f.get(h)??[]})}return k}async function i(c){let p=D(c.ref),f=e.credentials,k=await Ko(e.repository,f?()=>f.forProject(c.ref,c.bucket):null),S={SNOUTPOD_OWNER_PASSWORD:c.ownerPassword,POSTGRES_PASSWORD:c.adminPassword,...Jo(k)},h=[...await r(),"run","--detach","--name",p.pod,"--label",`${_e}=${c.ref}`,"--label",`${dn}=${c.generation}`,"--userns=auto","--memory",`${c.memoryMb}m`,...Oc(c.cpus),"--restart","no",...e.network?["--network",e.network]:[],...c.port?mn(e.podBind).flatMap($=>["--publish",`${$}:${c.port}:5432`]):[],...bc(c.services,e.podBind),"--volume",`${p.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"]:[],...Tc(c.tuning).flatMap($=>["--env",$]),"--env",`SNOUTPOD_RETENTION_FULL=${fo(c.backup?.retentionDays,c.backup?.fullIntervalHours)}`,...Wo(k),c.image,"snoutpod-postgres"];await R(n,t,h,{env:S})}async function u(c){let p=await R(n,t,["ps","--all","--filter",`label=${_e}=${J(c)}`,"--filter",`label=${De}`,"--format","json"]),f=p.trim()?JSON.parse(p):[],k=[];for(let S of f){let h=S.Labels?.[De];h&&k.push({name:h,container:Zo(c,h)})}return k}async function d(c){let p=[];for(let $ of c.services??[])$.dbUriEnv&&$.dbUriRole&&!p.includes($.dbUriRole)&&p.push(je($.dbUriRole));let f=new Map;if(p.length===0)return f;let k={},S=[],h=[];return p.forEach(($,I)=>{let O=lc(24).toString("base64url"),le=`SNOUTPOD_SERVICE_PASSWORD_${I}`;f.set($,O),k[le]=O,S.push("--env",le),h.push(`\\getenv service_password_${I} ${le}`),h.push(`\\set service_role_${I} ${$}`),h.push(`alter role :"service_role_${I}" with password :'service_password_${I}';`)}),h.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:h.join(`
199
- `)}),f}async function m(c){for(let k of await u(c.ref))await R(n,t,["rm","--force","--ignore",k.container]);let p=await d(c),f;(c.services??[]).some(k=>k.smtpSecretEnv)&&(f=await e.smtpSecret?.().catch(()=>{}));for(let k of c.services??[]){let S=Zo(c.ref,k.name),h=[...await r(),"run","--detach","--name",S,"--label",`${_e}=${c.ref}`,"--label",`${dn}=${c.generation}`,"--label",`${De}=${k.name}`,"--network",`container:${D(c.ref).pod}`,"--userns",`container:${D(c.ref).pod}`,"--memory",`${k.memoryMb}m`,"--restart","no",...xc(k),...es(Xo(c,k,p,f)),k.image];await R(n,t,h,{env:Xo(c,k,p,f)})}}return{list:a,create:i,createServices:m,async start(c){await R(n,t,["start",D(c).pod])},async startServices(c){for(let p of await u(c))await R(n,t,["start",p.container])},async applyHostServices(c){let p=[],f=await R(n,t,["ps","--all","--filter",`label=${Ie}`,"--format","json"]),k=f.trim()?JSON.parse(f):[],S=new Map;for(let $ of k){let I=$.Labels?.[Ie];I&&S.set(I,{spec:$.Labels?.[Qo]??"",state:$.State??""})}for(let $ of c)try{await h($)}catch(I){p.push({name:$.name,action:"failed",reason:I instanceof Error?I.message:String(I)})}async function h($){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=mt(O.name),En=wc(O),ue=S.get(O.name);if(S.delete(O.name),ue&&ue.spec===En){ue.state!=="running"&&(await R(n,t,["start",le]),p.push({name:O.name,action:"started",reason:ue.state}));return}let js=ue?"the spec changed":"it was not here";ue&&((await n(t,[...await r(),"image","exists",O.image])).code!==0&&await R(n,t,[...await r(),"pull","--quiet",O.image]),await R(n,t,["rm","--force","--ignore",le])),await R(n,t,[...await r(),"run","--detach","--name",le,"--label",`${Ie}=${O.name}`,"--label",`${Qo}=${En}`,...O.healthPath?["--label",`${gn}=${O.hostPort}:${O.healthPath}`]:[],"--memory",`${O.memoryMb}m`,"--restart","no",...O.network?["--network",O.network]:e.network?["--network",e.network]:[],...(O.volumes??[]).flatMap(Be=>["--volume",Be]),...mn(e.podBind).flatMap(Be=>["--publish",`${Be}:${O.hostPort}:${O.port}`]),...O.adminPort&&O.adminHostPort?["--publish",`127.0.0.1:${O.adminHostPort}:${O.adminPort}`]:[],...es(O.env),O.image,...O.command??[]],{env:{...O.env}}),p.push({name:O.name,action:ue?"recreated":"created",reason:js})}for(let $ of S.keys())await R(n,t,["rm","--force","--ignore",mt($)]),p.push({name:$,action:"removed",reason:"it is no longer on the list"});return p},async hostServiceHealth(c){let p=await R(n,t,["ps","--all","--filter",`label=${Ie}`,"--format","json"]),f=(p.trim()?JSON.parse(p):[]).map(S=>S.Labels?.[Ie]??"").filter(S=>S!=="");if(f.length===0)return[];let k=Date.now()+c;for(;;){let S=[];for(let h of f)S.push(await ns(n,t,h,mt(h),o));if(S.every(h=>h.ok)||Date.now()>=k)return S;await new Promise(h=>setTimeout(h,500))}},async serviceHealth(c,p){let f=await u(c);if(f.length===0)return[];let k=Date.now()+p;for(;;){let S=[];for(let h of f)S.push(await ns(n,t,h.name,h.container,o));if(S.every(h=>h.ok)||Date.now()>=k)return S;await new Promise(h=>setTimeout(h,500))}},async stop(c,p){for(let f of await u(c))await R(n,t,["stop","--time",String(fc),f.container]);await R(n,t,["stop","--time",String(p),D(c).pod])},async remove(c){for(let p of await u(c))await R(n,t,["rm","--force","--ignore",p.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 p=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${c.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
200
- `);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:p})},async applyRolePassword(c,p,f){let k=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${je(p)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
201
- `);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:f},stdin:k})},async updateExtensions(c){let p=D(c),f=await R(n,t,["exec","--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-tA","-F",un,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:Vo});return zo(f)},async runProjectSql(c,p){let f=D(c);await R(n,t,["exec","--interactive",f.pod,"psql","--username","snoutpod_admin","--dbname",f.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:p})},async ensureHostServiceDatabase(c,p,f){let k=mt(c),S=je(p),h=je(f),$=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 = '${h}'`))).trim()!=="1"&&await R(n,t,$(`create database "${h}"`))},async replicationSlots(c){let p=await R(n,t,["exec",D(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",No]);return Do(p)},async dropReplicationSlot(c,p){if(!/^[a-z0-9_]{1,63}$/.test(p))throw new Error(`not a replication slot name: ${JSON.stringify(p)}`);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('${p}')`])},async renameProject(c,p){p!==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:Rc(p,c.ref)})},async waitReady(c,p){let f=D(c).pod,k=Date.now()+p;for(;;){let S=await n(t,["exec",f,"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(h=>setTimeout(h,500))}},async capacity(){let c=await a(),p=await e.capacity();return{pods:c.length,...p}},async databaseBytes(c){let p=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 p.code===0?go(p.stdout):null},async setReadOnly(c,p){let f=D(c);await R(n,t,["exec",f.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${f.database} set default_transaction_read_only = ${p?"on":"off"}`])},async archiveState(c){let p=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(p.code!==0)return null;let[f="",k=""]=p.stdout.trim().split(`
202
- `)[0]?.split("|")??[],S=Number(k);return{lastArchivedAt:f.trim()||null,failures:Number.isInteger(S)&&S>=0?S:0}}}}async function ns(e,t,n,r,o=os){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 u=a?.Status??"unknown";if(u!=="running")return{name:n,ok:!1,detail:u};let d=kc(i?.[gn]);if(!d)return{name:n,ok:!0,detail:"running"};let m=await o(d.port,d.path);return m===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${m}`}}function Ac(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function Rc(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(`
203
- `)}function Oc(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var _e,dn,De,fc,gc,hc,Ie,Qo,Sc,rs,gn,os,vc,Pc,ts,as=A(()=>{"use strict";ie();rn();cn();dt();Go();on();Yo();_e="snoutpod.ref",dn="snoutpod.generation",De="snoutpod.service",fc=10,gc=/^[a-z][a-z0-9-]{0,30}$/;hc=/^[a-z][a-z0-9_]{0,62}$/;Ie="snoutpod.host-service",Qo="snoutpod.host-service-spec";Sc=/^[A-Za-z_][A-Za-z0-9_]*$/;rs=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,gn="snoutpod.health";os=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()})},vc="crun";Pc=3e4,ts=200});var hs={};Pn(hs,{DEFAULT_LOCAL_MEMORY_MB:()=>ls,DEFAULT_LOCAL_PORT:()=>jc,ENGINE:()=>L,ENGINE_NAME:()=>wn,IMAGE:()=>fe,NETWORK:()=>ce,NO_BLACKHOLE_ROUTES:()=>ds,PUBLIC_IMAGE:()=>Ee,capacity:()=>us,ensureImage:()=>Uc,ensureNetwork:()=>Kc,exec:()=>H,hasData:()=>pt,localPsql:()=>Gc,localRuntime:()=>qc,localSpec:()=>fs,localStatus:()=>bn,localUri:()=>ps,networkPolicyInPlace:()=>ms,newLocalPassword:()=>Mc,newLocalRef:()=>Cc,podLog:()=>gs,podName:()=>ft,podmanReady:()=>Bc,removeLocal:()=>Yc,startLocal:()=>Jc,stopLocal:()=>zc});import{randomBytes as cs}from"node:crypto";import{existsSync as is}from"node:fs";import{platform as yn}from"node:os";import{delimiter as Nc,join as Dc}from"node:path";function Ic(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=yn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(Nc))if(t&&e.some(n=>is(Dc(t,n))))return"podman";return is("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function Cc(){return uo(e=>cs(e))}function Mc(){return cs(18).toString("base64url")}async function Uc(e,t=H){if(process.env.SNOUTPOD_IMAGE||(await t(L,["image","exists",fe])).code===0)return null;e?.(`Fetching the database image (${Ee}). This happens once.`);let n=await t(L,["pull",Ee],{timeoutMs:Fc}).catch(o=>({code:1,stdout:"",stderr:o instanceof Error?o.message:String(o)}));if(n.code!==0)return`could not fetch the database image ${Ee}: ${n.stderr.trim()||n.stdout.trim()||"the pull failed"}`;let r=await t(L,["tag",Ee,fe]);return r.code!==0?`fetched ${Ee} but could not tag it as ${fe}: ${r.stderr.trim()}`:(e?.("Got it."),null)}async function Bc(e=Lc){if(wn!=="podman")return`SNOUTPOD_ENGINE=${wn}: 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 Hc(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(`
204
- `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function pt(e){let t=yn()==="darwin"||yn()==="win32";return Uo(H,e,{binary:L,look:t?Hc:void 0})}async function us(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function qc(e){return ss({exec:H,binary:L,network:ce,hasData:pt,capacity:us,repository:e})}function ft(e){return D(e).pod}async function Kc(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Wc()||await H(L,lt({name:ce}));return}let t=lt({name:ce,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await ms())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(`${ds}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${ce} with egress blocked${n?`: ${n}`:""}`)}async function Wc(){return(await H(L,["network","inspect",ce,"--format","json"])).code===0}async function ms(){let e=await H(L,["network","inspect",ce,"--format","json"]);if(e.code!==0)return!1;try{return Ro(JSON.parse(e.stdout)?.[0])}catch{return!1}}function ps(e){let t=D(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function fs(e,t={}){return J(e.ref),{ref:e.ref,generation:1,image:t.image??fe,memoryMb:t.memoryMb??ls,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 hn(e,t){e.onProgress?.(t)}async function Jc(e,t,n={}){let r=fs(t,n),o=n.timeoutMs??12e4,s=n.hasData??pt,a=(await e.list()).find(i=>i.ref===t.ref);if(a?a.status==="stopped"?(hn(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):hn(n,"The database is already running."):(hn(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 Vc(t.ref,o,n.podLog??gs));return await e.applyOwnerPassword(r),await e.createServices(r),bn(e,t,n)}async function Gc(e,t){let n=D(e.ref);return H(L,["exec","--interactive","-e","PGPASSWORD",ft(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 gs(e){let t=await H(L,["logs","--tail","20",ft(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function Vc(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${ft(e)} never became ready in ${t}ms
277
+ `});import{createHash as Sc,randomBytes as xc}from"node:crypto";import{access as kc,constants as _c}from"node:fs/promises";import{delimiter as vc,join as Ec}from"node:path";function wn(e){if(!Tc.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function je(e){if(!$c.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function Ac(e,t,n){return`postgres://${je(t)}:${n}@127.0.0.1:5432/${W(e)}`}function ss(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=mn(t.dbUriRole),a=n.get(s);if(a===void 0)throw new Error(`no password was applied for role ${JSON.stringify(t.dbUriRole)}, which ${JSON.stringify(t.name)} needs to reach its database`);o[t.dbUriEnv]=Ac(e.ref,s,a)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),Rc(t.image,o)}function Rc(e,t){if(!/(^|\/)snout-auth([:@]|$)/.test(e))return{...t};let n={};for(let[r,o]of Object.entries(t)){let s=r.startsWith("GOTRUE_")?`AUTH_${r.slice(7)}`:r==="API_EXTERNAL_URL"?"AUTH_API_EXTERNAL_URL":r;s!==r&&s in t||(n[s]=o)}return n}function as(e,t){return`${M(e).pod}-${wn(t)}`}function gt(e){return`snoutpod-host-${wn(e)}`}function Oc(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return Sc("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 Nc(e,t){let n=[],r=hn(t);for(let o of e??[])if(wn(o.name),yn(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){yn(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function hn(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 yn(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function cs(e){let t=[];for(let n of Object.keys(e)){if(!Dc.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function Mc(e){let t=e.healthPath;if(t==null||t==="")return[];if(!ds.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(yn(e.hostPort,`${e.name}: hostPort`),["--label",`${bn}=${e.hostPort}:${t}`])}function Ic(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||!ds.test(r)?null:{port:n,path:r}}async function jc(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>kc(o,_c.X_OK));for(let o of n.split(vc))if(o)try{let s=Ec(o,e);return await r(s),s}catch{}return null}function Lc(){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(`
278
+ `)}function Fc(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:ls;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,ls)}`),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 Bc(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??Cc;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await jc(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function fs(e){let t=e.binary??"podman",n=e.exec,r=Bc(e),o=e.probe??ms;async function s(c){try{await n(t,["exec","--interactive",M(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"--quiet","--file","-"],{timeoutMs:Uc,stdin:Lc()})}catch{}}async function a(){let c=await R(n,t,["ps","--all","--filter",`label=${Te}`,"--format","json"]),f=c.trim()?JSON.parse(c):[],p=new Map;for(let S of f){let h=S.Labels?.[Te],$=S.Labels?.[Ie];if(!h||!$)continue;let D=p.get(h)??[];D.push($),p.set(h,D)}let x=[];for(let S of f){let h=S.Labels?.[Te];if(!h||S.Labels?.[Ie])continue;let $=Number(S.Labels?.[gn]);x.push({ref:h,status:Hc(S.State),generation:Number.isInteger($)?$:null,hasData:await e.hasData(h),services:p.get(h)??[]})}return x}async function i(c){let f=M(c.ref),p=e.credentials,x=await Yo(e.repository,p?()=>p.forProject(c.ref,c.bucket):null),S={SNOUTPOD_OWNER_PASSWORD:c.ownerPassword,POSTGRES_PASSWORD:c.adminPassword,...Zo(x)},h=[...await r(),"run","--detach","--name",f.pod,"--label",`${Te}=${c.ref}`,"--label",`${gn}=${c.generation}`,"--userns=auto","--memory",`${c.memoryMb}m`,...Kc(c.cpus),"--restart","no",...e.network?["--network",e.network]:[],...c.port?hn(e.podBind).flatMap($=>["--publish",`${$}:${c.port}:5432`]):[],...Nc(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"]:[],...Fc(c.tuning).flatMap($=>["--env",$]),"--env",`SNOUTPOD_RETENTION_FULL=${So(c.backup?.retentionDays,c.backup?.fullIntervalHours)}`,...Xo(x),c.image,"snoutpod-postgres"];await R(n,t,h,{env:S})}async function u(c){let f=await R(n,t,["ps","--all","--filter",`label=${Te}=${W(c)}`,"--filter",`label=${Ie}`,"--format","json"]),p=f.trim()?JSON.parse(f):[],x=[];for(let S of p){let h=S.Labels?.[Ie];h&&x.push({name:h,container:as(c,h)})}return x}async function d(c){let f=[];for(let $ of c.services??[]){let D=$.dbUriRole?mn($.dbUriRole):null;$.dbUriEnv&&D&&!f.includes(D)&&f.push(je(D))}let p=new Map;if(f.length===0)return p;let x={},S=[],h=f.includes(ne)?[es]:[];return f.forEach(($,D)=>{let O=xc(24).toString("base64url"),de=`SNOUTPOD_SERVICE_PASSWORD_${D}`;p.set($,O),x[de]=O,S.push("--env",de),h.push(`\\getenv service_password_${D} ${de}`),h.push(`\\set service_role_${D} ${$}`),h.push(`alter role :"service_role_${D}" with password :'service_password_${D}';`)}),h.push(""),await R(n,t,["exec","--interactive",...S,M(c.ref).pod,"psql","--username","snoutpod_admin","--dbname",M(c.ref).database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:x,stdin:h.join(`
279
+ `)}),p}async function m(c){for(let x of await u(c.ref))await R(n,t,["rm","--force","--ignore",x.container]);let f=await d(c),p;(c.services??[]).some(x=>x.smtpSecretEnv)&&(p=await e.smtpSecret?.().catch(()=>{}));for(let x of c.services??[]){let S=as(c.ref,x.name),h=[...await r(),"run","--detach","--name",S,"--label",`${Te}=${c.ref}`,"--label",`${gn}=${c.generation}`,"--label",`${Ie}=${x.name}`,"--network",`container:${M(c.ref).pod}`,"--userns",`container:${M(c.ref).pod}`,"--memory",`${x.memoryMb}m`,"--restart","no",...Mc(x),...cs(ss(c,x,f,p)),x.image];await R(n,t,h,{env:ss(c,x,f,p)})}}return{list:a,create:i,createServices:m,async start(c){await R(n,t,["start",M(c).pod])},async startServices(c){for(let f of await u(c))await R(n,t,["start",f.container])},async applyHostServices(c){let f=[],p=await R(n,t,["ps","--all","--filter",`label=${Ce}`,"--format","json"]),x=p.trim()?JSON.parse(p):[],S=new Map;for(let $ of x){let D=$.Labels?.[Ce];D&&S.set(D,{spec:$.Labels?.[is]??"",state:$.State??""})}for(let $ of c)try{await h($)}catch(D){f.push({name:$.name,action:"failed",reason:D instanceof Error?D.message:String(D)})}async function h($){let D=e.hostServiceLocal?.($,c),O=D?{...$,...D.env&&Object.keys(D.env).length>0?{env:{...$.env,...D.env}}:{},...D.volumes?{volumes:D.volumes}:{},...D.network?{network:D.network}:{}}:$,de=gt(O.name),An=Oc(O),me=S.get(O.name);if(S.delete(O.name),me&&me.spec===An){me.state!=="running"&&(await R(n,t,["start",de]),f.push({name:O.name,action:"started",reason:me.state}));return}let qs=me?"the spec changed":"it was not here";me&&((await n(t,[...await r(),"image","exists",O.image])).code!==0&&await R(n,t,[...await r(),"pull","--quiet",O.image]),await R(n,t,["rm","--force","--ignore",de])),await R(n,t,[...await r(),"run","--detach","--name",de,"--label",`${Ce}=${O.name}`,"--label",`${is}=${An}`,...O.healthPath?["--label",`${bn}=${O.hostPort}:${O.healthPath}`]:[],"--memory",`${O.memoryMb}m`,"--restart","no",...O.network?["--network",O.network]:e.network?["--network",e.network]:[],...(O.volumes??[]).flatMap(qe=>["--volume",qe]),...hn(e.podBind).flatMap(qe=>["--publish",`${qe}:${O.hostPort}:${O.port}`]),...O.adminPort&&O.adminHostPort?["--publish",`127.0.0.1:${O.adminHostPort}:${O.adminPort}`]:[],...cs(O.env),O.image,...O.command??[]],{env:{...O.env}}),f.push({name:O.name,action:me?"recreated":"created",reason:qs})}for(let $ of S.keys())await R(n,t,["rm","--force","--ignore",gt($)]),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=${Ce}`,"--format","json"]),p=(f.trim()?JSON.parse(f):[]).map(S=>S.Labels?.[Ce]??"").filter(S=>S!=="");if(p.length===0)return[];let x=Date.now()+c;for(;;){let S=[];for(let h of p)S.push(await us(n,t,h,gt(h),o));if(S.every(h=>h.ok)||Date.now()>=x)return S;await new Promise(h=>setTimeout(h,500))}},async serviceHealth(c,f){let p=await u(c);if(p.length===0)return[];let x=Date.now()+f;for(;;){let S=[];for(let h of p)S.push(await us(n,t,h.name,h.container,o));if(S.every(h=>h.ok)||Date.now()>=x)return S;await new Promise(h=>setTimeout(h,500))}},async stop(c,f){for(let p of await u(c))await R(n,t,["stop","--time",String(Pc),p.container]);await R(n,t,["stop","--time",String(f),M(c).pod])},async remove(c){for(let f of await u(c))await R(n,t,["rm","--force","--ignore",f.container]);await R(n,t,["rm","--force","--ignore",M(c).pod])},async removeData(c){await R(n,t,["volume","rm","--force",M(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(`
280
+ `);await R(n,t,["exec","--interactive","--env","SNOUTPOD_OWNER_PASSWORD",M(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 x=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${je(f)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
281
+ `);await R(n,t,["exec","--interactive","--env","SNOUTPOD_ROLE_PASSWORD",M(c).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:p},stdin:x})},async updateExtensions(c){let f=M(c),p=await R(n,t,["exec","--interactive",f.pod,"psql","--username","snoutpod_admin","--dbname",f.database,"--quiet","-tA","-F",pn,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:ns});return rs(p)},async runProjectSql(c,f){let p=M(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 x=gt(c),S=je(f),h=je(p),$=O=>["exec",x,"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 = '${h}'`))).trim()!=="1"&&await R(n,t,$(`create database "${h}"`))},async replicationSlots(c){let f=await R(n,t,["exec",M(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tA","-F","|","-c",Lo]);return Uo(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",M(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",M(c.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:qc(f,c.ref)})},async waitReady(c,f){let p=M(c).pod,x=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()>=x)return!1;await new Promise(h=>setTimeout(h,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",M(c).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",c,"-tAc","select pg_database_size(current_database())"]);return f.code===0?xo(f.stdout):null},async setReadOnly(c,f){let p=M(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",M(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="",x=""]=f.stdout.trim().split(`
282
+ `)[0]?.split("|")??[],S=Number(x);return{lastArchivedAt:p.trim()||null,failures:Number.isInteger(S)&&S>=0?S:0}}}}async function us(e,t,n,r,o=ms){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 u=a?.Status??"unknown";if(u!=="running")return{name:n,ok:!1,detail:u};let d=Ic(i?.[bn]);if(!d)return{name:n,ok:!0,detail:"running"};let m=await o(d.port,d.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 qc(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(`
283
+ `)}function Kc(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var Te,gn,Ie,Pc,Tc,$c,Ce,is,Dc,ds,bn,ms,Cc,Uc,ls,ps=A(()=>{"use strict";ce();an();dn();pt();Qo();cn();ts();os();Te="snoutpod.ref",gn="snoutpod.generation",Ie="snoutpod.service",Pc=10,Tc=/^[a-z][a-z0-9-]{0,30}$/;$c=/^[a-z][a-z0-9_]{0,62}$/;Ce="snoutpod.host-service",is="snoutpod.host-service-spec";Dc=/^[A-Za-z_][A-Za-z0-9_]*$/;ds=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,bn="snoutpod.health";ms=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()})},Cc="crun";Uc=3e4,ls=200});var vs={};Rn(vs,{DEFAULT_LOCAL_MEMORY_MB:()=>ys,DEFAULT_LOCAL_PORT:()=>Gc,ENGINE:()=>L,ENGINE_NAME:()=>kn,IMAGE:()=>ye,NETWORK:()=>ue,NO_BLACKHOLE_ROUTES:()=>bs,PUBLIC_IMAGE:()=>$e,capacity:()=>ws,ensureImage:()=>Zc,ensureNetwork:()=>rl,exec:()=>H,hasData:()=>ht,localPsql:()=>al,localRuntime:()=>nl,localSpec:()=>ks,localStatus:()=>_n,localUri:()=>xs,networkPolicyInPlace:()=>Ss,newLocalPassword:()=>Yc,newLocalRef:()=>Vc,podLog:()=>_s,podName:()=>yt,podmanReady:()=>el,removeLocal:()=>ll,startLocal:()=>sl,stopLocal:()=>cl});import{randomBytes as hs}from"node:crypto";import{existsSync as gs}from"node:fs";import{platform as xn}from"node:os";import{delimiter as zc,join as Wc}from"node:path";function Jc(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=xn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(zc))if(t&&e.some(n=>gs(Wc(t,n))))return"podman";return gs("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function Vc(){return yo(e=>hs(e))}function Yc(){return hs(18).toString("base64url")}async function Zc(e,t=H){if(process.env.SNOUTPOD_IMAGE||(await t(L,["image","exists",ye])).code===0)return null;e?.(`Fetching the database image (${$e}). This happens once.`);let n=await t(L,["pull",$e],{timeoutMs:Qc}).catch(o=>({code:1,stdout:"",stderr:o instanceof Error?o.message:String(o)}));if(n.code!==0)return`could not fetch the database image ${$e}: ${n.stderr.trim()||n.stdout.trim()||"the pull failed"}`;let r=await t(L,["tag",$e,ye]);return r.code!==0?`fetched ${$e} but could not tag it as ${ye}: ${r.stderr.trim()}`:(e?.("Got it."),null)}async function el(e=Xc){if(kn!=="podman")return`SNOUTPOD_ENGINE=${kn}: 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",ye])).code!==0)return`the pod image ${ye} 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 tl(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(`
284
+ `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function ht(e){let t=xn()==="darwin"||xn()==="win32";return zo(H,e,{binary:L,look:t?tl:void 0})}async function ws(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function nl(e){return fs({exec:H,binary:L,network:ue,hasData:ht,capacity:ws,repository:e})}function yt(e){return M(e).pod}async function rl(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await ol()||await H(L,mt({name:ue}));return}let t=mt({name:ue,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await Ss())return;await e?.(),await H(L,["network","rm","--force",ue]).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(`${bs}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${ue} with egress blocked${n?`: ${n}`:""}`)}async function ol(){return(await H(L,["network","inspect",ue,"--format","json"])).code===0}async function Ss(){let e=await H(L,["network","inspect",ue,"--format","json"]);if(e.code!==0)return!1;try{return Co(JSON.parse(e.stdout)?.[0])}catch{return!1}}function xs(e){let t=M(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function ks(e,t={}){return W(e.ref),{ref:e.ref,generation:1,image:t.image??ye,memoryMb:t.memoryMb??ys,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 Sn(e,t){e.onProgress?.(t)}async function sl(e,t,n={}){let r=ks(t,n),o=n.timeoutMs??12e4,s=n.hasData??ht,a=(await e.list()).find(i=>i.ref===t.ref);if(a?a.status==="stopped"?(Sn(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):Sn(n,"The database is already running."):(Sn(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 il(t.ref,o,n.podLog??_s));return await e.applyOwnerPassword(r),await e.createServices(r),_n(e,t,n)}async function al(e,t){let n=M(e.ref);return H(L,["exec","--interactive","-e","PGPASSWORD",yt(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 _s(e){let t=await H(L,["logs","--tail","20",yt(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function il(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${yt(e)} never became ready in ${t}ms
205
285
  --- pod log ---
206
- ${r}`}async function zc(e,t,n={}){J(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,Co),bn(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Yc(e,t,n){J(t),await e.remove(t),n&&await e.removeData(t)}async function bn(e,t,n={}){J(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??pt;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?ps(t):null}}var wn,L,fe,ce,jc,ls,H,Lc,Ee,Fc,ds,ys=A(()=>{"use strict";Mo();dt();Fo();an();as();ie();wn=process.env.SNOUTPOD_ENGINE??"podman";L=Ic(),fe=process.env.SNOUTPOD_IMAGE??"snoutpod/postgres:17",ce=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",jc=54322,ls=512,H=Lo();Lc="cd packages/snoutpod && npm run image:build",Ee="ghcr.io/snoutdata/snoutpod-postgres:17";Fc=20*60*1e3;ds="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});z();var yt=[{name:"init",summary:"Create or link a project and write a DATABASE_URL",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to",flags:[]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database",flags:["--name","--region","--team","--no-wait","--timeout"]},{name:"projects pause",summary:"Stop a project",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, the data API and push: on or off",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions 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();we();ge();vt();Ve();U();Tt();M();M();ge();U();async function or(){let{projects:e,allowance:t}=await G();g({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?E("read-only"):n.state==="ready"?n.state:E(n.state),n.region,Z(n.lastConnectionAt)])])}
207
- `)})}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&&qe(e.link,{ref:n,name:r.name}),g({project:r,password:t.password,uri:`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`},()=>{process.stdout.write(`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require
208
- `),l(""),l(`Ready. ${E(n)} in ${r.region}.`),l("The password is stored for you: `snoutdata db url` prints this again.")})}async function sr(e,t){let n=`cloud-project-${e}`,r=await w(n,{ref:t});g(r,()=>{l(r.changed?`${t}: ${e}d.`:`${t} was already ${e==="delete"?"deleted":`${e}d`}.`)})}async function ar(e){let t=await w("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
209
- `),l(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function ir(){let{teams:e}=await tt();g({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"])])}
210
- `)})}async function cr(e){let{teams:t}=await tt(),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();U();async function lr(e){let t=await w("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
211
- `),l(""),l(`${E(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),l(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),l("This is the only time it is shown: only its hash is stored."),l(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function ur(){let{tokens:e}=await w("cloud-token-list");g({tokens:e},()=>{if(e.length===0){l("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${j([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?E(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",Z(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
212
- `)})}async function dr(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await w("cloud-token-revoke",t);g(n,()=>{l(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}M();z();U();async function mr(e){let t=await W(e);g({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(`${fr(t.anonKey,t.serviceRoleKey)}
213
- `),Aa(t.keysIssuedAt)})}async function pr(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});g(n,()=>{process.stdout.write(`${fr(n.anonKey,n.serviceRoleKey)}
214
- `),l(""),l(n.breaks),l(b(" snoutdata keys --json to read them again later"))})}function fr(e,t){return j([["KEY","VALUE"],["anon",e],["service_role",t]])}function Aa(e){l(""),l(`${E("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),l(`${E("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&l(b(` issued ${e.slice(0,10)}`))}M();z();U();import{readdir as La,readFile as Ua,stat as Fa}from"node:fs/promises";import{join as It,relative as Ba,sep as Ha}from"node:path";var Oa=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function Na(e){return Oa.test(e)}function Ot(e){if(!Na(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 Da=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function Ia(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!==".."&&Da.test(n))}var Rt=2*1024*1024,gr=200;function hr(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>gr)throw new Error(`a function may hold ${gr} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!Ia(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=ja(o);if(s.length>Rt)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${Rt/1024} KB. A function is source code; put large files in Storage.`);return o}function ja(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 Ca=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/,Ma=["SUPABASE_URL","SUPABASE_ANON_KEY","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_DB_URL"];function Nt(e){return Ca.test(e)?e.startsWith("SNOUT_")?`names beginning with SNOUT_ are set by the platform, so ${e} cannot be one of yours.`:Ma.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 xu=64*1024;var yr=["functions","supabase/functions"];async function Dt(e){try{return(await Fa(e)).isDirectory()}catch{return!1}}async function qa(e,t){if(t){if(!await Dt(t))throw new y(`${t} is not a directory`);return t}for(let n of yr){let r=It(n,e);if(await Dt(r))return r}throw new y(`no folder found for ${e} at ${yr[0]}/${e}; use --dir to say where it is.`)}async function wr(e){let t=[];async function n(r){for(let o of await La(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=It(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await Ua(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(Ha).join("/"),text:i})}}return await n(e),t}async function Ka(e,t,n){let r=await wr(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=It(t,"..","_shared");if(!await Dt(s))return{entrypoint:o,files:r};let a=(await wr(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 jt(e,t,n){Ot(t);let r=await qa(t,n.dir),{entrypoint:o,files:s}=await Ka(t,r,n.entrypoint),a=hr({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 br(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await jt(e,t,n),i=r.functions.find(u=>u.name===t);g(r,()=>{let u=Math.max(1,Math.round(a/1024));l(`${E(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${u} KB)`),i&&l(` ${i.url}`),n.verifyJwt||(l(""),l(E(" 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 Ct(e){return w("cloud-project-functions",{ref:e})}function Mt(e,t){return Ot(t),w("cloud-project-functions",{ref:e,action:"delete",name:t})}async function Sr(e){let t=await Ct(e);g(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":E("open"),n.bytes===null?"-":`${Math.max(1,Math.round(n.bytes/1024))} KB`,n.url])])}
215
- `),t.limit.maxFunctions!==null&&l(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`))})}async function xr(e,t){let n=await Mt(e,t);g(n,()=>{l(`${t} is gone. It stops answering within a few seconds.`)})}M();z();U();function Ja(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function Ga(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function kr(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 Ga()})}else for(let s of t){let a=Ja(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=Nt(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});g(o,()=>{for(let s of r)l(`${E(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 vr(e,t){let n=Nt(t);if(n)throw new y(n);let r=await w("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{l(`${t} is gone.`)})}async function _r(e){let t=await w("cloud-project-secrets",{ref:e});g(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`,Z(n.updatedAt)])])}
216
- `),l(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as Ft}from"node:child_process";import{appendFileSync as za,existsSync as $r,readFileSync as Ya,writeFileSync as Ut}from"node:fs";import{join as Xa}from"node:path";import{open as Ar}from"node:fs/promises";function Er(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 Tr(e){let t=e.split(`
217
- `).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();we();M();U();async function Rr(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 Bt(e){await Rr(e);let t=await W(e);g(t,()=>{process.stdout.write(`${t.uri}
218
- `)})}async function Or(e,t){if(t.length===0&&!ne()){let o=ye();P("usage",`db psql with no statements opens an interactive session, and ${o?he(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Rr(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=Ft("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 Nr(e,t,n="DATABASE_URL"){let r=Xa(e,".env"),o=`${n}=${t}`;if(!$r(r))return Ut(r,`${o}
219
- `),{path:r,replaced:!1};let s=Ya(r,"utf8"),a=new RegExp(`^${n}=.*$`,"m");return a.test(s)?(Ut(r,s.replace(a,o)),{path:r,replaced:!0}):(za(r,s.endsWith(`
286
+ ${r}`}async function cl(e,t,n={}){W(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,Ho),_n(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function ll(e,t,n){W(t),await e.remove(t),n&&await e.removeData(t)}async function _n(e,t,n={}){W(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??ht;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?xs(t):null}}var kn,L,ye,ue,Gc,ys,H,Xc,$e,Qc,bs,Es=A(()=>{"use strict";qo();pt();Wo();un();ps();ce();kn=process.env.SNOUTPOD_ENGINE??"podman";L=Jc(),ye=process.env.SNOUTPOD_IMAGE??"snoutpod/postgres:17",ue=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",Gc=54322,ys=512,H=Ko();Xc="cd packages/snoutpod && npm run image:build",$e="ghcr.io/snoutdata/snoutpod-postgres:17";Qc=20*60*1e3;bs="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});V();var St=[{name:"init",summary:"Create or link a project and write a DATABASE_URL",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to",flags:[]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database",flags:["--name","--region","--team","--no-wait","--timeout"]},{name:"projects pause",summary:"Stop a project",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, the data API and push: on or off",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions size",summary:"Its memory and workers, within the plan",flags:["--ref","--memory","--concurrency","--reset"]},{name:"functions delete",summary:"Remove one",flags:["--ref"]},{name:"secrets set",summary:"Set the environment functions run with",flags:["--ref","--stdin"]},{name:"secrets list",summary:"The names, never the values",flags:["--ref"]},{name:"secrets unset",summary:"Remove one",flags:["--ref"]},{name:"keys",summary:"The anon and service_role API keys for a project",flags:["--ref"]},{name:"keys rotate",summary:"New keys, breaking every one already issued",flags:["--ref","--force"]},{name:"teams",summary:"Teams this account belongs to",flags:[]},{name:"link",summary:"Point this folder at a project",flags:["--ref"]},{name:"usage",summary:"Size and compute against the plan limit",flags:["--ref","--days","--history"]},{name:"start",summary:"A Postgres on this machine, with your migrations applied",flags:["--port","--dir","--no-migrations","--out-of-order"]},{name:"stop",summary:"Stop the local database. The data stays",flags:[]},{name:"status",summary:"What the local database is doing",flags:[]},{name:"gen types typescript",summary:"The schema as a TypeScript Database type, on stdout",flags:["--ref","--db-url","--local","--schema","--out"]},{name:"db url",summary:"A connection string",flags:["--ref"]},{name:"db psql",summary:"Open psql, or run statements after --",flags:["--ref"]},{name:"db reset-password",summary:"Rotate the project password",flags:["--ref"]},{name:"db export",summary:"Take or download a dump",flags:["--ref","--out","--status","--timeout"]},{name:"db push",summary:"Apply migrations from a folder",flags:["--dir","--dry-run","--out-of-order"]},{name:"db restore",summary:"Put a dump into a project, or rewind it to a moment into a new project",flags:["--file","--force","--at","--name","--window","--ref"]},{name:"mcp",summary:"Serve these operations as tools over stdio",flags:["--allow-delete"]}];j();se();xe();we();Pt();Ye();F();Rt();j();j();we();F();async function cr(){let{projects:e,allowance:t}=await J();g({projects:e,allowance:t},()=>{if(e.length===0){l('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${I([["REF","NAME","STATE","REGION","LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?v("read-only"):n.state==="ready"?n.state:v(n.state),n.region,Z(n.lastConnectionAt)])])}
287
+ `)})}async function Nt(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 Oe(n,{timeoutMs:e.timeoutMs,onTick:o=>l(b(` ${o}`))})),e.link&&ze(e.link,{ref:n,name:r.name}),g({project:r,password:t.password,uri:`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`},()=>{process.stdout.write(`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require
288
+ `),l(""),l(`Ready. ${v(n)} in ${r.region}.`),l("The password is stored for you: `snoutdata db url` prints this again.")})}async function lr(e,t){let n=`cloud-project-${e}`,r=await w(n,{ref:t});g(r,()=>{l(r.changed?`${t}: ${e}d.`:`${t} was already ${e==="delete"?"deleted":`${e}d`}.`)})}async function ur(e){let t=await w("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
289
+ `),l(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function dr(){let{teams:e}=await rt();g({teams:e},()=>{if(e.length===0){l("You are not on any teams.");return}process.stdout.write(`${I([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
290
+ `)})}async function mr(e){let{teams:t}=await rt(),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}j();F();async function fr(e){let t=await w("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
291
+ `),l(""),l(`${v(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),l(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),l("This is the only time it is shown: only its hash is stored."),l(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function pr(){let{tokens:e}=await w("cloud-token-list");g({tokens:e},()=>{if(e.length===0){l("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${I([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?v(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",Z(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
292
+ `)})}async function gr(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await w("cloud-token-revoke",t);g(n,()=>{l(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}j();V();F();async function hr(e){let t=await z(e);g({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){l(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${wr(t.anonKey,t.serviceRoleKey)}
293
+ `),ja(t.keysIssuedAt)})}async function yr(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});g(n,()=>{process.stdout.write(`${wr(n.anonKey,n.serviceRoleKey)}
294
+ `),l(""),l(n.breaks),l(b(" snoutdata keys --json to read them again later"))})}function wr(e,t){return I([["KEY","VALUE"],["anon",e],["service_role",t]])}function ja(e){l(""),l(`${v("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),l(`${v("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&l(b(` issued ${e.slice(0,10)}`))}j();V();F();import{readdir as Wa,readFile as Ja,stat as Ga}from"node:fs/promises";import{join as Ct,relative as Va,sep as Ya}from"node:path";var Ua=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function Fa(e){return Ua.test(e)}function ot(e){if(!Fa(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 Ba=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function Ha(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!==".."&&Ba.test(n))}var Dt=2*1024*1024,br=200;function Sr(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>br)throw new Error(`a function may hold ${br} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!Ha(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=qa(o);if(s.length>Dt)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${Dt/1024} KB. A function is source code; put large files in Storage.`);return o}function qa(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 Ka=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/;var za=["SNOUTDATA_","SNOUT_"];function Mt(e){if(!Ka.test(e))return`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`;let t=za.find(n=>e.startsWith(n));return t?`names beginning with ${t} are set by the platform, so ${e} cannot be one of yours.`:null}var Mu=64*1024;var xr=["functions"];async function It(e){try{return(await Ga(e)).isDirectory()}catch{return!1}}async function Xa(e,t){if(t){if(!await It(t))throw new y(`${t} is not a directory`);return t}for(let n of xr){let r=Ct(n,e);if(await It(r))return r}throw new y(`no folder found for ${e} at ${xr[0]}/${e}; use --dir to say where it is.`)}async function kr(e){let t=[];async function n(r){for(let o of await Wa(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=Ct(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await Ja(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:Va(e,s).split(Ya).join("/"),text:i})}}return await n(e),t}async function Za(e,t,n){let r=await kr(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=Ct(t,"..","_shared");if(!await It(s))return{entrypoint:o,files:r};let a=(await kr(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 jt(e,t,n){ot(t);let r=await Xa(t,n.dir),{entrypoint:o,files:s}=await Za(t,r,n.entrypoint),a=Sr({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 _r(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await jt(e,t,n),i=r.functions.find(u=>u.name===t);g(r,()=>{let u=Math.max(1,Math.round(a/1024));l(`${v(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${u} KB)`),i&&l(` ${i.url}`),n.verifyJwt||(l(""),l(v(" 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 Lt(e){return w("cloud-project-functions",{ref:e})}function Ut(e,t){return ot(t),w("cloud-project-functions",{ref:e,action:"delete",name:t})}function Ft(e,t,n){if(ot(t),!n.reset&&n.memoryMb===void 0&&n.concurrency===void 0)throw new y("functions size needs --memory MB, --concurrency N, or --reset");return w("cloud-project-functions",{ref:e,action:"size",name:t,...n.reset?{reset:!0}:{memoryMb:n.memoryMb??null,concurrency:n.concurrency??null}})}function Qa(e,t){if(e.memoryMb==null||e.concurrency==null)return null;let n=e.memoryMb*e.concurrency,r=t.podMemoryMb?` of ${t.podMemoryMb} MB`:"";return`${e.memoryMb} MB x ${e.concurrency} worker${e.concurrency===1?"":"s"} = ${n} MB${r}`}function vr(e){return e.functionMemoryMb==null||e.concurrencyMax==null||e.podMemoryMb==null?null:`${e.tier??"this"} plan: up to ${e.functionMemoryMb} MB a worker and ${e.concurrencyMax} workers a function, memory x workers up to ${e.podMemoryMb} MB`}async function Er(e,t,n){let r=await Ft(e,t,n),o=r.functions.find(s=>s.name===t);g(r,()=>{let s=o?Qa(o,r.limit):null;l(`${v(t)} ${n.reset?"is back to the plan default":"is resized"}${s?`: ${s}`:""}`),l(b(" The next request runs at it; workers already running finish what they hold."));let a=vr(r.limit);a&&l(b(` ${a}`))})}async function Pr(e){let t=await Lt(e);g(t,()=>{if(t.functions.length===0){l(`${e} has no functions.`),l(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${I([["NAME","KEY","SIZE","MEMORY","WORKERS","URL"],...t.functions.map(r=>[r.name,r.verifyJwt?"required":v("open"),r.bytes===null?"-":`${Math.max(1,Math.round(r.bytes/1024))} KB`,r.memoryMb==null?"-":`${r.memoryMb} MB`,r.concurrency==null?"-":String(r.concurrency),r.url])])}
295
+ `),t.limit.maxFunctions!==null&&l(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`));let n=vr(t.limit);n&&l(b(`${n}. Change one with: snoutdata functions size <name> --memory MB --concurrency N`))})}async function Tr(e,t){let n=await Ut(e,t);g(n,()=>{l(`${t} is gone. It stops answering within a few seconds.`)})}j();V();F();function ti(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function ni(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function $r(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 ni()})}else for(let s of t){let a=ti(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=Mt(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});g(o,()=>{for(let s of r)l(`${v(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 Ar(e,t){let n=Mt(t);if(n)throw new y(n);let r=await w("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{l(`${t} is gone.`)})}async function Rr(e){let t=await w("cloud-project-secrets",{ref:e});g(t,()=>{if(t.secrets.length===0){l(`${e} has no function secrets.`),l(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${I([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,Z(n.updatedAt)])])}
296
+ `),l(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as qt}from"node:child_process";import{appendFileSync as oi,existsSync as Mr,readFileSync as si,writeFileSync as Ht}from"node:fs";import{join as ai}from"node:path";import{open as Ir}from"node:fs/promises";function Or(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 Nr(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 Dr(e){let t=e.split(`
297
+ `).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}}j();se();xe();j();F();async function Cr(e){try{let{projects:t}=await J(),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 Kt(e){await Cr(e);let t=await z(e);g(t,()=>{process.stdout.write(`${t.uri}
298
+ `)})}async function jr(e,t){if(t.length===0&&!re()){let o=Se();T("usage",`db psql with no statements opens an interactive session, and ${o?be(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Cr(e);let n=await z(e);n.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let r=qt("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 Lr(e,t,n="DATABASE_URL"){let r=ai(e,".env"),o=`${n}=${t}`;if(!Mr(r))return Ht(r,`${o}
299
+ `),{path:r,replaced:!1};let s=si(r,"utf8"),a=new RegExp(`^${n}=.*$`,"m");return a.test(s)?(Ht(r,s.replace(a,o)),{path:r,replaced:!0}):(oi(r,s.endsWith(`
220
300
  `)?`${o}
221
301
  `:`
222
302
  ${o}
223
- `),{path:r,replaced:!1})}async function Dr(e,t={}){if(t.statusOnly){let{export:a}=await xe(e);return g({ref:e,export:a},()=>Lt(a)),0}let n=await $e(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>Lt(n.export)),0;l("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await Qa(e,t.timeoutMs);if(r.error&&P("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>Lt(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 ei(r.url,t.out),s=r.rolesSql?Za(t.out):null;return s&&r.rolesSql&&Ut(s,r.rolesSql.endsWith(`
303
+ `),{path:r,replaced:!1})}async function Ur(e,t={}){if(t.statusOnly){let{export:a}=await ve(e);return g({ref:e,export:a},()=>Bt(a)),0}let n=await Re(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>Bt(n.export)),0;l("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await ci(e,t.timeoutMs);if(r.error&&T("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>Bt(r)),0;r.url||T("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await li(r.url,t.out),s=r.rolesSql?ii(t.out):null;return s&&r.rolesSql&&Ht(s,r.rolesSql.endsWith(`
224
304
  `)?r.rolesSql:`${r.rolesSql}
225
- `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{l(`Saved ${ee(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 Za(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function Qa(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await xe(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 ei(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 Ar(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 Lt(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 ${Z(e.completedAt)}, ${ee(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
226
- `),e.urlExpiresAt&&l(b(`That link stops working ${Z(e.urlExpiresAt)}.`))):l(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function ee(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 Ir(e,t){let n=t.file;n||P("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),$r(n)||P("usage",`no file at ${n}`,{file:n});let r=await Ar(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=Er(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:u}=await G(),d=u.find(k=>k.ref===e),m=await ti(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=Pr(a,{existingTables:m,readOnly:d?.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 p=await ni(i,c.tool,n),f=Tr(p.stderr);return p.code!==0&&f.fatal.length===0&&f.total>0?(g({ref:e,file:n,ok:!0,tool:c.tool,format:c.format,ignoredErrors:f.harmless},()=>{l(`Restored into ${e}.`),l(`${f.harmless} error${f.harmless===1?"":"s"} above ${f.harmless===1?"is":"are"} expected and nothing is missing: a dump carries comments on the extensions this image installs, and your role does not own them.`)}),0):(p.code!==0&&P("failed",`${c.tool} exited ${p.code}. Nothing here retried it: a half-finished restore is undone by deleting the project and making another.`,{ref:e,file:n,errors:f.fatal}),g({ref:e,file:n,ok:!0,tool:c.tool,format:c.format},()=>l(`Restored into ${e}.`)),0)}async function ti(e){return new Promise(t=>{let n=Ft("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 ni(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=Ft(t,o,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),i="";a.stderr?.on("data",u=>{let d=String(u);i+=d,process.stderr.write(d)}),a.on("error",u=>{K(u.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(u)),s({code:127,stderr:i})}),a.on("close",u=>s({code:u??0,stderr:i}))})}M();z();U();var nt=["auth","storage","data-api","push"];function me(e){return{error:e instanceof Error?e.message:String(e)}}async function rt(e){let[t,n,r,o]=await Promise.all([w("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),me),w("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),me),w("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),me),w("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),me)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o}}function jr(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!nt.includes(t))throw new y(`say which product: ${nt.join(", ")}`);return t}async function Ht(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 t==="push"?(await w("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await 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 Cr(e){let t=await rt(e);g(t,()=>{let n=(s,a,i="")=>[s,a&&typeof a=="object"&&"error"in a?`could not read: ${a.error}`:a.enabled?E("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":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":"")])}
227
- `),l(b(` snoutdata products enable storage --ref ${e}`))})}async function Mr(e,t,n){let r=await Ht(e,t,n);g(r,()=>l(r.note))}async function ot(e){let t=await w("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function qt(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 oi(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)}
228
- `)}async function Lr(e,t,n){if(t===void 0||t==="list"){let o=await ot(e);g(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":E("no"),s.certificate?.state??""])])}
229
- `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await qt(e,t,n??"");g(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||oi(r.domain,e)),r.next&&l(r.next)})}async function Kt(e){return w("cloud-project-restore",{ref:e},"GET")}async function Wt(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 Ur(e){let t=await Kt(e);g(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 Fr(e,t,n){let r=await Wt(e,t,n);g(r,()=>{l(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${E(r.project.name)} (${r.project.ref}).`),l(b(" snoutdata projects list to see when it is ready"))})}async function Jt(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([rt(e),w("cloud-project-functions",{ref:e}).then(i=>(i.functions??[]).map(u=>u.name),me),w("cloud-project-secrets",{ref:e}).then(i=>(i.secrets??[]).map(u=>u.name),me),ot(e).then(i=>i.domains.map(u=>({hostname:u.hostname,verified:u.verified})),me)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Br(e){let t=await Jt(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${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)]])}
230
- `)})}M();z();U();import{readFile as si}from"node:fs/promises";var ai="api.snoutdata.com";function Wr(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}.${ai}/auth/v1/callback`}}async function ii(e){return Wr(e,await w("cloud-project-auth",{ref:e,action:"status"}))}async function Gt(e,t){return Wr(e,await w("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function Vt(e){let t=e.google,n="off";t.ready?n=E("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?E("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]])}
231
- `),e.enabled||l(b(` snoutdata products enable auth --ref ${e.ref}`))}async function ci(){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 Jr(e,t,n,r){if(t===void 0||t==="show"){let o=await ii(e);g(o,()=>Vt(o));return}if(t==="google"){if(n[0]==="off"){let a=await Gt(e,{googleEnabled:!1});g(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 ci();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await Gt(e,o);g(s,()=>{s.google.ready?l("Google sign-in is on. Your auth service restarts within about a minute."):Vt(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 Gt(e,o);g(s,()=>Vt(s));return}if(t==="templates"){let o=await w("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>Kr(o.templates));return}if(t==="template"){let o=n[0];if(!o||!qr.includes(o))throw new y(`auth template needs a kind: ${qr.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 si(r.file,"utf8"),u=await w("cloud-project-auth",{ref:e,action:"templates"}),d=r.subject??u.templates.find(m=>m.kind===o)?.subject;s={kind:o,subject:d,body:i}}let a=await w("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{Kr(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 qr=["confirmation","recovery","magic_link","invite","email_change"];function Kr(e){process.stdout.write(`${j([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?E("yours"):"ours",t.subject])])}
232
- `)}M();U();oe();import{spawn as ui}from"node:child_process";import{createHash as di}from"node:crypto";import{existsSync as mi,readdirSync as pi,readFileSync as fi,statSync as gi}from"node:fs";import{isAbsolute as hi,join as zt,relative as yi,resolve as wi}from"node:path";function li(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function st(e,t,n={}){let r=li(e),o=new Map(r.map(d=>[d.name,d])),s=[];for(let d of t){let m=o.get(d.name);if(!m){s.push({code:"missing",name:d.name,reason:`${d.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}m.checksum!==d.checksum&&s.push({code:"changed",name:d.name,reason:`${d.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(d=>d.name).sort((d,m)=>d<m?-1:d>m?1:0)[t.length-1],i=new Set(t.map(d=>d.name)),u=[];for(let d of r)if(!i.has(d.name)){if(a!==void 0&&d.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:d.name,reason:`${d.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}u.push(d)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:u}}function Re(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function Vr(e){let t=yi(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var Oe="_snoutdata_migrations",bi=`create table if not exists ${Oe} (
305
+ `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{l(`Saved ${ee(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 ii(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function ci(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await ve(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 li(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 Ir(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 Bt(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 ${Z(e.completedAt)}, ${ee(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
306
+ `),e.urlExpiresAt&&l(b(`That link stops working ${Z(e.urlExpiresAt)}.`))):l(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function ee(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 Fr(e,t){let n=t.file;n||T("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),Mr(n)||T("usage",`no file at ${n}`,{file:n});let r=await Ir(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=Or(o.slice(0,s.bytesRead)),i=await z(e);i.wakesInstantly||l("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:u}=await J(),d=u.find(x=>x.ref===e),m=await ui(i);m===null&&T("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=Nr(a,{existingTables:m,readOnly:d?.readOnly===!0},{force:t.force});c.ok||T("conflict",c.reason,{ref:e,file:n,refusal:c.code}),l(`Restoring ${n} into ${e} with ${c.tool} (${c.format} format).`);let f=await di(i,c.tool,n),p=Dr(f.stderr);return f.code!==0&&p.fatal.length===0&&p.total>0?(g({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&&T("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}),g({ref:e,file:n,ok:!0,tool:c.tool,format:c.format},()=>l(`Restored into ${e}.`)),0)}async function ui(e){return new Promise(t=>{let n=qt("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 di(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=qt(t,o,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:"require"}}),i="";a.stderr?.on("data",u=>{let d=String(u);i+=d,process.stderr.write(d)}),a.on("error",u=>{K(u.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(u)),s({code:127,stderr:i})}),a.on("close",u=>s({code:u??0,stderr:i}))})}j();V();F();var st=["auth","storage","data-api","push"];function ge(e){return{error:e instanceof Error?e.message:String(e)}}async function at(e){let[t,n,r,o]=await Promise.all([w("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),ge),w("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),ge),w("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),ge),w("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),ge)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o}}function Br(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!st.includes(t))throw new y(`say which product: ${st.join(", ")}`);return t}async function zt(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 t==="push"?(await w("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await 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 Hr(e){let t=await at(e);g(t,()=>{let n=(s,a,i="")=>[s,a&&typeof a=="object"&&"error"in a?`could not read: ${a.error}`:a.enabled?v("on"):"off",i],r=t.storage,o=t.dataApi;process.stdout.write(`${I([["PRODUCT","STATE",""],n("auth",t.auth),n("storage",r,"pending"in r&&r.pending?"waiting for the host":""),n("data-api",o,"allowedOnPlan"in o&&!o.allowedOnPlan?"paid plans only":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":"")])}
307
+ `),l(b(` snoutdata products enable storage --ref ${e}`))})}async function qr(e,t,n){let r=await zt(e,t,n);g(r,()=>l(r.note))}async function it(e){let t=await w("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function Wt(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 fi(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(`${I(n)}
308
+ `)}async function Kr(e,t,n){if(t===void 0||t==="list"){let o=await it(e);g(o,()=>{if(o.domains.length===0){l(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${I([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":v("no"),s.certificate?.state??""])])}
309
+ `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await Wt(e,t,n??"");g(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||fi(r.domain,e)),r.next&&l(r.next)})}async function Jt(e){return w("cloud-project-restore",{ref:e},"GET")}async function Gt(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 zr(e){let t=await Jt(e);g(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 Wr(e,t,n){let r=await Gt(e,t,n);g(r,()=>{l(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${v(r.project.name)} (${r.project.ref}).`),l(b(" snoutdata projects list to see when it is ready"))})}async function Vt(e){let{projects:t}=await J(),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([at(e),w("cloud-project-functions",{ref:e}).then(i=>(i.functions??[]).map(u=>u.name),ge),w("cloud-project-secrets",{ref:e}).then(i=>(i.secrets??[]).map(u=>u.name),ge),it(e).then(i=>i.domains.map(u=>({hostname:u.hostname,verified:u.verified})),ge)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Jr(e){let t=await Vt(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${I([["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)]])}
310
+ `)})}j();V();F();import{readFile as pi}from"node:fs/promises";var gi="api.snoutdata.com";function Xr(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}.${gi}/auth/v1/callback`}}async function hi(e){return Xr(e,await w("cloud-project-auth",{ref:e,action:"status"}))}async function Yt(e,t){return Xr(e,await w("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function Xt(e){let t=e.google,n="off";t.ready?n=v("on"):t.enabled&&(n=`switched on, not offered yet: needs ${[!t.clientId&&"a client id",!t.clientSecretSet&&"a client secret"].filter(Boolean).join(" and ")}`),process.stdout.write(`${I([["auth",e.enabled?v("on"):"off"],["site url",e.siteUrl??b("not set")],["redirects",e.redirects.length?e.redirects.join(", "):b("none")],["google",n],["google client id",t.clientId??b("not set")],["google callback",e.googleCallback]])}
311
+ `),e.enabled||l(b(` snoutdata products enable auth --ref ${e.ref}`))}async function yi(){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 Zr(e,t,n,r){if(t===void 0||t==="show"){let o=await hi(e);g(o,()=>Xt(o));return}if(t==="google"){if(n[0]==="off"){let a=await Yt(e,{googleEnabled:!1});g(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 yi();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await Yt(e,o);g(s,()=>{s.google.ready?l("Google sign-in is on. Your auth service restarts within about a minute."):Xt(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 Yt(e,o);g(s,()=>Xt(s));return}if(t==="templates"){let o=await w("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>Yr(o.templates));return}if(t==="template"){let o=n[0];if(!o||!Vr.includes(o))throw new y(`auth template needs a kind: ${Vr.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 pi(r.file,"utf8"),u=await w("cloud-project-auth",{ref:e,action:"templates"}),d=r.subject??u.templates.find(m=>m.kind===o)?.subject;s={kind:o,subject:d,body:i}}let a=await w("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{Yr(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 Vr=["confirmation","recovery","magic_link","invite","email_change"];function Yr(e){process.stdout.write(`${I([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?v("yours"):"ours",t.subject])])}
312
+ `)}j();F();se();import{spawn as bi}from"node:child_process";import{createHash as Si}from"node:crypto";import{existsSync as xi,readdirSync as ki,readFileSync as _i,statSync as vi}from"node:fs";import{isAbsolute as Ei,join as Zt,relative as Pi,resolve as Ti}from"node:path";function wi(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function ct(e,t,n={}){let r=wi(e),o=new Map(r.map(d=>[d.name,d])),s=[];for(let d of t){let m=o.get(d.name);if(!m){s.push({code:"missing",name:d.name,reason:`${d.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}m.checksum!==d.checksum&&s.push({code:"changed",name:d.name,reason:`${d.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(d=>d.name).sort((d,m)=>d<m?-1:d>m?1:0)[t.length-1],i=new Set(t.map(d=>d.name)),u=[];for(let d of r)if(!i.has(d.name)){if(a!==void 0&&d.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:d.name,reason:`${d.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}u.push(d)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:u}}function Ne(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function eo(e){let t=Pi(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var De="_snoutdata_migrations",$i=`create table if not exists ${De} (
233
313
  name text primary key,
234
314
  checksum text not null,
235
315
  applied_at timestamptz not null default now()
236
- );`;function Si(e){if(!mi(e)||!gi(e).isDirectory())throw new Error(`no migrations folder at ${Vr(e)}. Put your .sql files there, or pass --dir.`);return pi(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=zt(e,t),r=fi(n,"utf8");return{name:t,path:n,sql:r,checksum:di("sha256").update(r,"utf8").digest("hex")}})}function Yt(e,t,n=[]){return new Promise(r=>{let o=ui("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 xi(e){let t=await Yt(e,bi);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${Oe}`);let n=await Yt(e,`select name || E'\\t' || checksum from ${Oe};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${Oe}`);return n.out.split(`
237
- `).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 Gr(e){return`'${e.replace(/'/g,"''")}'`}async function zr(e,t={}){let n=await Xt(e,t,r=>l(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
238
- `);P("conflict",`Refused. This folder and ${e} do not agree:
239
- ${r}`,{ref:e,problems:n.problems})}P("failed",`${n.failed} failed, and nothing after it was tried.
240
- ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(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 Xt(e,t={},n=()=>{}){let r=wi(t.dir===void 0?zt(process.cwd(),"migrations"):hi(t.dir)?t.dir:zt(process.cwd(),t.dir)),o=[];function s(f){o.push(f),n(f)}let a=Si(r);if(a.length===0)return s(`No .sql files in ${Vr(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 u=await xi(i),d=st(a,u,{outOfOrder:t.outOfOrder});if(!d.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...d.problems],notes:o};let m=new Map(a.map(f=>[f.name,f])),c=d.pending.map(f=>m.get(f.name));if(c.length===0)return s(`${e} is up to date: ${u.length} migration${u.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let f of c)Re(f.sql)||s(`${f.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:c.map(f=>f.name),notes:o}}let p=[];for(let f of c){let k=`insert into ${Oe} (name, checksum) values (${Gr(f.name)}, ${Gr(f.checksum)});`,S=Re(f.sql);S||s(`${f.name} runs outside a transaction, so an interruption can leave it half applied.`);let h=S?`begin;
241
- ${f.sql}
242
- ${k}
316
+ );`;function Ai(e){if(!xi(e)||!vi(e).isDirectory())throw new Error(`no migrations folder at ${eo(e)}. Put your .sql files there, or pass --dir.`);return ki(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=Zt(e,t),r=_i(n,"utf8");return{name:t,path:n,sql:r,checksum:Si("sha256").update(r,"utf8").digest("hex")}})}function Qt(e,t,n=[]){return new Promise(r=>{let o=bi("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 Ri(e){let t=await Qt(e,$i);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${De}`);let n=await Qt(e,`select name || E'\\t' || checksum from ${De};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${De}`);return n.out.split(`
317
+ `).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 Qr(e){return`'${e.replace(/'/g,"''")}'`}async function to(e,t={}){let n=await en(e,t,r=>l(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
318
+ `);T("conflict",`Refused. This folder and ${e} do not agree:
319
+ ${r}`,{ref:e,problems:n.problems})}T("failed",`${n.failed} failed, and nothing after it was tried.
320
+ ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(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 en(e,t={},n=()=>{}){let r=Ti(t.dir===void 0?Zt(process.cwd(),"migrations"):Ei(t.dir)?t.dir:Zt(process.cwd(),t.dir)),o=[];function s(p){o.push(p),n(p)}let a=Ai(r);if(a.length===0)return s(`No .sql files in ${eo(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let i=await z(e);i.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let u=await Ri(i),d=ct(a,u,{outOfOrder:t.outOfOrder});if(!d.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...d.problems],notes:o};let m=new Map(a.map(p=>[p.name,p])),c=d.pending.map(p=>m.get(p.name));if(c.length===0)return s(`${e} is up to date: ${u.length} migration${u.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let p of c)Ne(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 x=`insert into ${De} (name, checksum) values (${Qr(p.name)}, ${Qr(p.checksum)});`,S=Ne(p.sql);S||s(`${p.name} runs outside a transaction, so an interruption can leave it half applied.`);let h=S?`begin;
321
+ ${p.sql}
322
+ ${x}
243
323
  commit;
244
- `:`${f.sql}
245
- ${k}
246
- `,$=await Yt(i,h);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:p,failed:f.name,error:$.err.trim(),notes:o};p.push(f.name),s(` applied ${f.name}`)}return{ref:e,directory:r,ok:!0,applied:p,notes:o}}M();z();U();import{readFile as Yr}from"node:fs/promises";function Zt(e){if(e==="apns"||e==="fcm")return e;throw new y("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function ki(e){let t=await w("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function Xr(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new y("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new y("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await Yr(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function Zr(e){if(!e)throw new y("push credentials set fcm needs --file service-account.json");return{service_account:await Yr(e,"utf8")}}async function vi(e,t,n){let r=await w("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function _i(e,t){return await w("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function Qr(e){let t=await ki(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${j([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
247
- `),(!n||!r)&&l(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function eo(e,t,n){let r=await vi(e,t,n);g(r,()=>l(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function to(e,t){let n=await _i(e,t);g(n,()=>l(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}M();oe();U();import{spawn as Li}from"node:child_process";import{writeFileSync as Ui}from"node:fs";import{isAbsolute as Fi,resolve as Bi}from"node:path";var Pi=" ";function x(e){return Pi.repeat(e)}function B(e){return JSON.stringify(e)}function te(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:B(e)}function pe(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function Ti(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 $i={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"},Ai={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function Ri(e,t){let n=Ai[e]?.[t];return n||($i[t]??"unknown")}function at(e,t){let{base:n,array:r}=Ti(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=Ri(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function Oi(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function Qt(e,t,n,r){let o=at(e,t.dataType),s=t.nullable?`${o} | null`:o;return`${x(n)}${te(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 Di(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: ${Ni(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 no(e,t,n){let r=pe(t.columns),o=[];o.push(`${x(n)}${te(t.name)}: {`),o.push(`${x(n+1)}Row: {`);for(let s of r)o.push(Qt(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(Qt(e,s,n+2,Oi(s)));o.push(`${x(n+1)}}`),o.push(`${x(n+1)}Update: {`);for(let s of r)o.push(Qt(e,s,n+2,!0));o.push(`${x(n+1)}}`)}return o.push(...Di(t,n+1)),o.push(`${x(n)}}`),o}function Ne(e,t){return[`${x(t)}${e}: {`,`${x(t+1)}[_ in never]: never`,`${x(t)}}`]}function Ii(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}[]`:`${at(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??at(e,n)}function ji(e,t,n){let r=[],o=pe((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${x(n)}${te(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)}${te(s.name)}: ${at(e,s.dataType)}`);r.push(`${x(n+1)}}`)}return r.push(`${x(n+1)}Returns: ${Ii(e,t)}`),r.push(`${x(n)}}`),r}function ke(e,t){return e.schema&&e.schema.length>0?e.schema:t}function ro(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let c of o){let p=c.schema&&c.schema.length>0?c.schema:n;s.set(c.name.toLowerCase(),`Database[${B(p)}][${B("Enums")}][${B(c.name)}]`)}let a=new Map;for(let c of e.tables){let p=ke(c,n),f=c.kind==="view"?"Views":"Tables";a.set(c.name.toLowerCase(),`Database[${B(p)}][${B(f)}][${B(c.name)}][${B("Row")}]`)}let i={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},u=new Set([n]);for(let c of e.tables)u.add(ke(c,n));for(let c of e.routines??[])u.add(ke(c,n));for(let c of o)u.add(c.schema&&c.schema.length>0?c.schema:n);let d=[...u].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 d){let p=pe(e.tables.filter(h=>ke(h,n)===c&&h.kind!=="view")),f=pe(e.tables.filter(h=>ke(h,n)===c&&h.kind==="view")),k=pe((e.routines??[]).filter(h=>ke(h,n)===c)),S=pe(o.filter(h=>(h.schema&&h.schema.length>0?h.schema:n)===c));if(m.push(`${x(1)}${te(c)}: {`),p.length===0)m.push(...Ne("Tables",2));else{m.push(`${x(2)}Tables: {`);for(let h of p)m.push(...no(i,h,3));m.push(`${x(2)}}`)}if(f.length===0)m.push(...Ne("Views",2));else{m.push(`${x(2)}Views: {`);for(let h of f)m.push(...no(i,h,3));m.push(`${x(2)}}`)}if(k.length===0)m.push(...Ne("Functions",2));else{m.push(`${x(2)}Functions: {`);for(let h of k)m.push(...ji(i,h,3));m.push(`${x(2)}}`)}if(S.length===0)m.push(...Ne("Enums",2));else{m.push(`${x(2)}Enums: {`);for(let h of S)m.push(`${x(3)}${te(h.name)}: ${h.values.map(B).join(" | ")}`);m.push(`${x(2)}}`)}m.push(...Ne("CompositeTypes",2)),m.push(`${x(1)}}`)}return m.push("}"),t.helpers!==!1&&(m.push(""),m.push(Mi(n))),t.constants!==!1&&(m.push(""),m.push(Ci(d,o,n))),`${m.join(`
324
+ `:`${p.sql}
325
+ ${x}
326
+ `,$=await Qt(i,h);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}}j();V();F();import{readFile as no}from"node:fs/promises";function tn(e){if(e==="apns"||e==="fcm")return e;throw new y("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function Oi(e){let t=await w("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function ro(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new y("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new y("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await no(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function oo(e){if(!e)throw new y("push credentials set fcm needs --file service-account.json");return{service_account:await no(e,"utf8")}}async function Ni(e,t,n){let r=await w("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function Di(e,t){return await w("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function so(e){let t=await Oi(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${I([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
327
+ `),(!n||!r)&&l(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function ao(e,t,n){let r=await Ni(e,t,n);g(r,()=>l(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function io(e,t){let n=await Di(e,t);g(n,()=>l(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}j();se();F();import{spawn as Ji}from"node:child_process";import{writeFileSync as Gi}from"node:fs";import{isAbsolute as Vi,resolve as Yi}from"node:path";var Ii=" ";function k(e){return Ii.repeat(e)}function B(e){return JSON.stringify(e)}function te(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:B(e)}function he(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function Ci(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 ji={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"},Li={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function Ui(e,t){let n=Li[e]?.[t];return n||(ji[t]??"unknown")}function lt(e,t){let{base:n,array:r}=Ci(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=Ui(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function Fi(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function nn(e,t,n,r){let o=lt(e,t.dataType),s=t.nullable?`${o} | null`:o;return`${k(n)}${te(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 Hi(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(`${k(t)}Relationships: []`),n;n.push(`${k(t)}Relationships: [`);for(let o of r)n.push(`${k(t+1)}{`),n.push(`${k(t+2)}foreignKeyName: ${B(o.constraintName)}`),n.push(`${k(t+2)}columns: [${o.columns.map(B).join(", ")}]`),n.push(`${k(t+2)}isOneToOne: ${Bi(e,o)?"true":"false"}`),n.push(`${k(t+2)}referencedRelation: ${B(o.referencedTable)}`),n.push(`${k(t+2)}referencedColumns: [${o.referencedColumns.map(B).join(", ")}]`),n.push(`${k(t+1)}},`);return n.push(`${k(t)}]`),n}function co(e,t,n){let r=he(t.columns),o=[];o.push(`${k(n)}${te(t.name)}: {`),o.push(`${k(n+1)}Row: {`);for(let s of r)o.push(nn(e,s,n+2,!1));if(o.push(`${k(n+1)}}`),t.kind!=="view"){o.push(`${k(n+1)}Insert: {`);for(let s of r)o.push(nn(e,s,n+2,Fi(s)));o.push(`${k(n+1)}}`),o.push(`${k(n+1)}Update: {`);for(let s of r)o.push(nn(e,s,n+2,!0));o.push(`${k(n+1)}}`)}return o.push(...Hi(t,n+1)),o.push(`${k(n)}}`),o}function Me(e,t){return[`${k(t)}${e}: {`,`${k(t+1)}[_ in never]: never`,`${k(t)}}`]}function qi(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}[]`:`${lt(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??lt(e,n)}function Ki(e,t,n){let r=[],o=he((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${k(n)}${te(t.name)}: {`),o.length===0)r.push(`${k(n+1)}Args: never`);else{r.push(`${k(n+1)}Args: {`);for(let s of o)r.push(`${k(n+2)}${te(s.name)}: ${lt(e,s.dataType)}`);r.push(`${k(n+1)}}`)}return r.push(`${k(n+1)}Returns: ${qi(e,t)}`),r.push(`${k(n)}}`),r}function Ee(e,t){return e.schema&&e.schema.length>0?e.schema:t}function lo(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=Ee(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},u=new Set([n]);for(let c of e.tables)u.add(Ee(c,n));for(let c of e.routines??[])u.add(Ee(c,n));for(let c of o)u.add(c.schema&&c.schema.length>0?c.schema:n);let d=[...u].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 = {");for(let c of d){let f=he(e.tables.filter(h=>Ee(h,n)===c&&h.kind!=="view")),p=he(e.tables.filter(h=>Ee(h,n)===c&&h.kind==="view")),x=he((e.routines??[]).filter(h=>Ee(h,n)===c)),S=he(o.filter(h=>(h.schema&&h.schema.length>0?h.schema:n)===c));if(m.push(`${k(1)}${te(c)}: {`),f.length===0)m.push(...Me("Tables",2));else{m.push(`${k(2)}Tables: {`);for(let h of f)m.push(...co(i,h,3));m.push(`${k(2)}}`)}if(p.length===0)m.push(...Me("Views",2));else{m.push(`${k(2)}Views: {`);for(let h of p)m.push(...co(i,h,3));m.push(`${k(2)}}`)}if(x.length===0)m.push(...Me("Functions",2));else{m.push(`${k(2)}Functions: {`);for(let h of x)m.push(...Ki(i,h,3));m.push(`${k(2)}}`)}if(S.length===0)m.push(...Me("Enums",2));else{m.push(`${k(2)}Enums: {`);for(let h of S)m.push(`${k(3)}${te(h.name)}: ${h.values.map(B).join(" | ")}`);m.push(`${k(2)}}`)}m.push(...Me("CompositeTypes",2)),m.push(`${k(1)}}`)}return m.push("}"),t.helpers!==!1&&(m.push(""),m.push(Wi(n))),t.constants!==!1&&(m.push(""),m.push(zi(d,o,n))),`${m.join(`
248
328
  `)}
249
- `}function Ci(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=pe(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${x(1)}${te(o)}: {`),r.push(`${x(2)}Enums: {},`),r.push(`${x(1)}},`);continue}r.push(`${x(1)}${te(o)}: {`),r.push(`${x(2)}Enums: {`);for(let a of s)r.push(`${x(3)}${te(a.name)}: [${a.values.map(B).join(", ")}],`);r.push(`${x(2)}},`),r.push(`${x(1)}},`)}return r.push("} as const"),r.join(`
250
- `)}function Mi(e){return`type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
329
+ `}function zi(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=he(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${k(1)}${te(o)}: {`),r.push(`${k(2)}Enums: {},`),r.push(`${k(1)}},`);continue}r.push(`${k(1)}${te(o)}: {`),r.push(`${k(2)}Enums: {`);for(let a of s)r.push(`${k(3)}${te(a.name)}: [${a.values.map(B).join(", ")}],`);r.push(`${k(2)}},`),r.push(`${k(1)}},`)}return r.push("} as const"),r.join(`
330
+ `)}function Wi(e){return`type DatabaseWithoutInternals = Database
251
331
 
252
332
  type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${B(e)}>]
253
333
 
@@ -362,7 +442,7 @@ export type CompositeTypes<
362
442
  ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
363
443
  : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
364
444
  ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
365
- : never`}function Hi(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:"require",label:e.ref}}function it(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 en(e,t){return new Promise(n=>{let r=Li("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 qi=`
445
+ : never`}function Xi(e){return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:"require",label:e.ref}}function ut(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 rn(e,t){return new Promise(n=>{let r=Ji("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 Zi=`
366
446
  with wanted as (select unnest($SCHEMAS$::text[]) as nspname)
367
447
  select json_build_object(
368
448
  'tables', coalesce((
@@ -465,28 +545,28 @@ select json_build_object(
465
545
  where t.typtype = 'e' and n.nspname in (select nspname from wanted)
466
546
  ), '[]'::json)
467
547
  )::text
468
- `;function Ki(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Wi(e,t,n=0){let r=e.tables.map(a=>{let i=a.primaryKey??[],u=new Set(i),d=a.columns.map(m=>({name:m.name,dataType:m.dataType,nullable:m.nullable,isPrimaryKey:u.has(m.name),default:m.default,autoIncrement:m.autoIncrement}));return{name:a.name,schema:a.schema,kind:a.kind,columns:d,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 Ji(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?it(e.dbUrl):Hi(await W(Gi(e))),r=qi.replace("$SCHEMAS$",Ki(t)),o=await(e.sql?e.sql(r):en(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:u}=Wi(a,n.database,Date.now()),d=ro(i,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:u,postgrestVersion:e.dataApiVersion}),m=null;return e.out&&(m=Fi(e.out)?e.out:Bi(process.cwd(),e.out),Ui(m,d,"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:u.length,path:m,types:d}}function Gi(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 oo(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 Ji(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}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}oe();import{createHash as Xc}from"node:crypto";import{existsSync as Sn,mkdirSync as Zc,readFileSync as xn,readdirSync as Qc,statSync as el,writeFileSync as tl}from"node:fs";import{dirname as nl,isAbsolute as bs,join as Me,resolve as Ss}from"node:path";import{pathToFileURL as rl}from"node:url";U();function ol(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:bs(e)||e.startsWith("./")||e.startsWith("../")?rl(Ss(e)).href:e}function sl(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(ol(e)):Promise.resolve().then(()=>(ys(),hs))}function al(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 il="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 Le(){let e;try{e=await sl()}catch(t){P("tool-missing",al(t))}return{ready:()=>e.podmanReady(il),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 xs(e){return Me(e,".snoutdata","local.json")}function gt(e){let t=xs(e);if(!Sn(t))return null;try{let n=JSON.parse(xn(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 cl(e,t){let n=xs(e);return Zc(nl(n),{recursive:!0}),tl(n,`${JSON.stringify(t,null,2)}
469
- `,{encoding:"utf8",mode:384}),n}var ll=54322;async function ks(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 vs(e,t,n,r=en){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 ul(e,t={}){let n=t.cwd??process.cwd();await ks(e);let r=gt(n),o=r??e.mint(t.port??ll),s=r?null:cl(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 u=vs(e,o,it(i)),d=await dl(u,n,t),m=await ml(u,n,d.length>0||!!s);return{status:a,uri:i,applied:d,seeded:m,created:s}}async function dl(e,t,n){let r=Ss(n.dir===void 0?Me(t,"migrations"):bs(n.dir)?n.dir:Me(t,n.dir));if(!Sn(r)||!el(r).isDirectory())return[];let o=Qc(r).filter(c=>c.endsWith(".sql")).map(c=>{let p=xn(Me(r,c),"utf8");return{name:c,sql:p,checksum:Xc("sha256").update(p,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(pl);s.code!==0&&P(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${Ce}`);let a=await e(`select name || E'\\t' || checksum from ${Ce};`);a.code!==0&&P("failed",a.err.trim()||`could not read ${Ce}`);let i=a.out.split(`
470
- `).map(c=>c.trim()).filter(c=>c.length>0).map(c=>{let p=c.indexOf(" ");return{name:c.slice(0,p),checksum:c.slice(p+1)}}),u=st(o,i,{outOfOrder:n.outOfOrder});u.ok||P("conflict",`Refused. This folder and the local database do not agree:
548
+ `;function Qi(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function ec(e,t,n=0){let r=e.tables.map(a=>{let i=a.primaryKey??[],u=new Set(i),d=a.columns.map(m=>({name:m.name,dataType:m.dataType,nullable:m.nullable,isPrimaryKey:u.has(m.name),default:m.default,autoIncrement:m.autoIncrement}));return{name:a.name,schema:a.schema,kind:a.kind,columns:d,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 tc(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?ut(e.dbUrl):Xi(await z(nc(e))),r=Zi.replace("$SCHEMAS$",Qi(t)),o=await(e.sql?e.sql(r):rn(n,r));o.code===127&&T("tool-missing",o.err.trim()),o.code!==0&&T("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||T("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{T("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:i,enums:u}=ec(a,n.database,Date.now()),d=lo(i,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:u}),m=null;return e.out&&(m=Vi(e.out)?e.out:Yi(process.cwd(),e.out),Gi(m,d,"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:u.length,path:m,types:d}}function nc(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 uo(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&T("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await tc(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}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}se();import{createHash as ul}from"node:crypto";import{existsSync as vn,mkdirSync as dl,readFileSync as En,readdirSync as ml,statSync as fl,writeFileSync as pl}from"node:fs";import{dirname as gl,isAbsolute as Ts,join as Ue,resolve as $s}from"node:path";import{pathToFileURL as hl}from"node:url";F();function yl(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:Ts(e)||e.startsWith("./")||e.startsWith("../")?hl($s(e)).href:e}function wl(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(yl(e)):Promise.resolve().then(()=>(Es(),vs))}function bl(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 Sl="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 Fe(){let e;try{e=await wl()}catch(t){T("tool-missing",bl(t))}return{ready:()=>e.podmanReady(Sl),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 As(e){return Ue(e,".snoutdata","local.json")}function wt(e){let t=As(e);if(!vn(t))return null;try{let n=JSON.parse(En(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 xl(e,t){let n=As(e);return dl(gl(n),{recursive:!0}),pl(n,`${JSON.stringify(t,null,2)}
549
+ `,{encoding:"utf8",mode:384}),n}var kl=54322;async function Rs(e){let t=await e.ensureImage(r=>l(r));t&&T("tool-missing",t);let n=await e.ready();n&&T("tool-missing",n),await e.prepare()}function Os(e,t,n,r=rn){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 _l(e,t={}){let n=t.cwd??process.cwd();await Rs(e);let r=wt(n),o=r??e.mint(t.port??kl),s=r?null:xl(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 u=Os(e,o,ut(i)),d=await vl(u,n,t),m=await El(u,n,d.length>0||!!s);return{status:a,uri:i,applied:d,seeded:m,created:s}}async function vl(e,t,n){let r=$s(n.dir===void 0?Ue(t,"migrations"):Ts(n.dir)?n.dir:Ue(t,n.dir));if(!vn(r)||!fl(r).isDirectory())return[];let o=ml(r).filter(c=>c.endsWith(".sql")).map(c=>{let f=En(Ue(r,c),"utf8");return{name:c,sql:f,checksum:ul("sha256").update(f,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(Pl);s.code!==0&&T(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${Le}`);let a=await e(`select name || E'\\t' || checksum from ${Le};`);a.code!==0&&T("failed",a.err.trim()||`could not read ${Le}`);let i=a.out.split(`
550
+ `).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)}}),u=ct(o,i,{outOfOrder:n.outOfOrder});u.ok||T("conflict",`Refused. This folder and the local database do not agree:
471
551
  ${u.problems.map(c=>` ${c.reason}`).join(`
472
- `)}`,{problems:u.problems});let d=new Map(o.map(c=>[c.name,c])),m=[];for(let c of u.pending){let p=d.get(c.name),f=`insert into ${Ce} (name, checksum) values (${ws(p.name)}, ${ws(p.checksum)});`,k=Re(p.sql);k||l(`${p.name} runs outside a transaction, so an interruption can leave it half applied.`);let S=k?`begin;
473
- ${p.sql}
474
- ${f}
552
+ `)}`,{problems:u.problems});let d=new Map(o.map(c=>[c.name,c])),m=[];for(let c of u.pending){let f=d.get(c.name),p=`insert into ${Le} (name, checksum) values (${Ps(f.name)}, ${Ps(f.checksum)});`,x=Ne(f.sql);x||l(`${f.name} runs outside a transaction, so an interruption can leave it half applied.`);let S=x?`begin;
553
+ ${f.sql}
554
+ ${p}
475
555
  commit;
476
- `:`${p.sql}
477
- ${f}
478
- `,h=await e(S);h.code!==0&&P("failed",`${p.name} failed, and nothing after it was tried.
479
- ${h.err.trim()}`,{failed:p.name,applied:m}),m.push(p.name),l(` applied ${p.name}`)}return m}async function ml(e,t,n){let r=Me(t,"seed.sql");if(!n||!Sn(r))return!1;let o=await e(xn(r,"utf8"));return o.code!==0&&P("failed",`seed.sql failed.
480
- ${o.err.trim()}`),l(" ran seed.sql"),!0}var Ce="_snoutdata_migrations",pl=`create table if not exists ${Ce} (
556
+ `:`${f.sql}
557
+ ${p}
558
+ `,h=await e(S);h.code!==0&&T("failed",`${f.name} failed, and nothing after it was tried.
559
+ ${h.err.trim()}`,{failed:f.name,applied:m}),m.push(f.name),l(` applied ${f.name}`)}return m}async function El(e,t,n){let r=Ue(t,"seed.sql");if(!n||!vn(r))return!1;let o=await e(En(r,"utf8"));return o.code!==0&&T("failed",`seed.sql failed.
560
+ ${o.err.trim()}`),l(" ran seed.sql"),!0}var Le="_snoutdata_migrations",Pl=`create table if not exists ${Le} (
481
561
  name text primary key,
482
562
  checksum text not null,
483
563
  applied_at timestamptz not null default now()
484
- );`;function ws(e){return`'${e.replace(/'/g,"''")}'`}function fl(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 _s(e,t={}){let n=await ul(e,t);return g(n,()=>{n.created&&(l(""),l(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),l(""),l(` ${E("Database")} running on port ${n.status.port}`),n.applied.length>0&&l(` ${E("Migrations")} ${n.applied.length} applied`),n.seeded&&l(` ${E("Seed")} seed.sql ran`),l(""),process.stdout.write(`${n.uri}
485
- `),l(b(" snoutdata gen types typescript --local > database.types.ts")),l(b(" snoutdata stop"))}),0}async function Es(e,t={}){let n=t.cwd??process.cwd(),r=gt(n);r||P("not-found","there is no local database for this folder. `snoutdata start` makes one."),await ks(e);let o=await e.stop(r.ref);return g(o,()=>l("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function Ps(e,t={}){let n=t.cwd??process.cwd(),r=gt(n);if(!r)return g({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 g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>l(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{l(` ${E("Project")} ${s.ref}`),l(` ${E("State")} ${s.ready?"running":s.status}`),l(` ${E("Port")} ${s.port}`),l(` ${E("Size")} ${fl(s.databaseBytes)}`),s.services.length>0&&l(` ${E("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
486
- `)}),0}async function Ts(e,t=process.cwd()){let n=gt(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:vs(e,n,it(r))}}M();U();var gl="2024-11-05";function N(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var T={type:"string"},ht=[{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. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:N({name:T,expires:T,project: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"])}],hl=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"]),yl=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]);function wl(e){return{...e,annotations:hl.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:yl.has(e.name)}}}function bl(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function Ue(e,t){return{jsonrpc:"2.0",id:e,result:t}}function kn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function v(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return Ue(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function Sl(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function C(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function $s(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 Ue(r,{protocolVersion:gl,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return Ue(r,{});case"tools/list":return Ue(r,{tools:bl([...ht.map(wl),...n.borrowed??[]])});case"tools/call":return xl(r,s,t,n);default:return kn(r,-32601,`unknown method: ${o}`)}}async function xl(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(ht.some(d=>d.name===o)||(r.borrowed?.some(d=>d.name===o)??!1)))return kn(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"],u=C(s,"ref");if(i.includes(o)&&!u)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 d=C(s,"name");return d?v(e,await n.createProject({name:d,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 d=await n.connection(u);return v(e,{url:d.uri,state:d.state,wakesInstantly:d.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",u));case"resume_project":return v(e,await n.setProjectState("resume",u));case"usage":{let d=typeof s.days=="number"?s.days:void 0;return v(e,await n.usage(u,d))}case"export_status":return v(e,await n.exportStatus(u));case"start_export":return v(e,await n.requestExport(u));case"reset_password":return v(e,await n.resetPassword(u));case"list_teams":return v(e,await n.listTeams());case"get_project":return v(e,await n.getProject(u));case"list_products":return v(e,await n.getProducts(u));case"set_product":{let d=C(s,"product");return d!=="auth"&&d!=="storage"&&d!=="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(u,d,s.enabled))}case"list_domains":return v(e,await n.listDomains(u));case"add_domain":case"verify_domain":case"remove_domain":{let d=C(s,"hostname");return d?v(e,await n.domainAction(u,o.split("_")[0],d)):v(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":return v(e,await n.restoreWindow(u));case"restore_to_point":{let d=C(s,"at");return d?v(e,await n.restoreTo(u,d,C(s,"name")??void 0)):v(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let d=C(s,"ref"),m=C(s,"name");return!d||!m?v(e,"deploy_function needs a project ref and a function name.",!0):v(e,await n.deployFunction(d,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 d=C(s,"ref");return d?v(e,await n.listFunctions(d)):v(e,"list_functions needs a project ref.",!0)}case"delete_function":{let d=C(s,"ref"),m=C(s,"name");return!d||!m?v(e,"delete_function needs a project ref and a function name.",!0):v(e,await n.deleteFunction(d,m))}case"list_function_secrets":{let d=C(s,"ref");return d?v(e,await n.listFunctionSecrets(d)):v(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return v(e,await n.listTokens());case"create_token":{let d=C(s,"name");if(!d)return v(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let m=C(s,"expires"),c=m?Sl(m):void 0;return c===null?v(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${m}".`,!0):v(e,await n.createToken(d,c,C(s,"project")??void 0))}case"revoke_token":{let d=C(s,"id");return d?v(e,await n.revokeToken(d)):v(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return v(e,await n.pushMigrations(u,{dir:C(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?v(e,await n.setProjectState("delete",u)):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(d=>d.name===o)?n.callBorrowed?Ue(e,await n.callBorrowed(o,s)):v(e,`${o} needs the SnoutData app, which is no longer answering.`,!0):kn(e,-32602,`unknown tool: ${o}`)}}catch(d){return v(e,d instanceof Error?d.message:String(d),!0)}}function As(e){let t=e.split(`
487
- `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}Ve();function kl(){return{whoami:()=>Se(),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=>xe(e),requestExport:e=>$e(e),resetPassword:e=>w("cloud-project-reset-password",{ref:e}),listTeams:()=>w("cloud-team-list",{}),deployFunction:(e,t,n)=>jt(e,t,n).then(r=>r.answer),listFunctions:e=>Ct(e),deleteFunction:(e,t)=>Mt(e,t),listFunctionSecrets:e=>w("cloud-project-secrets",{ref:e}),listTokens:()=>w("cloud-token-list",{}),createToken:(e,t,n)=>w("cloud-token-create",{name:e,...t?{expiresInDays:t}:{},...n?{project:n}:{}}),revokeToken:e=>w("cloud-token-revoke",{id:e}),getProject:e=>Jt(e),getProducts:e=>rt(e),setProduct:(e,t,n)=>Ht(e,t,n),listDomains:e=>ot(e),domainAction:(e,t,n)=>qt(e,t,n),restoreWindow:e=>Kt(e),restoreTo:(e,t,n)=>Wt(e,t,n),setProjectState:(e,t)=>w(`cloud-project-${e}`,{ref:t}),connection:e=>W(e),pushMigrations:(e,t)=>Xt(e,{dir:t.dir,dryRun:t.dryRun})}}var Rs="app_";async function vl(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Je();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await Bn(t)).map(o=>({name:`${Rs}${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 Os(e){Te(!0);let{tools:t,config:n}=await vl(),r=kl();n&&(r.callBorrowed=(u,d)=>Hn(n,u.slice(Rs.length),d));let o={...e,borrowed:t};K(t.length>0?`snoutdata mcp: ${ht.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 u=await Se();K(`snoutdata mcp: signed in as ${u.email??"this account"}.`)}catch(u){return K(`snoutdata mcp: ${u instanceof Error?u.message:String(u)}`),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(u){a.add(u),u.finally(()=>a.delete(u))}return new Promise(u=>{process.stdin.on("data",d=>{let m=As(s+d);s=m.rest;for(let c of m.messages)i(_l(c,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);u(0)})()}),process.stdin.on("error",()=>u(1))})}async function _l(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 $s(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
564
+ );`;function Ps(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 Ns(e,t={}){let n=await _l(e,t);return g(n,()=>{n.created&&(l(""),l(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),l(""),l(` ${v("Database")} running on port ${n.status.port}`),n.applied.length>0&&l(` ${v("Migrations")} ${n.applied.length} applied`),n.seeded&&l(` ${v("Seed")} seed.sql ran`),l(""),process.stdout.write(`${n.uri}
565
+ `),l(b(" snoutdata gen types typescript --local > database.types.ts")),l(b(" snoutdata stop"))}),0}async function Ds(e,t={}){let n=t.cwd??process.cwd(),r=wt(n);r||T("not-found","there is no local database for this folder. `snoutdata start` makes one."),await Rs(e);let o=await e.stop(r.ref);return g(o,()=>l("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function Ms(e,t={}){let n=t.cwd??process.cwd(),r=wt(n);if(!r)return g({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 g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>l(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{l(` ${v("Project")} ${s.ref}`),l(` ${v("State")} ${s.ready?"running":s.status}`),l(` ${v("Port")} ${s.port}`),l(` ${v("Size")} ${Tl(s.databaseBytes)}`),s.services.length>0&&l(` ${v("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
566
+ `)}),0}async function Is(e,t=process.cwd()){let n=wt(t);n||T("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:Os(e,n,ut(r))}}j();F();var $l="2024-11-05";function N(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var E={type:"string"},bt=[{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:E,region:E,teamId:E},["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:E},["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:E},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it.",inputSchema:N({ref:E},["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:E,dir:E,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:E,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:E},["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:E},["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:E},["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. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:N({name:E,expires:E,project:E},["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:E},["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:E,name:E,dir:E,entrypoint:E,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, bundle sizes, whether each one needs an API key, and each one's memory and concurrency with the plan's limits for both. Costs nothing.",inputSchema:N({ref:E},["ref"])},{name:"size_function",description:"Set one function's memory (MB one worker may use) and concurrency (how many workers it may run at once), within the project's plan. memoryMb x concurrency may not exceed the project's memory; list_functions shows each function's size and the plan's limits (limit.functionMemoryMb, limit.concurrencyMax, limit.podMemoryMb). A size that does not fit is refused with a sentence saying why. Omit one to keep it as it runs now; reset: true goes back to the plan's default. A second worker only helps CPU-bound work: requests that wait on the network share one.",inputSchema:N({ref:E,name:E,memoryMb:{type:"integer"},concurrency:{type:"integer"},reset:{type:"boolean"}},["ref","name"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:N({ref:E,name:E},["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:E},["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:E},["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:E},["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:E,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:E},["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:E,hostname:E},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:N({ref:E,hostname:E},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:N({ref:E,hostname:E},["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:E},["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:E,at:E,name:E},["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:E},["ref"])}],Al=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"]),Rl=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]);function Ol(e){return{...e,annotations:Al.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:Rl.has(e.name)}}}function Nl(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function Be(e,t){return{jsonrpc:"2.0",id:e,result:t}}function Pn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function _(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return Be(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function Dl(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function C(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function Cs(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 Be(r,{protocolVersion:$l,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return Be(r,{});case"tools/list":return Be(r,{tools:Nl([...bt.map(Ol),...n.borrowed??[]])});case"tools/call":return Ml(r,s,t,n);default:return Pn(r,-32601,`unknown method: ${o}`)}}async function Ml(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(bt.some(d=>d.name===o)||(r.borrowed?.some(d=>d.name===o)??!1)))return Pn(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"],u=C(s,"ref");if(i.includes(o)&&!u)return _(e,`${o} needs a project ref. Call list_projects to see them.`,!0);try{switch(o){case"whoami":return _(e,await n.whoami());case"list_projects":return _(e,await n.listProjects());case"create_project":{let d=C(s,"name");return d?_(e,await n.createProject({name:d,region:C(s,"region")??void 0,teamId:C(s,"teamId")??void 0})):_(e,"create_project needs a name.",!0)}case"get_connection_url":{let d=await n.connection(u);return _(e,{url:d.uri,state:d.state,wakesInstantly:d.wakesInstantly,warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"pause_project":return _(e,await n.setProjectState("pause",u));case"resume_project":return _(e,await n.setProjectState("resume",u));case"usage":{let d=typeof s.days=="number"?s.days:void 0;return _(e,await n.usage(u,d))}case"export_status":return _(e,await n.exportStatus(u));case"start_export":return _(e,await n.requestExport(u));case"reset_password":return _(e,await n.resetPassword(u));case"list_teams":return _(e,await n.listTeams());case"get_project":return _(e,await n.getProject(u));case"list_products":return _(e,await n.getProducts(u));case"set_product":{let d=C(s,"product");return d!=="auth"&&d!=="storage"&&d!=="data-api"?_(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?_(e,"set_product needs enabled: true or false.",!0):_(e,await n.setProduct(u,d,s.enabled))}case"list_domains":return _(e,await n.listDomains(u));case"add_domain":case"verify_domain":case"remove_domain":{let d=C(s,"hostname");return d?_(e,await n.domainAction(u,o.split("_")[0],d)):_(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":return _(e,await n.restoreWindow(u));case"restore_to_point":{let d=C(s,"at");return d?_(e,await n.restoreTo(u,d,C(s,"name")??void 0)):_(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let d=C(s,"ref"),m=C(s,"name");return!d||!m?_(e,"deploy_function needs a project ref and a function name.",!0):_(e,await n.deployFunction(d,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 d=C(s,"ref");return d?_(e,await n.listFunctions(d)):_(e,"list_functions needs a project ref.",!0)}case"size_function":{let d=C(s,"ref"),m=C(s,"name");if(!d||!m)return _(e,"size_function needs a project ref and a function name.",!0);let c=typeof s.memoryMb=="number"?s.memoryMb:void 0,f=typeof s.concurrency=="number"?s.concurrency:void 0,p=s.reset===!0;return!p&&c===void 0&&f===void 0?_(e,"size_function needs memoryMb, concurrency, or reset: true.",!0):_(e,await n.sizeFunction(d,m,{memoryMb:c,concurrency:f,reset:p}))}case"delete_function":{let d=C(s,"ref"),m=C(s,"name");return!d||!m?_(e,"delete_function needs a project ref and a function name.",!0):_(e,await n.deleteFunction(d,m))}case"list_function_secrets":{let d=C(s,"ref");return d?_(e,await n.listFunctionSecrets(d)):_(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return _(e,await n.listTokens());case"create_token":{let d=C(s,"name");if(!d)return _(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let m=C(s,"expires"),c=m?Dl(m):void 0;return c===null?_(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${m}".`,!0):_(e,await n.createToken(d,c,C(s,"project")??void 0))}case"revoke_token":{let d=C(s,"id");return d?_(e,await n.revokeToken(d)):_(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return _(e,await n.pushMigrations(u,{dir:C(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?_(e,await n.setProjectState("delete",u)):_(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(d=>d.name===o)?n.callBorrowed?Be(e,await n.callBorrowed(o,s)):_(e,`${o} needs the SnoutData app, which is no longer answering.`,!0):Pn(e,-32602,`unknown tool: ${o}`)}}catch(d){return _(e,d instanceof Error?d.message:String(d),!0)}}function js(e){let t=e.split(`
567
+ `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}Ye();function Il(){return{whoami:()=>_e(),listProjects:()=>J(),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 Oe(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=>ve(e),requestExport:e=>Re(e),resetPassword:e=>w("cloud-project-reset-password",{ref:e}),listTeams:()=>w("cloud-team-list",{}),deployFunction:(e,t,n)=>jt(e,t,n).then(r=>r.answer),listFunctions:e=>Lt(e),deleteFunction:(e,t)=>Ut(e,t),sizeFunction:(e,t,n)=>Ft(e,t,n),listFunctionSecrets:e=>w("cloud-project-secrets",{ref:e}),listTokens:()=>w("cloud-token-list",{}),createToken:(e,t,n)=>w("cloud-token-create",{name:e,...t?{expiresInDays:t}:{},...n?{project:n}:{}}),revokeToken:e=>w("cloud-token-revoke",{id:e}),getProject:e=>Vt(e),getProducts:e=>at(e),setProduct:(e,t,n)=>zt(e,t,n),listDomains:e=>it(e),domainAction:(e,t,n)=>Wt(e,t,n),restoreWindow:e=>Jt(e),restoreTo:(e,t,n)=>Gt(e,t,n),setProjectState:(e,t)=>w(`cloud-project-${e}`,{ref:t}),connection:e=>z(e),pushMigrations:(e,t)=>en(e,{dir:t.dir,dryRun:t.dryRun})}}var Ls="app_";async function Cl(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Ge();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await zn(t)).map(o=>({name:`${Ls}${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 Us(e){Ae(!0);let{tools:t,config:n}=await Cl(),r=Il();n&&(r.callBorrowed=(u,d)=>Wn(n,u.slice(Ls.length),d));let o={...e,borrowed:t};K(t.length>0?`snoutdata mcp: ${bt.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 u=await _e();K(`snoutdata mcp: signed in as ${u.email??"this account"}.`)}catch(u){return K(`snoutdata mcp: ${u instanceof Error?u.message:String(u)}`),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(u){a.add(u),u.finally(()=>a.delete(u))}return new Promise(u=>{process.stdin.on("data",d=>{let m=js(s+d);s=m.rest;for(let c of m.messages)i(jl(c,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);u(0)})()}),process.stdin.on("error",()=>u(1))})}async function jl(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 Cs(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
488
568
  `)}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)}})}
489
- `)}}M();U();function El(e){return e>=1?"over":e>=.8?"near":"ok"}function Ns(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:El(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function vn(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 Ds(e,t={}){let n=await w("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=Ns(n.days,n.limit);return g({...n,summary:r},()=>{if(l(E(`${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=`${ee(r.storage.usedBytes)} of ${ee(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(`${ee(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"}: ${vn(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 ${ee(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?"-":ee(o.dbBytes),o.repoBytes===null?"-":ee(o.repoBytes),vn(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 Fe="0.7.0",Is=`snoutdata ${Fe} \u2014 hosted Postgres, from a terminal or an agent
569
+ `)}}j();F();function Ll(e){return e>=1?"over":e>=.8?"near":"ok"}function Fs(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:Ll(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function Tn(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 Bs(e,t={}){let n=await w("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=Fs(n.days,n.limit);return g({...n,summary:r},()=>{if(l(v(`${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=`${ee(r.storage.usedBytes)} of ${ee(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(`${ee(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"}: ${Tn(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 ${ee(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(l(""),l(I([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":ee(o.dbBytes),o.repoBytes===null?"-":ee(o.repoBytes),Tn(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 He="0.9.0",Hs=`snoutdata ${He} \u2014 hosted Postgres, from a terminal or an agent
490
570
 
491
571
  snoutdata init [--name X] [--env] a database for this folder, linked, ready to use
492
572
  snoutdata login [--provider github] sign in through a browser
@@ -537,6 +617,8 @@ ${o.err.trim()}`),l(" ran seed.sql"),!0}var Ce="_snoutdata_migrations",pl=`crea
537
617
  snoutdata functions deploy <name> [--dir D] [--no-verify-jwt]
538
618
  from functions/<name>, onto the edge
539
619
  snoutdata functions list [--ref R]
620
+ snoutdata functions size <name> [--memory MB] [--concurrency N] [--reset]
621
+ its memory and workers, within the plan
540
622
  snoutdata functions delete <name>
541
623
 
542
624
  snoutdata secrets set NAME=value [...] the environment functions run with
@@ -579,10 +661,10 @@ an hour, so CI wants "tokens create", which does not expire.
579
661
  Docs: https://docs.snoutdata.com/cloud/cli
580
662
  Examples: https://github.com/snoutdata/snoutdata (a star helps other people find it)
581
663
  Source: https://github.com/snoutdata/snout-cli
582
- `;async function Pl(e){let[t,n,...r]=e.command;if(F(e,"version")||t==="version")return g({version:Fe},()=>process.stdout.write(`${Fe}
583
- `)),0;if(F(e,"help")&&t&&t!=="help"){let i=yt.filter(u=>u.name===t||u.name.startsWith(`${t} `));if(i.length>0)return g({commands:i},()=>{for(let u of i){let d=u.flags.length>0?` ${u.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${u.name}${d}
664
+ `;async function Ul(e){let[t,n,...r]=e.command;if(U(e,"version")||t==="version")return g({version:He},()=>process.stdout.write(`${He}
665
+ `)),0;if(U(e,"help")&&t&&t!=="help"){let i=St.filter(u=>u.name===t||u.name.startsWith(`${t} `));if(i.length>0)return g({commands:i},()=>{for(let u of i){let d=u.flags.length>0?` ${u.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${u.name}${d}
584
666
  ${u.summary}
585
667
  `)}process.stdout.write(`
586
668
  Every command also takes --json, --quiet and --help.
587
- `)}),0}let o=F(e,"help")||t==="help";if(o||!t)return g({version:Fe,usage:Is.trim(),commands:yt},()=>process.stdout.write(Is)),o?0:re.usage;let s=()=>{let i=Pe(e,"timeout");if(i!==void 0){if(!(i>0))throw new y("--timeout is a number of seconds greater than zero");return i*1e3}},a=()=>{let i=Ke({flag:_(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(F(e,"device")){let m=await Ye({name:Ge(),platform:process.platform});g({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 Xe(m.secret,{intervalMs:Math.max(1,m.interval)*1e3,expiresAt:m.expiresAt});return Y({accessToken:c.token}),g({ok:!0,name:c.name??null},()=>l("Signed in.")),0}let i=_(e,"domain"),u=F(e,"sso")||i!==void 0;if(u&&_(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let d=await et({provider:_(e,"provider"),sso:u,domain:i,noBrowser:F(e,"no-browser")||!ne(),timeoutMs:s()});return g({email:d.email??null},()=>l(`Signed in${d.email?` as ${d.email}`:""}.`)),0}case"logout":{In();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({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 Se();return g(i,()=>{process.stdout.write(`${i.email??i.id}
588
- `),i.token&&l(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await ur(),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 lr({name:i,expiresInDays:Pe(e,"expires"),project:_(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new y("tokens revoke needs the token's id or its sdt_ prefix");return await dr(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await or(),0;case"create":{let i=_(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let u=_(e,"team");return await At({teamId:u?await cr(u):void 0,name:i,region:_(e,"region"),wait:!F(e,"no-wait"),timeoutMs:s(),link:void 0}),0}case"pause":case"resume":case"delete":return await sr(n,_(e,"ref")??r[0]??a()),0;case"show":return await Br(_(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 br(a(),i,{dir:_(e,"dir"),entrypoint:_(e,"entrypoint"),verifyJwt:!F(e,"no-verify-jwt")}),0}case void 0:case"list":return await Sr(a()),0;case"delete":case"remove":{let i=r[0];if(!i)throw new y("functions delete needs a name");return await xr(a(),i),0}default:throw new y(`unknown command: functions ${n}`)}case"secrets":switch(n){case"set":return await kr(a(),r,{stdin:F(e,"stdin")}),0;case void 0:case"list":return await _r(a()),0;case"unset":case"remove":{let i=r[0];if(!i)throw new y("secrets unset needs a name");return await vr(a(),i),0}default:throw new y(`unknown command: secrets ${n}`)}case"keys":switch(n){case void 0:case"show":case"list":return await mr(a()),0;case"rotate":return await pr(a(),{force:F(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Cr(a()),0;if(n==="enable"||n==="disable")return await Mr(a(),jr(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${nt.join("|")}]`)}case"push":{if(n!=="credentials")throw new y("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await Qr(a()),0;if(i==="set"){let u=Zt(r[1]),d=u==="apns"?await Xr({p8:_(e,"p8"),keyId:_(e,"key-id"),teamId:_(e,"team-id"),topic:_(e,"topic"),environment:_(e,"environment")}):await Zr(_(e,"file"));return await eo(a(),u,d),0}if(i==="remove"||i==="unset")return await to(a(),Zt(r[1])),0;throw new y(`unknown command: push credentials ${i}`)}case"auth":return await Jr(a(),n,r,{clientId:_(e,"client-id"),stdin:F(e,"stdin"),siteUrl:_(e,"site-url"),allow:_(e,"allow"),file:_(e,"file"),subject:_(e,"subject")}),0;case"domains":return await Lr(a(),n,r[0]),0;case"teams":return await ir(),0;case"link":{let i=_(e,"ref")??n;if(!i)throw new y("link needs --ref");let u=qe(process.cwd(),{ref:i});return g({ref:i,path:u},()=>l(`Linked ${i} (${u}).`)),0}case"db":switch(n){case"url":return await Bt(a()),0;case"psql":return Or(a(),r);case"reset-password":return await ar(a()),0;case"push":return zr(a(),{dir:_(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return F(e,"window")?(await Ur(a()),0):_(e,"at")?(await Fr(a(),_(e,"at"),_(e,"name")),0):Ir(a(),{file:_(e,"file"),force:!!e.flags.force});case"export":return Dr(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=F(e,"local")?await Ts(await Le()):null,u=i?i.dbUrl:_(e,"db-url");return oo(r[0],{ref:u?void 0:a(),dbUrl:u,schemas:(_(e,"schema")??"public").split(",").map(d=>d.trim()).filter(Boolean),out:_(e,"out"),dataApiVersion:_(e,"data-api-version")??_(e,"postgrest-version"),sql:i?.sql})}case"start":return _s(await Le(),{port:Pe(e,"port"),dir:_(e,"dir"),noMigrations:F(e,"no-migrations"),outOfOrder:F(e,"out-of-order")});case"stop":return Es(await Le());case"status":return Ps(await Le());case"usage":return Ds(a(),{days:Pe(e,"days"),history:!!e.flags.history});case"mcp":return Os({version:Fe,allowDelete:!!e.flags["allow-delete"]});case"init":return Tl(e);default:throw new y(`unknown command: ${t}`)}}async function Tl(e){let t=Ke({flag:_(e,"ref")});if(t)return l(`This folder is already linked to ${t}.`),await Bt(t),0;let n=_(e,"name")??$l(process.cwd());l(`Creating a project called "${n}"\u2026`),await At({name:n,region:_(e,"region"),wait:!0,link:process.cwd()});let r=Ke({cwd:process.cwd()});if(r&&F(e,"env")){let{connection:o}=await Promise.resolve().then(()=>(M(),ae)),s=await o(r),a=Nr(process.cwd(),s.uri);l(`${a.replaced?"Updated":"Wrote"} DATABASE_URL in ${a.path}.`)}return 0}function $l(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Al(e){if(e instanceof q)return de(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return de("usage",e.message),re.usage;if(e instanceof se)return de("not-signed-in",e.message),re["not-signed-in"];if(e instanceof Q){let n=ze(e.status);return de(n,e.message,{status:e.status}),re[n]}let t=Kn(e);return de(t,e instanceof Error?e.message:String(e)),re[t]}function _n(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function Rl(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function Ol(){Te(Rl(process.argv.slice(2)));let e;try{e=Tn(process.argv.slice(2))}catch(t){de("usage",String(t instanceof Error?t.message:t)),_n(re.usage);return}Te(e.json),Gn(e.flags.quiet===!0),Mn(We({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{_n(await Pl(e))}catch(t){_n(Al(t))}}Ol();
669
+ `)}),0}let o=U(e,"help")||t==="help";if(o||!t)return g({version:He,usage:Hs.trim(),commands:St},()=>process.stdout.write(Hs)),o?0:oe.usage;let s=()=>{let i=fe(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=We({flag:P(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 Ze({name:Ve(),platform:process.platform});g({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 Qe(m.secret,{intervalMs:Math.max(1,m.interval)*1e3,expiresAt:m.expiresAt});return Y({accessToken:c.token}),g({ok:!0,name:c.name??null},()=>l("Signed in.")),0}let i=P(e,"domain"),u=U(e,"sso")||i!==void 0;if(u&&P(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let d=await nt({provider:P(e,"provider"),sso:u,domain:i,noBrowser:U(e,"no-browser")||!re(),timeoutMs:s()});return g({email:d.email??null},()=>l(`Signed in${d.email?` as ${d.email}`:""}.`)),0}case"logout":{Ln();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({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 _e();return g(i,()=>{process.stdout.write(`${i.email??i.id}
670
+ `),i.token&&l(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await pr(),0;case"create":{let i=P(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await fr({name:i,expiresInDays:fe(e,"expires"),project:P(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new y("tokens revoke needs the token's id or its sdt_ prefix");return await gr(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await cr(),0;case"create":{let i=P(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let u=P(e,"team");return await Nt({teamId:u?await mr(u):void 0,name:i,region:P(e,"region"),wait:!U(e,"no-wait"),timeoutMs:s(),link:void 0}),0}case"pause":case"resume":case"delete":return await lr(n,P(e,"ref")??r[0]??a()),0;case"show":return await Jr(P(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 _r(a(),i,{dir:P(e,"dir"),entrypoint:P(e,"entrypoint"),verifyJwt:!U(e,"no-verify-jwt")}),0}case void 0:case"list":return await Pr(a()),0;case"size":{let i=r[0];if(!i)throw new y("functions size needs a name: snoutdata functions size <name> --memory MB --concurrency N");return await Er(a(),i,{memoryMb:fe(e,"memory"),concurrency:fe(e,"concurrency"),reset:U(e,"reset")}),0}case"delete":case"remove":{let i=r[0];if(!i)throw new y("functions delete needs a name");return await Tr(a(),i),0}default:throw new y(`unknown command: functions ${n}`)}case"secrets":switch(n){case"set":return await $r(a(),r,{stdin:U(e,"stdin")}),0;case void 0:case"list":return await Rr(a()),0;case"unset":case"remove":{let i=r[0];if(!i)throw new y("secrets unset needs a name");return await Ar(a(),i),0}default:throw new y(`unknown command: secrets ${n}`)}case"keys":switch(n){case void 0:case"show":case"list":return await hr(a()),0;case"rotate":return await yr(a(),{force:U(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Hr(a()),0;if(n==="enable"||n==="disable")return await qr(a(),Br(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${st.join("|")}]`)}case"push":{if(n!=="credentials")throw new y("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await so(a()),0;if(i==="set"){let u=tn(r[1]),d=u==="apns"?await ro({p8:P(e,"p8"),keyId:P(e,"key-id"),teamId:P(e,"team-id"),topic:P(e,"topic"),environment:P(e,"environment")}):await oo(P(e,"file"));return await ao(a(),u,d),0}if(i==="remove"||i==="unset")return await io(a(),tn(r[1])),0;throw new y(`unknown command: push credentials ${i}`)}case"auth":return await Zr(a(),n,r,{clientId:P(e,"client-id"),stdin:U(e,"stdin"),siteUrl:P(e,"site-url"),allow:P(e,"allow"),file:P(e,"file"),subject:P(e,"subject")}),0;case"domains":return await Kr(a(),n,r[0]),0;case"teams":return await dr(),0;case"link":{let i=P(e,"ref")??n;if(!i)throw new y("link needs --ref");let u=ze(process.cwd(),{ref:i});return g({ref:i,path:u},()=>l(`Linked ${i} (${u}).`)),0}case"db":switch(n){case"url":return await Kt(a()),0;case"psql":return jr(a(),r);case"reset-password":return await ur(a()),0;case"push":return to(a(),{dir:P(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return U(e,"window")?(await zr(a()),0):P(e,"at")?(await Wr(a(),P(e,"at"),P(e,"name")),0):Fr(a(),{file:P(e,"file"),force:!!e.flags.force});case"export":return Ur(a(),{timeoutMs:s(),out:P(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 Is(await Fe()):null,u=i?i.dbUrl:P(e,"db-url");return uo(r[0],{ref:u?void 0:a(),dbUrl:u,schemas:(P(e,"schema")??"public").split(",").map(d=>d.trim()).filter(Boolean),out:P(e,"out"),sql:i?.sql})}case"start":return Ns(await Fe(),{port:fe(e,"port"),dir:P(e,"dir"),noMigrations:U(e,"no-migrations"),outOfOrder:U(e,"out-of-order")});case"stop":return Ds(await Fe());case"status":return Ms(await Fe());case"usage":return Bs(a(),{days:fe(e,"days"),history:!!e.flags.history});case"mcp":return Us({version:He,allowDelete:!!e.flags["allow-delete"]});case"init":return Fl(e);default:throw new y(`unknown command: ${t}`)}}async function Fl(e){let t=We({flag:P(e,"ref")});if(t)return l(`This folder is already linked to ${t}.`),await Kt(t),0;let n=P(e,"name")??Bl(process.cwd());l(`Creating a project called "${n}"\u2026`),await Nt({name:n,region:P(e,"region"),wait:!0,link:process.cwd()});let r=We({cwd:process.cwd()});if(r&&U(e,"env")){let{connection:o}=await Promise.resolve().then(()=>(j(),ie)),s=await o(r),a=Lr(process.cwd(),s.uri);l(`${a.replaced?"Updated":"Wrote"} DATABASE_URL in ${a.path}.`)}return 0}function Bl(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Hl(e){if(e instanceof q)return pe(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return pe("usage",e.message),oe.usage;if(e instanceof ae)return pe("not-signed-in",e.message),oe["not-signed-in"];if(e instanceof Q){let n=Xe(e.status);return pe(n,e.message,{status:e.status}),oe[n]}let t=Gn(e);return pe(t,e instanceof Error?e.message:String(e)),oe[t]}function $n(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function ql(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function Kl(){Ae(ql(process.argv.slice(2)));let e;try{e=On(process.argv.slice(2))}catch(t){pe("usage",String(t instanceof Error?t.message:t)),$n(oe.usage);return}Ae(e.json),Xn(e.flags.quiet===!0),Bn(Je({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{$n(await Ul(e))}catch(t){$n(Hl(t))}}Kl();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snoutdata",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "SnoutData Cloud projects from a terminal or an agent. Create a project with a Postgres database, get a DATABASE_URL, run migrations, and serve the same operations to an AI agent over MCP.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "SnoutData",