palbase 0.36.0 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/index.js +2 -2
- package/dist/backend/engine.js +2 -2
- package/dist/backend/env.js +1 -1
- package/dist/backend/index.js +3 -3
- package/dist/backend/stack.js +1 -1
- package/dist/backend/test.js +2 -2
- package/dist/chunks/{FTSLUVFM.js → 4XFRN45H.js} +2 -2
- package/dist/chunks/{CG4MLOSS.js → 5A5YCRPV.js} +2 -2
- package/dist/chunks/{NIRJK52A.js → 5XN7I6JA.js} +1 -1
- package/dist/chunks/{MIGVS67R.js → H4U6H3OQ.js} +1 -1
- package/dist/chunks/{RSDDSUSD.js → HBXDBDED.js} +2 -2
- package/dist/chunks/{JWCGOXP2.js → ICTHOHDI.js} +2 -2
- package/dist/chunks/ID4L4YNL.js +2 -0
- package/dist/chunks/{UJ6BQKTD.js → IYSOKCF4.js} +2 -2
- package/dist/chunks/{IHV64CIO.js → KAUIR66Z.js} +2 -2
- package/dist/chunks/{D4FVVF3V.js → KN2GS5WM.js} +2 -2
- package/dist/chunks/{7H437WNH.js → LFNWB4YQ.js} +1 -1
- package/dist/chunks/{ODKRTA5Q.js → LZM7DZDZ.js} +2 -2
- package/dist/chunks/{UCYI2SR2.js → M7SGK2KE.js} +2 -2
- package/dist/chunks/{PIGHUXF4.js → PORYVJPX.js} +2 -2
- package/dist/chunks/{TJRKN6TT.js → QAH6YJUC.js} +1 -1
- package/dist/chunks/{M5AWMRKL.js → QMNURUZN.js} +2 -2
- package/dist/chunks/{U567PXWS.js → QY5HQFM5.js} +2 -2
- package/dist/chunks/{QBYUSNOL.js → RR6G2GXT.js} +1 -1
- package/dist/chunks/{46OHFRCO.js → S76G5MS2.js} +1 -1
- package/dist/chunks/{KYDXOXBY.js → VAC32QIB.js} +2 -2
- package/dist/chunks/{NVD5UXUT.js → XNR3LADA.js} +2 -2
- package/dist/chunks/{E4CI4NNS.js → YULBKQL2.js} +2 -2
- package/dist/chunks/{H56DM2K7.js → ZBANMLOU.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/cloud/index.js +2 -2
- package/dist/refuse/ai.js +1 -1
- package/dist/refuse/backend.js +1 -1
- package/dist/refuse/cloud.js +1 -1
- package/dist/refuse/server.js +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/testing.js +2 -2
- package/dist/web/index.js +2 -2
- package/dist/web/message.js +2 -2
- package/dist/web/palbe.js +2 -2
- package/dist/web/paywall.js +2 -2
- package/dist/web/push-sw.js +1 -1
- package/dist/web/ssr.js +2 -2
- package/dist/web/vite.js +2 -2
- package/package.json +2 -2
- package/types/ai/src/ai.d.ts +2 -2
- package/dist/chunks/TV4DBUC7.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! palbase 0.36.
|
|
2
|
+
/*! palbase 0.36.1 — Palbase SDK License (see LICENSE) */
|
|
3
3
|
var qb=Object.defineProperty;var We=(e,t)=>{for(var n in t)qb(e,n,{get:t[n],enumerable:!0})};import{parseArgs as kw}from"node:util";var Kb=Symbol.for("palbase.data"),Gb=globalThis,tp=Gb[Kb]??=new WeakSet;function Ub(e){return e instanceof Date||ArrayBuffer.isView(e)||e instanceof ArrayBuffer}function Ca(e){let t=[e];for(;t.length>0;){let n=t.pop();if(!(typeof n!="object"||n===null||tp.has(n)||Ub(n))){tp.add(n);for(let s of Array.isArray(n)?n:Object.values(n))t.push(s)}}return e}var zb="X-PoW-Challenge-ID",Bb="X-PoW-Nonce";function np(e){if(typeof e!="object"||e===null)return null;let t=e;if(t.error!=="pow_required"||typeof t.challenge!="object"||t.challenge===null)return null;let{id:n,prefix:s,difficulty:a}=t.challenge;return typeof n!="string"||typeof s!="string"||typeof a!="number"||!Number.isInteger(a)||a<0?null:{id:n,prefix:s,difficulty:a}}function Fb(e){return 8*2**e}var Wn;async function Jb(){if(Wn)return Wn;let e=globalThis;if(e.process?.versions?.node!==void 0||e.process?.versions?.bun!==void 0)try{let s=(await import("node:crypto")).createHash;if(typeof s=="function")return Wn=a=>new Uint8Array(s("sha256").update(a).digest()),Wn}catch{}let t=new TextEncoder;return Wn=async n=>new Uint8Array(await crypto.subtle.digest("SHA-256",t.encode(n))),Wn}function Wb(e){let t=0;for(let n of e){if(n===0){t+=8;continue}return t+Math.clz32(n)-24}return t}var Vb=4096,Hb=typeof globalThis.setImmediate=="function"?()=>new Promise(e=>globalThis.setImmediate(e)):()=>new Promise(e=>setTimeout(e,0));async function sp(e,t={}){if(e.difficulty>24)throw new Error(`proof-of-work: difficulty ${e.difficulty} is above auth's ceiling of 24`);let n=await Jb(),s=Fb(e.difficulty),a=Date.now()+(t.timeLimitMs??12e4);for(let r=0;r<s;r++){if(r%Vb===0&&r>0){if(await Hb(),t.signal?.aborted)throw t.signal.reason??new DOMException("proof-of-work aborted","AbortError");if(Date.now()>a)throw new Error(`proof-of-work: gave up on difficulty ${e.difficulty} after ${r} attempts`)}let i=n(e.prefix+r),o=i instanceof Uint8Array?i:await i;if(Wb(o)>=e.difficulty)return{[zb]:e.id,[Bb]:String(r)}}throw new Error(`proof-of-work: no nonce for difficulty ${e.difficulty} within ${s} attempts`)}var j=class extends Error{status;code;requestId;retryAfter;body;op;get isNetworkError(){return this.code==="network_error"}constructor(t){super(t.message,t.cause===void 0?void 0:{cause:t.cause}),this.name="PalbaseError",this.op=t.op,this.status=t.status,this.code=t.code,this.requestId=t.requestId,this.retryAfter=t.retryAfter,this.body=t.body}};function c(e,t,n){let s=String(n);if(s===""||s==="."||s==="..")throw new j({op:e,status:0,code:"invalid_path_parameter",message:`${e}: ${t} cannot be ${s===""?"empty":JSON.stringify(s)}`});return encodeURIComponent(s)}var Yb=new Set(["apikey","authorization"]),Xb=64*1024;function bi(e){let t=ja(e.baseUrl),n=e.fetch??globalThis.fetch,s=e.solveProofOfWork??!0,a=async(r,i)=>{let o=ew(t,r),l=new Headers;for(let[b,w]of Object.entries(r.headers??{}))w!=null&&!Yb.has(b.toLowerCase())&&l.set(b,String(w));for(let[b,w]of Object.entries(i))l.set(b,w);await tw(r,e.credentials,l),l.has("accept")||l.set("accept","application/json");let d={method:r.method,headers:l,redirect:"manual",signal:r.signal,...r.keepalive?{keepalive:!0}:{}};r.body&&r.body.value!==void 0&&(l.set("content-type",r.body.contentType),d.body=nw(r.body.kind,r.body.value),Vn(d.body)||(d.duplex="half"));let p;try{p=r.onUploadProgress&&e.xmlHttpRequest?await Zb(e.xmlHttpRequest,r.op,o,d,r.onUploadProgress):await n(o,d)}catch(b){throw r.signal?.aborted||b instanceof j?b:new j({op:r.op,status:0,code:"network_error",message:`${r.op}: the request did not reach ${t.origin}`,cause:b})}let u=r.answers[p.status]??(r.ownResponse&&p.status>=200&&p.status<300?"raw":void 0);if(u===void 0)throw p.status>=200&&p.status<300?(await p.body?.cancel(),new j({op:r.op,status:p.status,code:"undeclared_answer",message:`${r.op}: the server answered ${p.status}, which its contract does not declare; the request may have taken effect`})):await aw(r.op,p);let g;switch(u){case"json":try{g=Ca(await p.json())}catch(b){throw new j({op:r.op,status:p.status,code:"invalid_json",message:`${r.op}: the answer is not JSON`,cause:b})}break;case"raw":g=p;break;default:await p.body?.cancel(),g=void 0}return{status:p.status,data:g,headers:p.headers}};return{async send(r){try{return await a(r,{})}catch(i){let o=s&&i instanceof j&&i.status===403?np(i.body):null,l=r.body?.value;if(!o||r.body?.kind==="raw"&&!Vn(l))throw i;let d=i,p;try{p=await sp(o,{signal:r.signal})}catch(u){throw r.signal?.aborted?u:new j({op:r.op,status:403,code:"pow_unsolved",message:`${r.op}: ${u instanceof Error?u.message:String(u)}`,requestId:d.requestId,body:d.body,cause:u})}return a(r,p)}}}}function Zb(e,t,n,s,a){return new Promise((r,i)=>{let o=s.signal;if(o?.aborted)return i(o.reason);let l=new e,d=()=>l.abort();o?.addEventListener("abort",d,{once:!0});let p=u=>{o?.removeEventListener("abort",d),u()};l.open(s.method??"GET",n.href),l.responseType="arraybuffer",new Headers(s.headers).forEach((u,g)=>l.setRequestHeader(g,u)),l.upload&&(l.upload.onprogress=u=>{u.lengthComputable&&a(u.loaded,u.total)}),l.onload=()=>p(()=>{if(l.responseURL!==""&&l.responseURL!==n.href){i(new j({op:t,status:0,code:"undeclared_redirect",message:`${t}: the server redirected; the answer is another address's, and was not read`}));return}if(l.status<200||l.status>599)return i(new TypeError(`${t}: answered ${l.status}`));let u=l.status===204||l.status===205||l.status===304;r(new Response(u?null:l.response,{status:l.status,statusText:l.statusText,headers:Qb(l.getAllResponseHeaders())}))}),l.onerror=()=>p(()=>i(new TypeError(`${t}: the request failed`))),l.onabort=()=>p(()=>i(o?.reason??new DOMException("The request was aborted","AbortError"))),l.send(s.body)})}function Qb(e){let t=new Headers;for(let n of e.split(/\r?\n/)){let s=n.indexOf(":");if(!(s<=0))try{t.append(n.slice(0,s).trim(),n.slice(s+1).trim())}catch{}}return t}function ja(e){let t;try{t=new URL(e)}catch{throw new TypeError(`baseUrl ${JSON.stringify(e)} is not an absolute URL`)}let n=t.hostname==="localhost"||t.hostname.endsWith(".localhost")||t.hostname==="127.0.0.1"||t.hostname==="[::1]";if(t.protocol!=="https:"&&!(t.protocol==="http:"&&n))throw new TypeError(`baseUrl must be https (http only for a loopback address): ${t.origin}`);if(t.username||t.password||t.search||t.hash)throw new TypeError("baseUrl carries no user, query or fragment");if(t.pathname!=="/"&&t.pathname!=="")throw new TypeError("baseUrl is the project origin alone, with no path");return t}function ew(e,t){if(!t.path.startsWith("/")||t.path.startsWith("//"))throw new j({op:t.op,status:0,code:"invalid_path",message:`${t.op}: path ${JSON.stringify(t.path)} is not absolute`});let n=new URL(t.path+sw(t.query),e);if(n.origin!==e.origin)throw new j({op:t.op,status:0,code:"invalid_path",message:`${t.op}: path leaves the project's origin`});return n}async function tw(e,t,n){if(e.auth.length===0)return;let s=e.auth.some(i=>i.includes("userJWT"))?await t.accessToken?.():void 0,a=i=>{switch(i){case"ApiKeyAuth":return!!(t.publishableKey??t.secretKey);case"SecretKeyAuth":case"AdminAuth":return!!(t.secretKey??t.token);case"PalbaseBearer":return!!t.token;case"userJWT":return!!s&&!t.token;default:return!1}},r=e.auth.filter(i=>i.every(a)).reduce((i,o)=>i===void 0||o.length>i.length?o:i,void 0);if(!r)throw new j({op:e.op,status:0,code:"missing_credential",message:`${e.op} needs one of: ${e.auth.map(i=>i.length===0?"nothing":i.join(" + ")).join(" | ")}`});for(let i of r)i==="ApiKeyAuth"?n.set("apikey",t.publishableKey??t.secretKey):(i==="SecretKeyAuth"||i==="AdminAuth"||i==="PalbaseBearer")&&t.token?n.set("authorization",`Bearer ${t.token}`):i==="SecretKeyAuth"||i==="AdminAuth"?n.set("apikey",t.secretKey):i==="userJWT"&&n.set("authorization",`Bearer ${s}`)}function nw(e,t){switch(e){case"json":return JSON.stringify(t);case"form":{let n=new URLSearchParams;for(let[s,a]of Object.entries(t))wi(n,s,a);return n}case"raw":return t}}function Vn(e){return!!(e==null||typeof e=="string"||e instanceof URLSearchParams||e instanceof Blob||e instanceof FormData||e instanceof ArrayBuffer||ArrayBuffer.isView(e))}function sw(e){if(!e)return"";let t=new URLSearchParams;for(let[s,a]of Object.entries(e))wi(t,s,a);let n=t.toString();return n===""?"":`?${n}`}function wi(e,t,n){if(n!=null){if(Array.isArray(n)){for(let s of n)wi(e,t,s);return}if(n instanceof Date){e.append(t,n.toISOString());return}e.append(t,typeof n=="object"?JSON.stringify(n):String(n))}}async function aw(e,t){let n=await rw(t,Xb).catch(()=>""),s;try{s=n===""?void 0:JSON.parse(n)}catch{s=n}let a=iw(s)?s:void 0,r=Number(t.headers.get("retry-after")),i=t.status>=300&&t.status<400;return new j({op:e,status:t.status,code:a?.error??(i?"undeclared_redirect":`http_${t.status}`),message:a?.error_description??(i?`${e}: the server redirected (${t.status}); the request was not followed, its credentials stay here`:`${e}: ${t.status} ${t.statusText}`.trim()),requestId:a?.request_id??t.headers.get("x-request-id")??void 0,retryAfter:a?.retry_after??(Number.isFinite(r)&&r>0?r:void 0),body:s})}async function rw(e,t){if(!e.body)return"";let n=e.body.getReader(),s=[],a=0;for(;a<t;){let{done:o,value:l}=await n.read();if(o)break;s.push(l),a+=l.byteLength}await n.cancel().catch(()=>{});let r=new Uint8Array(Math.min(a,t)),i=0;for(let o of s){let l=Math.min(o.byteLength,r.byteLength-i);if(r.set(o.subarray(0,l),i),i+=l,i>=r.byteLength)break}return new TextDecoder().decode(r)}function iw(e){return typeof e=="object"&&e!==null&&typeof e.error=="string"}var Oa={project:{type:"string",short:"P",value:"project",description:"the project (name or id; org/project when the name is in two organizations)"},env:{type:"string",short:"E",value:"env",description:"the project's environment (slug or ref)"},json:{type:"boolean",description:"print the answer as JSON"},"non-interactive":{type:"boolean",description:"never ask: refuse what would need a question"},yes:{type:"boolean",short:"y",description:"confirm a change to a production environment without asking"},debug:{type:"boolean",description:"print each request the command sends"},help:{type:"boolean",short:"h",description:"show help"}};var y=class extends Error{exitCode;hint;code;reported;constructor(t,n,s={}){super(n),this.name="CliError",this.code=t,this.hint=s.hint,this.exitCode=s.exitCode??1,this.reported=s.reported??!1}};function m(e,t){return new y("usage",e,{hint:t,exitCode:2})}var Ma=class{io;json;constructor(t,n){this.io=t,this.json=n}result(t,n){this.json?this.io.stdout(`${JSON.stringify(t,null,2)}
|
|
4
4
|
`):n()}line(t=""){this.io.stdout(`${K(t)}
|
|
5
5
|
`)}note(t){this.io.stderr(`${K(t)}
|
|
@@ -9,7 +9,7 @@ var qb=Object.defineProperty;var We=(e,t)=>{for(var n in t)qb(e,n,{get:t[n],enum
|
|
|
9
9
|
`);let i;try{i=await this.io.fetch(t,{method:n.method,headers:{...n.headers,...a},body:n.body,signal:n.signal,...s?{}:{duplex:"half"},redirect:"manual"})}catch(l){throw n.signal?.aborted?l:new y("network_error",`${new URL(t).origin} could not be reached: ${l instanceof Error?l.message:String(l)}`)}if(this.debug&&this.io.stderr(`< ${i.status} ${i.headers.get("x-request-id")??""}
|
|
10
10
|
`),i.status!==503||!s||r>=ww||!bw.has(await ki(i)??""))return i;await i.body?.cancel();let o=Number(i.headers.get("retry-after")??"");await this.io.sleep((Number.isFinite(o)&&o>0?Math.min(o,30):1)*1e3)}}async exchange(t,n,s){let a=await this.token(),r=await this.send(t,n,s,a.value);if(r.status===401&&a.renewable){let i=await this.renew(await this.currentSession());r=await this.send(t,n,s,i.access_token)}return{res:r,renewable:a.renewable}}async answered(t,n,s,a){if(s.status===204)return;let r=await s.text(),i=r===""?void 0:vw(r);if(s.ok)return i;let o=i??{};throw s.status===401?o.error==="token_not_for_machines"?wp(new URL(this.cloud.studioApi).host):a?Hn():this.tokenRefused("Studio"):new j({op:`${t} ${n}`,status:s.status,code:o.error??`http_${s.status}`,message:o.error_description??`Studio answered ${s.status}`,requestId:o.request_id??s.headers.get("x-request-id")??void 0,body:i})}tokenRefused(t){return(this.io.env.PALBASE_TOKEN??"").startsWith("pbe_")?new y("token_refused",`${t} refused PALBASE_TOKEN, an environment's token: it ended (a few hours at most), it was revoked, or what minted it no longer holds`,{hint:"mint another: palbase env:tokens:create --scope <scope> (an admin), or with the environment's secret key (Studio's POST /environments/{ref}/tokens)"}):(this.io.env.PALBASE_TOKEN??"").startsWith("pbu_")?new y("token_refused",`${t} refused PALBASE_TOKEN, an account token: it was revoked, it ended, or, in this organization, the one who made it left it`,{hint:"its maker makes a new one: palbase account:tokens:create <name>"}):new y("token_refused",`${t} refused PALBASE_TOKEN: it was revoked, it ended, or the one who made it no longer holds its role`,{hint:"an admin makes a new one: palbase tokens:create <name>"})}async token(){let t=this.io.env.PALBASE_TOKEN;return t&&t!==""?{value:t,renewable:!1}:{value:(await this.currentSession()).access_token,renewable:!0}}async renew(t){let n=await mp(this.io,this.cloud,t);return await this.state.writeSession(n),this.session=n,n}async send(t,n,s,a){let r=`${this.cloud.studioApi}${yw}${n}`;this.debug&&this.io.stderr(`> ${t} ${r}
|
|
11
11
|
`);try{let i=await this.io.fetch(r,{method:t,headers:{...a===void 0?{}:{authorization:`Bearer ${a}`},accept:"application/json",...s===void 0?{}:{"content-type":"application/json"}},body:s===void 0?void 0:JSON.stringify(s),redirect:"manual"});return this.debug&&this.io.stderr(`< ${i.status} ${i.headers.get("x-request-id")??""}
|
|
12
|
-
`),i}catch(i){throw new y("network_error",`Studio (${this.cloud.studioApi}) could not be reached: ${i instanceof Error?i.message:String(i)}`)}}};function wp(e){return new y("token_not_for_machines",`the platform's edge refused PALBASE_TOKEN at ${e}, taking it for a machine's address (token_not_for_machines): the token itself was not refused`,{hint:"nothing is wrong with the token: run this again in a few minutes, and tell Palbase if it lasts"})}async function ki(e){let t=await e.clone().json().catch(()=>null),n=t&&typeof t=="object"?t.error:void 0;return typeof n=="string"&&/^(console|token|edge)_/.test(n)?n:void 0}function vw(e){try{return JSON.parse(e)}catch{return e}}var vp={version:"0.36.
|
|
12
|
+
`),i}catch(i){throw new y("network_error",`Studio (${this.cloud.studioApi}) could not be reached: ${i instanceof Error?i.message:String(i)}`)}}};function wp(e){return new y("token_not_for_machines",`the platform's edge refused PALBASE_TOKEN at ${e}, taking it for a machine's address (token_not_for_machines): the token itself was not refused`,{hint:"nothing is wrong with the token: run this again in a few minutes, and tell Palbase if it lasts"})}async function ki(e){let t=await e.clone().json().catch(()=>null),n=t&&typeof t=="object"?t.error:void 0;return typeof n=="string"&&/^(console|token|edge)_/.test(n)?n:void 0}function vw(e){try{return JSON.parse(e)}catch{return e}}var vp={version:"0.36.1"};var Na=vp.version;async function Pp(e,t,n){let s=new Map(n.map(i=>[i.name,i])),a=e.includes("--json"),r=e.includes("--debug");try{let{name:i,at:o}=Sw(e);if(i===void 0)return e.includes("--version")?(t.stdout(`palbase ${Na}
|
|
13
13
|
`),0):(t.stdout($p(n)),e.length===0||e.includes("--help")||e.includes("-h")?0:2);if(i==="help"){let S=e[o+1],$=S===void 0?void 0:s.get(S);if(S!==void 0&&!$)throw _p(S,n);return t.stdout($?Sp($):$p(n)),0}let l=s.get(i);if(!l)throw _p(i,n);let d=[...e.slice(0,o),...e.slice(o+1)];if(d.includes("--help")||d.includes("-h"))return t.stdout(Sp(l)),0;let{flags:p,positionals:u}=$w(l,d),g=l.args??{min:0,max:0};if(u.length<g.min)throw m(`${l.name} needs ${g.min===1?"an argument":`${g.min} arguments`}`,$i(l));if(u.length>g.max)throw m(`${l.name} takes ${g.max===0?"no argument":`at most ${g.max}`}: ${JSON.stringify(u.slice(g.max))} is left over`,$i(l));let b={project:p.project,env:p.env,json:p.json===!0,nonInteractive:p["non-interactive"]===!0,yes:p.yes===!0,debug:p.debug===!0},w=pp(t.env),_=new xa(cp(t.configDir,w)),T={io:t,state:_,out:new Ma(t,b.json),global:b,args:u,flags:p,interactive:t.interactive&&!b.nonInteractive,cloud:w,studio:new Da(t,w,_,b.debug)};return await l.run(T)??0}catch(i){return Pw(i,t,a,r)}}function Sw(e){let t=new Set;for(let[n,s]of Object.entries(Oa))s.type==="string"&&(t.add(`--${n}`),s.short&&t.add(`-${s.short}`));for(let n=0;n<e.length;n++){let s=e[n];if(s==="--")return{at:-1};if(t.has(s)){n++;continue}if(!s.startsWith("-"))return{name:s,at:n}}return{at:-1}}function $w(e,t){let n={...Oa,...e.flags??{}},s={};for(let[a,r]of Object.entries(n))s[a]={type:r.type,...r.short?{short:r.short}:{},...r.multiple?{multiple:!0}:{}};try{let{values:a,positionals:r}=kw({args:t,options:s,strict:!0,allowPositionals:!0});return{flags:a,positionals:r}}catch(a){throw m(a instanceof Error?a.message.replace(/^.*?: /,""):String(a),`palbase help ${e.name}`)}}function Pw(e,t,n,s){let a="error",r,i,o=1,l;return e instanceof y?(a=e.code,r=e.message,i=e.hint,o=e.exitCode):e instanceof j?(a=e.code,r=e.message,l=e.requestId,e.code==="token_out_of_scope"&&t.env.PALBASE_TOKEN&&(r=`${e.op} refused PALBASE_TOKEN: ${e.message}`,i="this is the token's reach, fixed when it was minted: a person's yes does not change it \u2014 mint a token whose scopes name this call, or have a person run it signed in")):r=e instanceof Error?e.message:String(e),n&&e instanceof y&&e.reported||(n?t.stdout(`${JSON.stringify({error:a,message:r,...i?{hint:i}:{},...l?{request_id:l}:{}},null,2)}
|
|
14
14
|
`):(t.stderr(`error: ${K(r)}${l?` (request ${K(l)})`:""}
|
|
15
15
|
`),i&&t.stderr(` ${K(i)}
|
|
@@ -638,7 +638,7 @@ ${ee}`;for(let[G,ee]of w){ee.has(fe)?i.set(`${z}/${G}.ts`,M(`export * from './en
|
|
|
638
638
|
`))}for(let{machine:G}of F.get(fe)??[])i.set(`${z}/palbe/${G}.ts`,M(`export * from '../environments/${fe}/palbe/${G}';
|
|
639
639
|
`));for(let G of Qe)for(let{machine:ee}of F.get(G)??[])i.set(`${z}/palbe/${ee}.${G}.ts`,Q(G,`export * from '../environments/${G}/palbe/${ee}';
|
|
640
640
|
`))}let Fn={schema:1,project:{id:n.id,name:n.name},apps:r.map(M=>({id:M.id,slug:M.slug,name:M.name,platform:M.platform})),...fe!==void 0&&Qe.length>0?{types_environment:fe}:{},...Object.keys(L).length>0?{clients:Object.fromEntries(Object.entries(L).sort(([M],[Q])=>M.localeCompare(Q)))}:{}};return i.set(`${z}/project.json`,us(Fn)),{files:i,notes:o,project:Fn}}async function g$(e,t,n){let s=`${z}/environments/${t}`,a;try{a=await pa(await he(e,`${s}/contracts/${n}.openapi.json`),"utf8")}catch(o){if(o.code==="ENOENT")return;throw o}let r=`contracts/${n}.openapi.json, sha256 `,i=vg("sha256").update(a).digest("hex");for(let o of[`${s}/palbe/${n}.ts`,`${s}/palbe/${n}.kt`]){let l=await pa(rt(e,o),"utf8").catch(()=>""),d=l.indexOf(r);if(d>=0){i=l.slice(d+r.length,l.indexOf(")",d));break}}return{doc:JSON.parse(a),digest:i}}function m$(e){return[...e.matchAll(/^public (?:nonisolated )?(?:struct|enum|typealias|final class|class|protocol) ([A-Za-z_][A-Za-z0-9_]*)/gm)].map(t=>t[1])}function f$(e){return[...e.matchAll(/^(?:(?:data|sealed|enum|value|internal|private|public) )*(?:class|object|interface|typealias) ([A-Za-z_][A-Za-z0-9_]*)|^val ([A-Za-z_][A-Za-z0-9_.]*\.[A-Za-z_][A-Za-z0-9_]*):/gm)].map(t=>t[1]??t[2])}function wg(e,t,n,s,a){for(let r of t){let i=e.get(r);if(i!==void 0&&i!==n)throw new y("backends_collide",`${i} and ${n} both declare ${r} in ${s}'s ${a} client, which one target cannot hold`,{hint:"rename the controller or route in one of them (it names the type), or reach them from different apps"});e.set(r,n)}}async function y$(e){let t=new Map,n=async s=>{let a;try{a=await No(rt(e,s),{withFileTypes:!0})}catch(r){if(r.code==="ENOENT")return;throw r}for(let r of a){let i=`${s}/${r.name}`;if(r.isDirectory()){(s!==z||r.name==="environments"||r.name==="palbe")&&await n(i);continue}if(!r.isFile()||r.name.endsWith(".storekit"))continue;let o=await pa(rt(e,i),"utf8");(s!==z||r.name==="project.json"||r.name.endsWith(".palbase")||o.startsWith(la))&&t.set(i,o)}};return await n(z),t}async function jr(e){try{return JSON.parse(await pa(rt(e,z,"project.json"),"utf8"))}catch(t){if(t.code==="ENOENT")return null;throw new y("unreadable_project_file",`${z}/project.json is not what palbase apps:sync writes: ${t.message}`)}}async function kg(e){let t=e.io.cwd,n=e.flags.check===!0,s=await jr(t),a=await h$(e,s),r=await y$(t),i=[],o=[],l=[];for(let[u,g]of a.files){let b=r.get(u);b===void 0?i.push(u):b!==g&&o.push(u)}for(let u of r.keys())a.files.has(u)||l.push(u);let d=i.length+o.length+l.length>0;if(!n){for(let u of[...i,...o]){let g=await he(t,u);await t$(i$(g),{recursive:!0}),await he(t,u),await a$(g,a.files.get(u))}for(let u of l)await _g(await he(t,u));await Sg(t,rt(t,z))}let p={project:a.project.project.name,apps:a.project.apps.map(u=>u.slug),added:i,changed:o,removed:l,unchanged:a.files.size-i.length-o.length,check:n,differs:d};for(let u of a.notes)e.out.note(u);return e.out.result(p,()=>{let u=(g,b)=>b.forEach(w=>e.out.line(` ${g} ${w}`));if(!d){e.out.line(`${z}/ is what ${a.project.project.name} gives now (${a.files.size} files)`);return}e.out.line(n?`${z}/ differs from what ${a.project.project.name} gives now:`:`${z}/ written for ${a.project.apps.map(g=>g.slug).join(", ")}:`),u(n?"missing":"added",i),u(n?"differs":"changed",o),u(n?"not given":"removed",l),n&&e.out.note("palbase apps:sync brings it up to date")}),n&&d?1:void 0}async function he(e,t){let n=rt(e,z),s=rt(e,...t.split("/")),a=Cr(n,s);if(a===""||a.startsWith(`..${Er}`)||a===".."||a.split(Er).some(i=>i===".."||i===""))throw new y("outside_palbase",`${t} is not under ${z}/`);let r=e;for(let i of[z,...a.split(Er)]){r=rt(r,i);try{if((await e$(r)).isSymbolicLink())throw new y("linked_path",`${Cr(e,r)} is a link: palbase writes only into ${z}/ itself`,{hint:"remove the link, and run it again"})}catch(o){if(o.code==="ENOENT")break;throw o}}return s}async function Sg(e,t){let n;try{n=await No(t,{withFileTypes:!0})}catch{return}for(let s of n)s.isDirectory()&&await Sg(e,rt(t,s.name));Cr(rt(e,z),t)===""||Cr(rt(e,z),t).startsWith(`..${Er}`)||(await No(t)).length===0&&await s$(t)}async function ln(e){let t=await qt(e);return{id:t[0].project_id,name:t[0].project_name,org:t[0].org_name}}async function Xe(e,t){return(await e.studio.get(`/projects/${t}/apps`)).apps}function lt(e,t){let n=e.find(s=>s.slug===t||s.id===t);if(!n)throw new y("no_such_app",`the project has no app ${t}`,{hint:`its apps: ${e.map(s=>s.slug).join(", ")||"none \u2014 palbase apps:create"}`});return n}function Or(e){let t=e.ios?[e.ios.bundle,e.ios.app_clip&&`App Clip ${e.ios.app_clip}`,e.ios.watch&&`watch ${e.ios.watch}`].filter(Boolean).join(", "):e.android?`${e.android.package} (${e.android.fingerprints.map(n=>n.label).join(", ")||"no certificate"})`:e.web?.origins.join(", ")??"";return e.suspended?`${t} \u2014 suspended`:t}var $g={name:"apps:list",summary:"the project's apps, and what each is in every environment it is set in",scope:"environment",async run(e){let t=await ln(e),n=await Xe(e,t.id);e.out.result({apps:n},()=>{if(n.length===0)return e.out.note(`${t.name} has no app yet: palbase apps:create ios|android|web <name>`);e.out.table(n,[{header:"slug",value:s=>s.slug},{header:"platform",value:s=>s.platform},{header:"name",value:s=>s.deleted_at?`${s.name} (deleted; restorable until ${s.purge_after?.slice(0,10)})`:s.name},{header:"environments",value:s=>s.environments.map(a=>`${a.slug}: ${Or(a)}`).join("; ")||"\u2014"}])})}},Pg={name:"apps:create",summary:"register an app of the project (an iOS app names its Apple team); then set it in each environment (apps:env:set)",usage:"ios|android|web <name> [--slug <slug>] [--team-id <id>] [--app-id-prefix <prefix>]",details:["An app is one product on one platform: its widgets, notification service and other extensions are not apps of their own \u2014 Apple gives them bundles under the app's, and they share its sign-in.","The slug is the app's file in a checkout: palbase/environments/<env>/<slug>.json."],args:{min:2,max:2},flags:{slug:{type:"string",value:"slug",description:"its name in a checkout (default: from the name)"},"team-id":{type:"string",value:"id",description:"an iOS app's Apple team: 10 characters, Apple Developer \u203A Membership"},"app-id-prefix":{type:"string",value:"prefix",description:"an iOS app's App ID prefix when it is not the team's (an app made before 2011, or moved from another team)"}},scope:"environment",async run(e){let[t,n]=e.args;if(t!=="ios"&&t!=="android"&&t!=="web")throw m("the platform is ios, android or web");let s=await ln(e),a=await e.studio.post(`/projects/${s.id}/apps`,{platform:t,name:n,...e.flags.slug?{slug:e.flags.slug}:{},...e.flags["team-id"]?{team_id:e.flags["team-id"]}:{},...e.flags["app-id-prefix"]?{app_id_prefix:e.flags["app-id-prefix"]}:{}});e.out.result(a,()=>{e.out.line(`${s.name} \u203A ${a.name} (${a.platform}, ${a.slug})`),e.out.note(`set it in each environment: palbase apps:env:set ${a.slug} -E <env> ${t==="ios"?"--bundle <bundle id>":t==="android"?"--package <application id> --sha256 <label>=<fingerprint>":"--origin <https://\u2026>"}`)})}},Ag={name:"apps:get",summary:"an app of the project, with what it is and its key in every environment it is set in",usage:"<app>",args:{min:1,max:1},scope:"environment",async run(e){let t=await ln(e),n=lt(await Xe(e,t.id),e.args[0]);e.out.result(n,()=>{if(e.out.line(`${n.name} \u2014 ${n.platform}, ${n.slug}${n.team_id?`, team ${n.team_id}${n.app_id_prefix?` (App ID prefix ${n.app_id_prefix})`:""}`:""}`),n.deleted_at&&e.out.note(`deleted; restorable until ${n.purge_after} (palbase apps:restore ${n.slug})`),n.environments.length===0)return e.out.note(`in no environment yet: palbase apps:env:set ${n.slug} -E <env> \u2026`);e.out.table(n.environments,[{header:"environment",value:s=>s.slug},{header:"is",value:s=>Or(s)},{header:"publishable key",value:s=>s.key?.key??"\u2014"}])})}},Rg={name:"apps:update",summary:"rename an app, or change an iOS app's Apple team: every environment it is in is told",usage:'<app> [--name <name>] [--team-id <id>] [--app-id-prefix <prefix>|""]',args:{min:1,max:1},flags:{name:{type:"string",value:"name",description:"what it is called"},"team-id":{type:"string",value:"id",description:"an iOS app's Apple team"},"app-id-prefix":{type:"string",value:"prefix",description:`its App ID prefix; "" for the team's`}},scope:"environment",async run(e){let t=await ln(e),n=lt(await Xe(e,t.id),e.args[0]),s={};if(e.flags.name!==void 0&&(s.name=e.flags.name),e.flags["team-id"]!==void 0&&(s.team_id=e.flags["team-id"]),e.flags["app-id-prefix"]!==void 0&&(s.app_id_prefix=e.flags["app-id-prefix"]===""?null:e.flags["app-id-prefix"]),Object.keys(s).length===0)throw m("nothing to change: --name, --team-id or --app-id-prefix");let a=await e.studio.request("PATCH",`/projects/${t.id}/apps/${n.id}`,s);e.out.result(a,()=>e.out.line(`${a.name} (${a.slug}) \u2014 told to ${a.environments.length} environment(s)`))}},Tg={name:"apps:delete",summary:"delete an app: suspended in every environment for 30 days \u2014 its builds stop reaching the project \u2014 then removed for good",usage:"<app>",args:{min:1,max:1},scope:"environment",async run(e){let t=await ln(e),n=lt(await Xe(e,t.id),e.args[0]);if(!e.global.yes){if(!e.interactive)throw new y("confirmation_needed",`deleting ${n.name} stops every build of it reaching the project`,{hint:"confirm with -y"});let a=(await e.io.prompt(`Every build of ${n.name} stops reaching ${t.name} at once (restorable for 30 days). Delete it? [y/N] `)).trim().toLowerCase();if(a!=="y"&&a!=="yes")throw new y("cancelled","nothing was changed")}let s=await e.studio.request("DELETE",`/projects/${t.id}/apps/${n.id}`);e.out.result(s,()=>e.out.line(`${s.name} is deleted; palbase apps:restore ${s.slug} brings it back as it was until ${s.purge_after}`))}},Eg={name:"apps:restore",summary:"bring a deleted app back as it was, with its keys, in every environment",usage:"<app>",args:{min:1,max:1},scope:"environment",async run(e){let t=await ln(e),n=lt(await Xe(e,t.id),e.args[0]),s=await e.studio.post(`/projects/${t.id}/apps/${n.id}/restore`);e.out.result(s,()=>e.out.line(`${s.name} is back in ${s.environments.length} environment(s)`))}},b$=/^([a-z][a-z0-9_-]{0,31})=(.+)$/,Cg={name:"apps:env:set",summary:"what an app is in the environment: its bundle (iOS), package and signing certificates (Android) or origins (web)",usage:"<app> [--bundle <id> [--app-clip <id>] [--watch <id>]] [--package <id> --sha256 <label>=<fingerprint>\u2026] [--origin <url>\u2026 --redirect <url>\u2026]",details:["iOS: the app's bundle in this environment; its App Clip and watch app are the bundle followed by one more part. Its extensions are not set: they share the app's.","Android: its application ID and the SHA-256 of each certificate it is signed with \u2014 Play Console \u203A App signing (Google's app signing key, not the upload key), ./gradlew signingReport for a debug key. Label each: debug, play_signing, upload\u2026","Web: the origins its pages are served from (https; http only on this computer) and the addresses a sign-in may return to.","The platform publishes it to Apple and Google for the environment: a device sees a change in a day or so (Apple caches its file; Android 15 re-checks within a week)."],args:{min:1,max:1},flags:{bundle:{type:"string",value:"id",description:"an iOS app's bundle identifier here"},"app-clip":{type:"string",value:"id",description:"its App Clip's bundle (the bundle followed by one more part)"},watch:{type:"string",value:"id",description:"its watch app's bundle (the bundle followed by one more part)"},package:{type:"string",value:"id",description:"an Android app's application ID here"},sha256:{type:"string",multiple:!0,value:"label=fingerprint",description:"a signing certificate's SHA-256, labelled (play_signing=AB:CD:\u2026); every one the app is signed with"},origin:{type:"string",multiple:!0,value:"url",description:"a web app's origin"},redirect:{type:"string",multiple:!0,value:"url",description:"an address a sign-in may return to"}},scope:"environment",async run(e){let t=await f(e),n=lt(await Xe(e,t.project_id),e.args[0]),s=i=>(e.flags[i]??[]).flatMap(o=>o.split(",")).map(o=>o.trim()).filter(Boolean),a;if(n.platform==="ios"){if(!e.flags.bundle)throw m(`${n.name} is an iOS app: give its bundle here (--bundle)`);a={ios:{bundle:e.flags.bundle,...e.flags["app-clip"]?{app_clip:e.flags["app-clip"]}:{},...e.flags.watch?{watch:e.flags.watch}:{}}}}else if(n.platform==="android"){if(!e.flags.package)throw m(`${n.name} is an Android app: give its package here (--package)`);let i=(e.flags.sha256??[]).map(o=>{let l=b$.exec(o.trim());if(!l)throw m(`--sha256 ${JSON.stringify(o)}: label=fingerprint, like play_signing=AB:CD:\u2026`);return{label:l[1],sha256:l[2]}});a={android:{package:e.flags.package,fingerprints:i}}}else{let i=s("origin");if(i.length===0)throw m(`${n.name} is a web app: give its origins here (--origin)`);let o=s("redirect");a={web:{origins:i,...o.length?{redirects:o}:{}}}}await v(e,t,`set ${n.name} (${n.slug}) here: ${Or(a)}`);let r=await e.studio.request("PUT",`/environments/${t.env_ref}/apps/${n.id}`,a);for(let i of r.warnings)e.out.warn(i);e.out.result(r,()=>{e.out.line(`${n.name} in ${t.env_name}: ${Or(r)}`),r.key&&e.out.line(`its publishable key here: ${r.key.key}`),e.out.note("palbase apps:sync writes its settings into this checkout")})}},jg={name:"apps:env:unset",summary:"take an app out of the environment: its key there revoked, Apple's and Google's files no longer naming it",usage:"<app>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=lt(await Xe(e,t.project_id),e.args[0]);await v(e,t,`take ${n.name} out of here: builds of it stop reaching ${t.env_name}`),await e.studio.request("DELETE",`/environments/${t.env_ref}/apps/${n.id}`),e.out.result({app:n.slug,environment:t.env_slug,removed:!0},()=>e.out.line(`${n.name} is out of ${t.env_name}`))}},Og={name:"apps:sdkconfig",summary:"an app's settings in the environment, as palbase apps:sync writes them: its address, its publishable key, what its modules tell it",usage:"<app> [--format json|env]",details:["The publishable key is public: it ships in the app. A secret key never does."],args:{min:1,max:1},flags:{format:{type:"string",value:"format",description:"json (default) or env"}},scope:"environment",async run(e){let t=await f(e),n=lt(await Xe(e,t.project_id),e.args[0]),s=e.flags.format??"json";if(s!=="json"&&s!=="env")throw m("--format is json or env");let a=await e.studio.get(`/environments/${t.env_ref}/apps/${n.id}/config`);e.out.result(a,()=>{s==="env"?(e.out.line(`PALBASE_URL=${a.url}`),e.out.line(`PALBASE_PUBLISHABLE_KEY=${a.publishable_key}`)):e.out.line(JSON.stringify(a,null,2))})}},Mg={name:"apps:sync",summary:"write each environment's settings and backend clients (Palbe) of this checkout's apps under palbase/ \u2014 nothing else is touched",usage:"[<app>\u2026] [--check] [--types <env>] [--backend [<machine>=]<dir>] [--as <machine>=<name>]\u2026",args:{min:0,max:20},details:["The apps named join this checkout's (palbase/project.json); with none named, its apps are synced.","palbase/environments/<env>/: each app's settings (<app>.json), each backend's contract (contracts/<machine>.openapi.json) and Palbe client made from it \u2014 TypeScript for a web app, Swift for an iOS app (in palbase/<app>.palbase), Kotlin for an Android app. A web app imports pb from palbase/<app>.ts: one environment's for the editor (--types), the build's mode's for the build (palbase() in vite.config), which type-checks the app against that environment.","--check writes nothing: it exits 1 naming what differs from the project now \u2014 for CI.","--backend makes a backend's Palbe from its checkout on this computer (its palbase/backend app writes its contract and exits; nothing is connected to), to try an endpoint before any machine serves it.","A backend is pb.<name> in the apps \u2014 pb.shopApi.rooms.create() for the machine shop-api \u2014 unless --as names it (--as shop-api=shop): the name is the checkout's, the same in every environment, and none of the client's own (auth, flags, storage, \u2026)."],flags:{check:{type:"boolean",description:"write nothing; exit 1 when the checkout is not what the project gives now"},types:{type:"string",value:"env",description:"the environment the editor's types are of (remembered in palbase/project.json)"},backend:{type:"string",value:"[machine=]dir",description:"a backend's checkout here: its Palbe made from the contract it writes, not from its machine's"},as:{type:"string",multiple:!0,value:"machine=name",description:"a backend's name in the apps (pb.<name>): its machine's camelCased unless named; the same in every environment (remembered in palbase/project.json)"}},scope:"environment",async run(e){return kg(e)}};function Mr(e){let t=new URLSearchParams;for(let[s,a]of Object.entries(e))a!==void 0&&t.set(s,String(a));let n=t.toString();return n===""?"":`?${n}`}function xr(e,t){let n=te(e.flags.limit,"limit");if(n!==void 0&&(n<1||n>t))throw m(`--limit is a whole number from 1 to ${t}`);return n}function Ir(e,t){return{limit:{type:"string",value:"n",description:`at most this many (1\u2013${e}, default ${t})`},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}}}var Ut=e=>e?e.slice(0,19).replace("T"," "):"",On=(e,t)=>e||t;function Lr(e,t,n){t&&e.out.line(`More: palbase ${n} --cursor ${t}`)}function Ig(e){e.out.note("This read is in the project's access log (palbase messaging:access-log).")}function Lg(e){return e.map(t=>`${t.name?A(t.name):"a file"} (${A(t.content_type)}, ${t.size} bytes)`).join(", ")}var xg="A report is deleted, with what it carries, 190 days after it is resolved; one never resolved, 2 years after it was made.",w$={name:"messaging:reports:list",summary:"the reports people made, newest first: why, whom, which messages (what they carry: messaging:reports:get)",usage:"[--status open|resolved] [--limit <n>] [--cursor <cursor>]",flags:{status:{type:"string",value:"status",description:"only the open ones, or only the resolved ones"},...Ir(100,30)},scope:"environment",async run(e){let t=e.flags.status;if(t!==void 0&&t!=="open"&&t!=="resolved")throw m("--status is open or resolved");let n=xr(e,100),s=await f(e),a=await h(e,s,"GET",`/messaging/admin/reports${Mr({status:t,limit:n,cursor:e.flags.cursor})}`);e.out.result(a,()=>{a.data.length===0&&e.out.line(t===void 0?"No report yet.":`No ${t} report.`),e.out.table(a.data,[{header:"report",value:r=>r.id},{header:"made",value:r=>Ut(r.created_at)},{header:"status",value:r=>r.status==="resolved"?`resolved: ${r.resolution??""}`:"open"},{header:"reason",value:r=>r.reason},{header:"reported",value:r=>On(r.reported_user_id,"")},{header:"by",value:r=>On(r.reporter_id,"(deleted)")},{header:"messages",value:r=>String(r.message_ids.length)}]),Lr(e,a.next_cursor,`messaging:reports:list${t?` --status ${t}`:""}`)})}},v$={name:"messaging:reports:get",summary:"one report with what it carries: the reporter's note, what the messages it names said when reported and before they were edited",usage:"<report id>",details:["An owner's or an admin's (a member lists the reports, without what they carry); the read is in the project's access log. --json carries the messages' files with the keys that open them.","A report of a group a person deleted says who deleted it and when; naming no message, it carries the last 30 messages the reporter saw in it.",xg],args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET",`/messaging/admin/reports/${D("the report's id",e.args[0])}`);Ig(e),e.out.note(xg),e.out.result(n,()=>{e.out.line(`report ${n.id} (${n.status==="resolved"?`resolved ${Ut(n.resolved_at)}: ${A(n.resolution??"")}`:"open"})`),e.out.line(`made ${Ut(n.created_at)} by ${On(n.reporter_id,"a user since deleted")}`),e.out.line(`reported ${On(n.reported_user_id,"no one in particular")}`),e.out.line(`reason ${A(n.reason)}`),e.out.line(`conversation ${n.conversation_id}${n.conversation_deleted_at?` (group deleted by ${On(n.conversation_deleted_by,"a user since deleted")} at ${Ut(n.conversation_deleted_at)})`:""}`),n.note&&e.out.line(`note ${A(n.note)}`);let s=n.messages??[];if(s.length!==0){e.out.line(),e.out.line("What the messages said when reported:");for(let a of s){e.out.line(` #${a.seq} ${Ut(a.created_at)} ${On(a.sender_id,"the server")} ${a.kind==="text"||a.text?A(a.text??""):`(${a.kind})`}`),a.attachments.length>0&&e.out.line(` files: ${Lg(a.attachments)}`);for(let r of a.earlier_versions)e.out.line(` before it was edited: ${A(r.text)}, until ${Ut(r.replaced_at)}`)}}})}},_$={name:"messaging:reports:resolve",summary:"resolve a report, saying what came of it in the project's own words",usage:"<report id> --resolution <text>",args:{min:1,max:1},flags:{resolution:{type:"string",value:"text",description:"what came of it: 1 to 64 characters (warned, removed, nothing found\u2026)"}},scope:"environment",async run(e){let t=e.flags.resolution?.trim();if(!t||[...t].length>64)throw m("--resolution says what came of it: 1 to 64 characters");let n=await f(e),s=D("the report's id",e.args[0]);await v(e,n,`resolve the report ${e.args[0]}: ${t}`);let a=await h(e,n,"POST",`/messaging/admin/reports/${s}/resolve`,{resolution:t});e.out.result(a,()=>e.out.line(`Resolved the report ${a.id}: ${A(a.resolution??t)}.`))}},k$={name:"messaging:access-log",summary:"each time people's messages were read or removed \u2014 by the project's server, or an owner or admin from Studio or the CLI \u2014 newest first: what, which conversation, whose, by whom (kept 400 days)",usage:"[--limit <n>] [--cursor <cursor>]",flags:Ir(200,50),scope:"environment",async run(e){let t=xr(e,200),n=await f(e),s=await h(e,n,"GET",`/messaging/admin/access-log${Mr({limit:t,cursor:e.flags.cursor})}`);e.out.result(s,()=>{s.data.length===0&&e.out.line("No read or removal of people's messages yet."),e.out.table(s.data,[{header:"when",value:a=>Ut(a.at)},{header:"what",value:a=>a.action},{header:"messages",value:a=>String(a.count)},{header:"conversation",value:a=>a.conversation_id??""},{header:"person",value:a=>a.user_id??""},{header:"by",value:a=>a.actor},{header:"request",value:a=>a.request_id??""}]),Lr(e,s.next_cursor,"messaging:access-log")})}},S$={name:"messaging:conversations:list",summary:"the project's conversations, or a person's with --user, or a space's with --space, the latest first: whose, how many people, when it last moved",usage:"[--user <user id>] [--space <space>] [--limit <n>] [--cursor <cursor>]",flags:{user:{type:"string",value:"user id",description:"only this person's conversations"},space:{type:"string",value:"space",description:"only the conversations of this space (as org:<id>)"},...Ir(100,30)},scope:"environment",async run(e){let t=e.flags.user;if(t!==void 0&&t.trim()==="")throw m("--user is a person's id");let n=e.flags.space;if(n!==void 0&&!/^[A-Za-z0-9:_./@-]{1,128}$/.test(n))throw m("--space is 1 to 128 letters, digits and : _ . / @ - (as org:<id>)");let s=xr(e,100),a=await f(e),r=await h(e,a,"GET",`/messaging/admin/conversations${Mr({user_id:t,space:n,limit:s,cursor:e.flags.cursor})}`);e.out.result(r,()=>{r.data.length===0&&e.out.line(t===void 0?"No conversation yet.":`${t} is in no conversation.`),e.out.table(r.data,[{header:"conversation",value:i=>i.id},{header:"kind",value:i=>i.kind==="group"&&i.visibility==="open"?"group (open)":i.kind},{header:"space",value:i=>i.space??""},{header:"title",value:i=>i.kind==="group"?i.title??"":(i.members??[]).map(o=>o.user_id).join(" \xB7 ")},{header:"people",value:i=>String(i.member_count)},{header:"messages",value:i=>String(i.last_seq)},{header:"last moved",value:i=>Ut(i.last_message_at??i.created_at)}]),Lr(e,r.next_cursor,`messaging:conversations:list${t!==void 0?` --user ${t}`:""}${n!==void 0?` --space ${n}`:""}`)})}},$$={name:"messaging:messages:list",summary:"what a conversation said, newest first \u2014 each read in the project's access log",usage:"<conversation id> [--limit <n>] [--cursor <cursor>]",details:["An owner's or an admin's: a member sees the conversations and the reports, not what was said.","--json carries the messages whole, their files with the keys that open them."],args:{min:1,max:1},flags:Ir(100,50),scope:"environment",async run(e){let t=xr(e,100),n=await f(e),s=D("the conversation's id",e.args[0]),a=await h(e,n,"GET",`/messaging/admin/conversations/${s}/messages${Mr({limit:t,cursor:e.flags.cursor})}`);Ig(e),e.out.result(a,()=>{a.data.length===0&&e.out.line("The conversation has said nothing yet.");for(let r of a.data){let i=r.deleted?`(removed${r.removed_reason?`: ${r.removed_reason}`:""})`:r.kind==="system"?`(${r.system?.type??"system"})`:r.text!==null?A(r.text):`(${r.kind})`;e.out.line(`#${r.seq} ${Ut(r.created_at)} ${On(r.sender_id,"the server")} ${r.id} ${i}`),!r.deleted&&r.attachments.length>0&&e.out.line(` files: ${Lg(r.attachments)}`)}Lr(e,a.next_cursor,`messaging:messages:list ${e.args[0]}`)})}},P$={name:"messaging:messages:delete",summary:"remove a message for everyone in its conversation: what it said and its files go (in the access log)",usage:"<conversation id> <message id>",details:["It is not undone: asked before it is done, whatever the environment (-y confirms)."],args:{min:2,max:2},scope:"environment",async run(e){let[t,n]=e.args,s=D("the conversation's id",t),a=D("the message's id",n),r=await f(e);await v(e,r,`remove the message ${n} of the conversation ${t} for everyone`,"It removes what the message said for everyone in the conversation, and it is not undone"),await h(e,r,"DELETE",`/messaging/admin/conversations/${s}/messages/${a}`),e.out.result({removed:{conversation_id:t,message_id:n}},()=>e.out.line(`Removed the message ${n} for everyone in ${N(r)}; the removal is in the access log.`))}},Dg=[w$,v$,_$,k$,S$,$$,P$];function A$(e){let t=new URLSearchParams;for(let[s,a]of Object.entries(e))a!==void 0&&t.set(s,String(a));let n=t.toString();return n===""?"":`?${n}`}function R$(e,t){let n=te(e.flags.limit,"limit");if(n!==void 0&&(n<1||n>t))throw m(`--limit is a whole number from 1 to ${t}`);return n}function T$(e,t){return{limit:{type:"string",value:"n",description:`at most this many (1\u2013${e}, default ${t})`},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}}}var Dr=e=>e?e.slice(0,19).replace("T"," "):"";function E$(e,t,n){t&&e.out.line(`More: palbase ${n} --cursor ${t}`)}function qo(e){return!e.answered_at||!e.ended_at?"":String(Math.max(0,Math.round((Date.parse(e.ended_at)-Date.parse(e.answered_at))/1e3)))}var Ng=e=>e.video?"video":"voice";function C$(e){if(e<1e3)return`${e} B`;let t=["kB","MB","GB","TB"],n=e/1e3,s=0;for(;n>=1e3&&s<t.length-1;)n/=1e3,s+=1;return`${n>=100?Math.round(n):Math.round(n*10)/10} ${t[s]}`}var j$=["ringing","active","ended"],O$={name:"calls:list",summary:"the project's calls, or a person's with --user, newest first: kind, voice or video, state, why it ended, who called, how many were in it, how long",usage:"[--user <user id> [--missed]] [--state ringing|active|ended] [--limit <n>] [--cursor <cursor>]",details:["A call's length is from its answer to its end, in seconds: a group call, which no one answers, has its people's seconds in calls:get."],flags:{user:{type:"string",value:"user id",description:"only this person's calls"},missed:{type:"boolean",description:"with --user: only the calls they missed (or were busy for)"},state:{type:"string",value:"state",description:"only the calls ringing, going on (active), or over (ended)"},...T$(100,20)},scope:"environment",async run(e){let t=e.flags.user;if(t!==void 0&&t.trim()==="")throw m("--user is a person's id");let n=e.flags.missed===!0;if(n&&t===void 0)throw m("--missed says whose: give --user with it");let s=e.flags.state;if(s!==void 0&&!j$.includes(s))throw m("--state is ringing, active or ended");let a=R$(e,100),r=await f(e),i=await h(e,r,"GET",`/calls/admin/calls${A$({user_id:t,missed:n?"true":void 0,state:s,limit:a,cursor:e.flags.cursor})}`);e.out.result(i,()=>{i.calls.length===0&&e.out.line(`No ${n?"missed ":""}${s?`${s} `:""}call${t!==void 0?` of ${t}`:n||s?"":" yet"}.`),e.out.table(i.calls,[{header:"call",value:l=>l.id},{header:"started",value:l=>Dr(l.created_at)},{header:"kind",value:l=>l.kind},{header:"media",value:Ng},{header:"state",value:l=>l.state},{header:"end reason",value:l=>l.end_reason??""},{header:"caller",value:l=>l.caller_id},{header:"people",value:l=>String(l.participants.length)},{header:"seconds",value:qo}]);let o=[t!==void 0?` --user ${t}`:"",n?" --missed":"",s?` --state ${s}`:""].join("");E$(e,i.next_cursor,`calls:list${o}`)})}},M$={name:"calls:get",summary:"one call with its people and bots: how each was in it (rung, joined, left, declined, missed, busy), for how many seconds, and what the media server sent them",usage:"<call id>",details:["Every call's media goes through the platform's media server, and a call is billed by the data it sent the call's people: each one's, in bytes, is as its meter last told it (up to a minute behind).","--json carries the call whole: its people's names and pictures, the caller's app's words about it (metadata), each one's bytes."],args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET",`/calls/admin/calls/${D("the call's id",e.args[0])}`);e.out.result(n,()=>{e.out.line(`call ${n.id}`),e.out.line(`kind ${n.kind}, ${Ng(n)}${n.ring?"":", rings no one"}`),e.out.line(`state ${n.state}${n.end_reason?`: ${n.end_reason}${n.ended_by?` by ${n.ended_by}`:""}`:""}`),e.out.line(`caller ${n.caller_id}`),e.out.line(`started ${Dr(n.created_at)}`),n.answered_at&&e.out.line(`answered ${Dr(n.answered_at)}`),n.ended_at&&e.out.line(`ended ${Dr(n.ended_at)}${qo(n)?`, after ${qo(n)} s`:""}`),n.conversation_id&&e.out.line(`conversation ${n.conversation_id}`),e.out.line(`holds at most ${n.max_participants}`),e.out.line(),e.out.table(n.participants,[{header:"user",value:s=>s.user_id},{header:"kind",value:s=>s.kind},{header:"role",value:s=>s.role},{header:"state",value:s=>s.state},{header:"seconds",value:s=>String(s.seconds)},{header:"data",value:s=>C$(s.bytes)}])})}},x$={name:"calls:end",summary:"end a call for everyone in it (asked before it is done, whatever the environment)",usage:"<call id>",details:["It is not undone: asked before it is done, whatever the environment (-y confirms). A call already over is left as it is."],args:{min:1,max:1},scope:"environment",async run(e){let t=D("the call's id",e.args[0]),n=await f(e);await v(e,n,`end the call ${e.args[0]} for everyone in it`,"It ends the call for everyone in it, and it is not undone");let s=await h(e,n,"POST",`/calls/admin/calls/${t}/end`);e.out.result(s,()=>e.out.line(s.end_reason&&s.end_reason!=="ended"?`The call ${s.id} was already over: ${s.end_reason}.`:`Ended the call ${s.id} for everyone in it in ${N(n)}.`))}},qg=[O$,M$,x$];import{writeFile as I$}from"node:fs/promises";var L$=/^[A-Z][A-Z0-9_]{0,127}$/,hs={dev:{type:"boolean",description:"the development set (what a backend on a developer's computer reads), not production"}};function gs(e){return e.flags.dev===!0?"development":"production"}function da(e){let t=e.args[0];if(!L$.test(t))throw m(`${JSON.stringify(t)} is not a secret's name: capital letters, digits and _, a letter first`);return t}async function Ko(e,t,n){let s=[],a;do{let r=await h(e,t,"GET",`/secrets/${n}?limit=1000${a?`&cursor=${encodeURIComponent(a)}`:""}`);s.push(...r.secrets),a=r.next_cursor}while(a);return s}var D$={name:"secrets:list",summary:"the environment's secrets by name, with their version \u2014 never their values",usage:"[--dev]",flags:hs,scope:"environment",async run(e){let t=await f(e),n=gs(e),s=await Ko(e,t,n);e.out.result({set:n,secrets:s},()=>{s.length===0&&e.out.line(`No ${n} secrets.`);for(let a of s)e.out.line(`${a.name} v${a.version} ${a.updated_at}`)})}},N$={name:"secrets:set",summary:"set a secret: its value is read from stdin, never from the command line",usage:"<NAME> [--dev] < value",details:["The environment's machines read production at their next start; a backend run with palbase run or env:pull reads development."],args:{min:1,max:1},flags:hs,scope:"environment",async run(e){let t=await f(e),n=da(e),s=gs(e),a=await Me(e,`the value of ${n}`);await v(e,t,`set the ${s} secret ${n}`);let r=await h(e,t,"PUT",`/secrets/${s}/${n}`,{value:a});e.out.result({set:s,name:n,version:r.version},()=>e.out.line(`Set ${n} (${s}, v${r.version}) in ${N(t)}.`))}},q$={name:"secrets:unset",summary:"take a secret away",usage:"<NAME> [--dev]",args:{min:1,max:1},flags:hs,scope:"environment",async run(e){let t=await f(e),n=da(e),s=gs(e);await v(e,t,`take away the ${s} secret ${n}`),await h(e,t,"DELETE",`/secrets/${s}/${n}`),e.out.result({set:s,name:n,deleted:!0},()=>e.out.line(`Took ${n} (${s}) away from ${N(t)}.`))}},K$={name:"secrets:get",summary:"print one secret's value (an admin's)",usage:"<NAME> [--dev]",args:{min:1,max:1},flags:hs,scope:"environment",async run(e){let t=await f(e),n=da(e),s=await h(e,t,"GET",`/secrets/${gs(e)}/${n}`);e.out.result({name:n,version:s.version,value:s.value},()=>e.out.line(s.value))}},G$={name:"secrets:versions",summary:"a secret's versions, newest first \u2014 never their values",usage:"<NAME> [--dev]",args:{min:1,max:1},flags:hs,scope:"environment",async run(e){let t=await f(e),n=da(e),s=await h(e,t,"GET",`/secrets/${gs(e)}/${n}/versions`);e.out.result(s,()=>{for(let a of s.versions)e.out.line(`v${a.version} ${a.created_at}${a.current?" (current)":""}`)})}},U$={name:"secrets:rollback",summary:"put a secret back to one of its versions (a new version with that value)",usage:"<NAME> --version <n> [--dev]",args:{min:1,max:1},flags:{...hs,version:{type:"string",value:"n",description:"the version to go back to"}},scope:"environment",async run(e){let t=await f(e),n=da(e),s=Number(e.flags.version);if(!Number.isInteger(s)||s<1)throw m("--version is a version number (secrets:versions lists them)");let a=gs(e);await v(e,t,`put the ${a} secret ${n} back to v${s}`);let r=await h(e,t,"POST",`/secrets/${a}/${n}/rollback`,{version:s});e.out.result({set:a,name:n,version:r.version},()=>e.out.line(`${n} (${a}) is v${s}'s value again, as v${r.version}.`))}},z$={name:"secrets:audit",summary:"every read and write of the environment's secrets, newest first: which secret, which key did it, when \u2014 never a value",usage:"[--name <NAME>] [--dev | --prod] [--limit <n>]",details:["A backend that reads the secrets at boot (palbase run, a machine) shows here as a read, with the key it read with: palbase keys:list names the key.","An encrypted column's values opened through Palbase (Studio's table, a secret key) are recorded under the column's address: --name patients.tc lists who opened them, and when."],flags:{name:{type:"string",value:"NAME",description:"only this secret's, or an encrypted field's opens (table.column)"},dev:{type:"boolean",description:"only the development secrets"},prod:{type:"boolean",description:"only the production secrets"},limit:{type:"string",value:"n",description:"how many (default 30)"}},scope:"environment",async run(e){let t=await f(e);if(e.flags.dev===!0&&e.flags.prod===!0)throw m("--dev or --prod, not both");let n=Number(e.flags.limit??30);if(!Number.isInteger(n)||n<1||n>500)throw m("--limit is a whole number from 1 to 500");let s=new URLSearchParams({limit:String(n),...e.flags.name?{name:e.flags.name}:{},...e.flags.dev===!0?{environment:"development"}:e.flags.prod===!0?{environment:"production"}:{}}),a=await h(e,t,"GET",`/secrets/audit?${s}`);e.out.result(a,()=>{a.entries.length===0?e.out.line("No read or write of a secret yet."):e.out.table(a.entries,[{header:"when",value:r=>r.at.slice(0,19).replace("T"," ")},{header:"what",value:r=>r.action},{header:"set",value:r=>r.environment??""},{header:"secret",value:r=>r.name?`${r.name}${r.version?` v${r.version}`:""}`:"every one"},{header:"key",value:r=>r.key_id}])})}};function Kg(e,t){e.out.table(t.keys,[{header:"key",value:n=>`${n.id}${n.id===t.current?" (in force)":""}`},{header:"in force from",value:n=>n.activates_at.slice(0,19).replace("T"," ")},{header:"made by",value:n=>n.created_by}])}var B$={name:"fields:list",summary:"the keys the environment's encrypted database fields are sealed with \u2014 never the keys themselves",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/secrets/fields");e.out.result(n,()=>Kg(e,n))}},F$={name:"fields:rotate",summary:"make a new key for the environment's encrypted fields (an owner's or an admin's)",details:["It comes into force in five minutes: by then every running backend has fetched it. New values are sealed with it from then on; a palbase/backend app seals its older values again with it, in the background.","The older keys go on opening what they sealed."],scope:"environment",async run(e){let t=await f(e);await v(e,t,"rotate the encrypted fields' key");let n=await h(e,t,"POST","/secrets/fields/rotate"),s=n.keys[n.keys.length-1];e.out.result(n,()=>{e.out.line(`Made ${s.id} in ${N(t)}: in force from ${s.activates_at.slice(0,19).replace("T"," ")} UTC.`),Kg(e,n)})}},J$={name:"fields:keys",summary:"download the environment's field keys into a file (its owner's): what opens a dump of its database elsewhere",usage:"--out <file>",details:["The file opens every encrypted field of the environment: keep it as you would the database's password. It is the shape palbase/server's Fields reads: new Fields({ fetch: async () => JSON.parse(readFileSync(file, 'utf8')) }).","The download is in the secrets audit trail (secrets:audit)."],flags:{out:{type:"string",value:"file",description:"where the keys go (made readable by you alone); never the screen"}},scope:"environment",async run(e){let t=e.flags.out;if(!t)throw m("fields:keys writes the keys into a file: --out <file> (never onto the screen, which keeps them)");let n=await f(e),s=await h(e,n,"GET","/secrets/fields/keys");await I$(t,`${JSON.stringify(s,null,2)}
|
|
641
|
-
`,{mode:384,flag:"wx"}),e.out.result({file:t,keys:s.keys.map(a=>a.id)},()=>e.out.line(`Wrote the keys of ${N(n)} (${s.keys.map(a=>a.id).join(", ")}) to ${t}.`))}};function Go(e,t){e.out.table(t.keys,[{header:"key",value:n=>`${n.id}${n.id===t.current?" (in use)":""}`},{header:"made",value:n=>n.created_at.slice(0,19).replace("T"," ")},{header:"made by",value:n=>n.created_by},{header:"retired",value:n=>n.retired_at?`${n.retired_at.slice(0,19).replace("T"," ")} by ${n.retired_by}`:""}])}var W$={name:"transit:list",summary:"the keys the encrypted fields of a request to the environment's backend are sealed to \u2014 their public halves alone",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/secrets/transit");e.out.result(n,()=>Go(e,n))}},V$={name:"transit:rotate",summary:"make a new key for the encrypted fields of requests to the backend (an owner's or an admin's)",usage:"[--retire-previous]",details:["Apps seal to it from their next fetch of the project's keys (within the hour), or at once with --retire-previous: every older key is retired with it \u2014 what to do when one may have been taken \u2014 and an app that seals to a retired one is told so, fetches the keys again and sends its call once more.","No app needs a new build: an app holds the project's signing key, not its keys."],flags:{"retire-previous":{type:"boolean",description:"retire every older key at once"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["retire-previous"]===!0;await v(e,t,n?"rotate the transit keys, retiring every older one":"rotate the transit keys");let s=await h(e,t,"POST","/secrets/transit/rotate",{retire_previous:n});e.out.result(s,()=>{e.out.line(`Made ${s.current} in ${N(t)}: apps seal to it from now${n?"; every older key is retired":""}.`),Go(e,s)})}},H$={name:"transit:retire",summary:"retire a transit key that is not the one in use: no request sealed to it opens from then on",usage:"<key>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=e.args[0];if(!/^t[1-9][0-9]{0,5}$/.test(n))throw m(`${n} is not a transit key's id`,"palbase transit:list names them (t1, t2, \u2026)");await v(e,t,`retire the transit key ${n}`);let s=await h(e,t,"POST",`/secrets/transit/${n}/retire`);e.out.result(s,()=>{e.out.line(`Retired ${n} in ${N(t)}.`),Go(e,s)})}},Gg=[D$,N$,K$,q$,G$,U$,z$,B$,F$,J$,W$,V$,H$];var Y$=/^[A-Z][A-Z0-9_]{0,127}$/;function pn(e,t){return`/environments/${D("the environment's ref",e.env_ref)}/webhooks${t!==void 0?`/${D("the webhook's id",t)}`:""}`}function Ug(e){return re(e.flags.events)}function Nr(e){let t=e.machine?`, the machine ${A(e.machine)}'s \u2014 kept by the platform to what its app takes`:"",n=e.created_by?`, made by ${A(e.created_by.name)}${e.created_by.id.startsWith("tok_")?" (access token)":e.created_by.id.startsWith("etk_")?" (environment token)":""}`:"";return`${A(e.url)} \u2014 ${e.events.length===0?"every event":A(e.events.join(", "))} (${A(e.id)}${t}${n})`}function X$(e,t){let n=t.find(a=>a.id===e.endpoint_id)?.url??`${e.endpoint_id} (no longer an endpoint)`,s=e.redelivered_at?`; redelivered ${A(e.redelivered_at)}`:e.redeliver_asked_at?"; redelivery asked":e.redelivery_error?`; redelivery failed: ${A(e.redelivery_error)}`:"";return`${A(e.id)} ${A(e.event_type)} ${A(e.event_id)} \u2192 ${A(n)} \u2014 ${A(e.last_error)} (${A(String(e.attempts))} tries, given up ${A(e.failed_at)})${s}`}var Z$={name:"webhooks:list",summary:"the environment's webhook endpoints: where its events go, which (never their secrets)",scope:"environment",async run(e){let t=await f(e),{endpoints:n}=await e.studio.get(pn(t));e.out.result({endpoints:n},()=>{n.length===0&&e.out.line("No webhook endpoint yet: palbase webhooks:create <url> makes one.");for(let s of n)e.out.line(Nr(s))})}},Q$={name:"webhooks:create",summary:"add a webhook endpoint; its signing secret is printed this once, or kept in a secret with --secret-to",usage:"<url> [--events <a,b>] [--secret-to <NAME> [--dev]]",details:["A machine is given its own endpoint and its secret (PALBASE_WEBHOOK_SECRET) by the platform, for the events its app takes: this is for another receiver. --secret-to keeps the secret in the environment's secrets (production; --dev: development) and prints it nowhere."],args:{min:1,max:1},flags:{events:{type:"string",value:"list",description:"the events it is sent, comma-separated (user.deleted, \u2026); none: every event"},"secret-to":{type:"string",value:"NAME",description:"keep its signing secret in the environment's secret NAME instead of printing it"},dev:{type:"boolean",description:"with --secret-to: the development secrets, not production"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["secret-to"];if(n!==void 0&&!Y$.test(n))throw m(`--secret-to ${JSON.stringify(n)} is not a secret's name: capital letters, digits and _, a letter first`);if(e.flags.dev===!0&&n===void 0)throw m("--dev goes with --secret-to <NAME>");let s=e.flags.dev===!0?"development":"production";await v(e,t,`add the webhook endpoint ${e.args[0]}${n?`, its secret kept in ${n} (${s})`:""}`);let a=await e.studio.post(pn(t),{url:e.args[0],events:Ug(e)??[]}),{secret:r,...i}=a;if(n!==void 0){let o=await h(e,t,"PUT",`/secrets/${s}/${n}`,{value:r});e.out.result({endpoint:i,secret_to:{set:s,name:n,version:o.version}},()=>e.out.line(`Added ${Nr(i)}; its secret is ${n} (${s}, v${o.version}) in ${N(t)}.`));return}e.out.result(a,()=>{e.out.line(`Added ${Nr(i)}.`),e.out.line(`signing secret (shown this once): ${r}`)})}},eP={name:"webhooks:update",summary:"change a webhook endpoint's address or events (its secret stays)",usage:'<id> [--url <url>] [--events <a,b>|""]',args:{min:1,max:1},flags:{url:{type:"string",value:"url",description:"where it is delivered now"},events:{type:"string",value:"list",description:'the events it is sent now, comma-separated; "" for every event'}},scope:"environment",async run(e){let t=await f(e);if(e.flags.url===void 0&&e.flags.events===void 0)throw m("nothing to change: --url or --events");let{endpoints:n}=await e.studio.get(pn(t)),s=n.find(r=>r.id===e.args[0]);if(!s)throw m(`the environment has no webhook endpoint ${JSON.stringify(e.args[0])}`,"palbase webhooks:list shows them");await v(e,t,`change the webhook endpoint ${s.url}`);let a=await e.studio.request("PUT",pn(t,s.id),{url:e.flags.url??s.url,events:Ug(e)??s.events});e.out.result(a,()=>e.out.line(`Changed: ${Nr(a)}.`))}},tP={name:"webhooks:delete",summary:"remove a webhook endpoint: its events are no longer delivered there",usage:"<id>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e);await v(e,t,`remove the webhook endpoint ${e.args[0]}`),await e.studio.request("DELETE",pn(t,e.args[0])),e.out.result({deleted:e.args[0]},()=>e.out.line(`Removed the webhook endpoint ${e.args[0]}.`))}},nP={name:"webhooks:deliveries:list",summary:"the deliveries the platform gave up on after its tries, still owed (--failed), or every one kept 30 days (--all)",usage:"[--failed] [--all]",flags:{failed:{type:"boolean",description:"those it gave up on and still owes (the default)"},all:{type:"boolean",description:"every one kept, those delivered again among them"}},scope:"environment",async run(e){let t=await f(e),[{deliveries:n},{endpoints:s}]=await Promise.all([e.studio.get(`${pn(t)}/deliveries${e.flags.all===!0?"?all=true":"?failed=true"}`),e.studio.get(pn(t))]);e.out.result({deliveries:n},()=>{n.length===0&&e.out.line("No failed delivery: every event reached its endpoints.");for(let a of n)e.out.line(X$(a,s))})}},sP={name:"webhooks:deliveries:redeliver",summary:"send a failed delivery again: the same event, its id and body, to the endpoint it failed at, within seconds",usage:"<id>",details:["As Stripe's Resend: the receiver takes an event more than once by its Palbase-Event-Id. An admin's; it is written to the organization's audit log."],args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e);await v(e,t,`send the failed delivery ${e.args[0]} again`);let n=await e.studio.post(`${pn(t)}/deliveries/${D("the delivery's id",e.args[0])}/redeliver`,{});e.out.result(n,()=>e.out.line(`Asked: ${A(n.event_type)} ${A(n.event_id)} goes to its endpoint again within seconds (palbase webhooks:deliveries:list --all shows when it went).`))}},zg=[Z$,Q$,eP,tP,nP,sP];import{constants as qr,createReadStream as aP}from"node:fs";import{mkdir as rP,open as iP,readdir as oP,realpath as Bg,stat as Vg}from"node:fs/promises";import{dirname as Fg,extname as lP,join as Hg,relative as pP,resolve as Jg,sep as Kr}from"node:path";import{Readable as Yg}from"node:stream";import{pipeline as dP}from"node:stream/promises";var Xg=600;function ua(e){let t=/^([a-z0-9][a-z0-9_-]{0,62}):(.*)$/.exec(e);return!t||e.startsWith("/")||e.startsWith(".")?null:{bucket:t[1],path:t[2].replace(/^\/+/,"")}}function Gr(e){if(e.split("/").some(n=>n===""||n==="."||n===".."))throw new y("unsafe_path",`the object ${JSON.stringify(e)} has a name no request can carry`);return encodeURIComponent(e)}async function Uo(e,t,n,s){let a=[],r;do{let i=new URLSearchParams({limit:"1000",...s?{prefix:s}:{},...r?{cursor:r}:{}}),o=await h(e,t,"GET",`/storage/buckets/${D("the bucket",n)}/objects?${i}`);a.push(...o.objects),r=o.next_cursor}while(r);return a}async function Zg(e,t,n,s){let a;try{a=await e.io.fetch(t,{...n,redirect:"manual"})}catch(r){throw new y("network_error",`${s}: ${r instanceof Error?r.message:String(r)}`)}if(!a.ok){let r=await a.json().catch(()=>({}));throw new y(r.error??`http_${a.status}`,`${s}: ${r.error_description??r.error??`answered ${a.status}`}`)}return a}var uP={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".heic":"image/heic",".heif":"image/heif",".avif":"image/avif",".svg":"image/svg+xml",".ico":"image/x-icon",".pdf":"application/pdf",".json":"application/json",".txt":"text/plain",".csv":"text/csv",".html":"text/html",".css":"text/css",".js":"text/javascript",".xml":"application/xml",".zip":"application/zip",".mp4":"video/mp4",".mov":"video/quicktime",".webm":"video/webm",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4"};function Wg(e){return uP[lP(e).toLowerCase()]}async function zo(e,t,n,s,a,r){let i=(await Vg(n)).size,o=await h(e,t,"POST",`/storage/buckets/${D("the bucket",s)}/upload-urls`,{path:a,expires_in:Xg,...r?{owner_id:r}:{}});return await Zg(e,o.url,{method:"PUT",body:Yg.toWeb(aP(n)),headers:{"content-length":String(i),...Wg(n)?{"content-type":Wg(n)}:{}},duplex:"half"},`${n} \u2192 ${s}:${a}`),i}function cP(e,t){let n=t.split("/");if(t===""||n.some(a=>a===""||a==="."||a===".."||a.includes("\\")||a.includes("\0")))throw new y("unsafe_path",`the object ${JSON.stringify(t)} has a name no file here can take`);let s=Hg(e,...n);if(!s.startsWith(e+Kr))throw new y("unsafe_path",`${t} would land outside ${e}`);return s}async function hP(e,t,n,s,a,r){let i=await h(e,t,"POST",`/storage/buckets/${D("the bucket",n)}/signed-urls`,{path:s,expires_in:Xg}),o=await Zg(e,i.url,{method:"GET"},`${n}:${s} \u2192 ${a}`);if(await rP(Fg(a),{recursive:!0}),r!==void 0){let[g,b]=await Promise.all([Bg(r),Bg(Fg(a))]);if(b!==g&&!b.startsWith(g+Kr))throw new y("unsafe_path",`${s} would land outside ${r} (through a link)`)}let d=(await iP(a,qr.O_WRONLY|qr.O_CREAT|qr.O_TRUNC|qr.O_NOFOLLOW)).createWriteStream();if(!o.body)return await new Promise((g,b)=>d.on("error",b).end(g)),0;let p=0,u=Yg.fromWeb(o.body).on("data",g=>{p+=g.length});return await dP(u,d),p}async function Qg(e){let t=[];for(let n of await oP(e,{withFileTypes:!0})){let s=Hg(e,n.name);n.isDirectory()?t.push(...await Qg(s)):n.isFile()&&t.push(s)}return t}var gP={name:"storage:ls",summary:"a bucket's objects, or those under a prefix: path, size, type",usage:"<bucket>[:prefix]",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0].includes(":")?e.args[0]:`${e.args[0]}:`);if(!n)throw m(`${e.args[0]} is not a bucket (bucket or bucket:prefix)`);let s=await Uo(e,t,n.bucket,n.path);e.out.result({bucket:n.bucket,objects:s},()=>{s.length===0&&e.out.line(`Nothing in ${n.bucket}:${n.path}`);for(let a of s)e.out.line(`${a.path} ${a.size} ${a.content_type??""}`)})}},mP={name:"storage:cp",summary:"copy files between this computer and a bucket: a file, or a folder whole with -r",usage:"<from> <to> [-r] [--owner <user id>]",details:["bucket:path names an object of the environment; anything else is a path on this computer.","Up: a file to bucket:path (a path ending in / keeps the file name); with -r, a folder to bucket:prefix, its paths kept.","Down: bucket:path to a file; with -r, every object under bucket:prefix into a folder."],args:{min:2,max:2},flags:{recursive:{type:"boolean",short:"r",description:"a folder (up) or a prefix (down), whole"},owner:{type:"string",value:"user id",description:"the user uploaded objects belong to (deleted with them)"}},scope:"environment",async run(e){let t=await f(e),[n,s]=e.args,a=ua(n),r=ua(s),i=e.flags.recursive===!0,o=e.flags.owner;if(a&&r)throw m("one side is a bucket and the other this computer (bucket-to-bucket: the storage API copies within a bucket)");if(!a&&!r)throw m("one side is bucket:path");let l=0,d=0;if(r){let p=Jg(e.io.cwd,n),u=i?await Qg(p):[p];if(!i&&(await Vg(p)).isDirectory())throw m(`${n} is a folder: copy it with -r`);await v(e,t,`upload ${u.length} file${u.length===1?"":"s"} to ${r.bucket}:${r.path} in ${N(t)}`);for(let g of u){let b=i?pP(p,g).split(Kr).join("/"):g.split(Kr).pop(),w=i||r.path===""||r.path.endsWith("/")?`${r.path.replace(/\/?$/,r.path?"/":"")}${b}`:r.path;d+=await zo(e,t,g,r.bucket,w,o),l++,u.length>1&&e.out.note(`${l}/${u.length} ${w}`)}}else if(a){let p=Jg(e.io.cwd,s),u=i?await Uo(e,t,a.bucket,a.path):[{path:a.path,size:0}];for(let g of u){let b=i?g.path.slice(a.path.length).replace(/^\/+/,""):"",w=i?cP(p,b):p;d+=await hP(e,t,a.bucket,g.path,w,i?p:void 0),l++,u.length>1&&e.out.note(`${l}/${u.length} ${g.path}`)}}e.out.result({copied:l,bytes:d},()=>e.out.line(`Copied ${l} file${l===1?"":"s"} (${d} bytes).`))}},fP={name:"storage:rm",summary:"delete an object, or every object under a prefix with -r",usage:"<bucket:path> [-r]",args:{min:1,max:1},flags:{recursive:{type:"boolean",short:"r",description:"every object under the prefix"}},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0]);if(!n||!n.path&&e.flags.recursive!==!0)throw m(`${e.args[0]} is not bucket:path`);let s=e.flags.recursive===!0?(await Uo(e,t,n.bucket,n.path)).map(a=>a.path):[n.path];await v(e,t,`delete ${s.length} object${s.length===1?"":"s"} from ${n.bucket} in ${N(t)}`);for(let a of s)await h(e,t,"DELETE",`/storage/buckets/${D("the bucket",n.bucket)}/objects/${Gr(a)}`);e.out.result({deleted:s.length},()=>e.out.line(`Deleted ${s.length} object${s.length===1?"":"s"}.`))}},yP={name:"storage:url",summary:"a signed download URL for one object",usage:"<bucket:path> [--expires <seconds>]",args:{min:1,max:1},flags:{expires:{type:"string",value:"seconds",description:"how long it works (default 3600, at most 604800)"}},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0]);if(!n||!n.path)throw m(`${e.args[0]} is not bucket:path`);let s=e.flags.expires===void 0?3600:Number(e.flags.expires);if(!Number.isInteger(s)||s<1||s>604800)throw m("--expires is 1 to 604800 seconds");let a=await h(e,t,"POST",`/storage/buckets/${D("the bucket",n.bucket)}/signed-urls`,{path:n.path,expires_in:s});e.out.result(a,()=>e.out.line(a.url))}},em=[gP,mP,fP,yP];import{readFile as bP}from"node:fs/promises";import{resolve as wP}from"node:path";var Wo=/^[a-z][a-z0-9_-]{0,63}$/,tm=["backend","client","https","mcp","platform"];function dn(e,t,n=0){let s=e.args[n];if(!Wo.test(s))throw m(`${JSON.stringify(s)} is not ${t}'s name: lowercase letters, digits, _ and -, a letter first, at most 64`);return s}function jt(e,t){let n=te(e,t);if(n===0)throw m(`--${t} is a whole number from 1`);return n}function un(e){let t=new URLSearchParams;for(let[s,a]of Object.entries(e))a!==void 0&&t.set(s,String(a));let n=t.toString();return n===""?"":`?${n}`}async function nm(e,t,n){let s;try{s=await bP(wP(e.io.cwd,t),"utf8")}catch(a){throw new y("unreadable_file",`${t} could not be read: ${a instanceof Error?a.message:String(a)}`)}return ue(s,n)}var Ur=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),zr=e=>`$${e<.01&&e>0?e.toPrecision(2):e.toFixed(e<1?4:2)}`,Br=e=>`$${e.toFixed(2)}`,sm="or connect the environment's own key for the model's provider, which its provider bills: palbase ai:providers:set <provider>",Bo=`raise the budget in Studio \u203A AI \u203A Settings or with palbase ai:settings:set --monthly-budget <usd>; ${sm}`,Fo=`move the environment to Pay as you go in Studio \u203A Billing or with palbase billing:env --plan pay_as_you_go (its organization paid for by a payment account with a card, not held); ${sm}`;function am(e){if(!(e instanceof j))return e;switch(e.code){case"ai_budget_reached":return new y(e.code,"the environment's monthly AI budget is spent: Palbase's models serve it again when the month turns (UTC) or the budget is raised",{hint:Bo});case"ai_payment_required":return new y(e.code,"the month's free AI dollar is spent and the environment has nothing to pay for Palbase's models with: it is on Free, or its payment account is held",{hint:Fo});case"provider_key_required":return new y(e.code,"Palbase's models do not serve the environment, and it has no key of its own for the model's provider",{hint:"connect one: palbase ai:providers:set <provider> (palbase ai:spend says why)"});case"billing_unavailable":return new y(e.code,`Palbase's billing did not say whether its models serve the environment${e.retryAfter!==void 0?`: try again in ${e.retryAfter} s`:"; try again shortly"}`);default:return e}}function vP(e){if(e===void 0)return;let t={};for(let n of Array.isArray(e)?e:[e]){let s=String(n),a=s.indexOf("=");if(a<=0)throw m(`--metadata is key=value, not ${JSON.stringify(s)}`);Object.defineProperty(t,s.slice(0,a),{value:s.slice(a+1),enumerable:!0,writable:!0,configurable:!0})}return t}var _P={name:"ai:usage",summary:"the environment's uses of models, newest first: what each was, what it cost, and whose key it went out with",usage:"[--kind text|embedding|image] [--metadata key=value]\u2026 [--limit <n>] [--cursor <cursor>]",flags:{kind:{type:"string",value:"kind",description:"only uses of this kind: text, embedding or image"},metadata:{type:"string",multiple:!0,value:"key=value",description:"only uses carrying this label (repeat for several): a product's spend, a task's"},limit:{type:"string",value:"n",description:"at most this many (1\u2013500, default 50)"},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}},scope:"environment",async run(e){let t=e.flags.kind;if(t!==void 0&&!["text","embedding","image"].includes(t))throw m("--kind is text, embedding or image");let n=vP(e.flags.metadata),s=jt(e.flags.limit,"limit"),a=await f(e),r=await h(e,a,"GET",`/ai/usage${un({kind:t,limit:s,cursor:e.flags.cursor,metadata:n&&JSON.stringify(n)})}`);e.out.result(r,()=>{r.usage.length===0&&e.out.line("No use of a model yet."),e.out.table(r.usage,[{header:"when",value:i=>i.created_at},{header:"kind",value:i=>i.kind},{header:"model",value:i=>i.model},{header:"tokens",value:i=>`${i.kind==="image"?`${i.items} picture(s)`:`${i.input_tokens} in / ${i.output_tokens} out`}${i.estimated?" (estimate)":""}`},{header:"cost",value:i=>zr(i.cost_nano_usd/1e9)},{header:"key",value:i=>i.connection_owner==="project"?"project's":"platform's"},{header:"labels",value:i=>Object.entries(i.metadata??{}).map(([o,l])=>`${o}=${l}`).join(" ")}]),r.usage.some(i=>i.estimated)&&e.out.line("(estimate): a reasoning call cut before its answer \u2014 its provider never said what its hidden thinking came to, so it is billed as the time the model thought at its measured speed, rounded down."),r.next_cursor&&e.out.line(`More: palbase ai:usage --cursor ${r.next_cursor}`)})}},kP={name:"ai:models",summary:"the models the environment may name \u2014 every text model with its rungs, window and price, and the embedding and picture models",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/models");e.out.result(n,()=>{e.out.line("Roles (an agent names one to get the model Palbase picks for it now):"),e.out.table(n.text,[{header:"role",value:s=>s.role},{header:"what it is for",value:s=>s.description}]),e.out.line(),e.out.line("Text models (an agent's or an answer's model by its id):"),n.chat.length===0?e.out.line("None listed for the environment just now: name a role, or connect a provider's key (ai:providers:set) to list its models."):e.out.table(n.chat,[{header:"id",value:s=>`${s.id}${s.default?" (default)":""}`},{header:"roles",value:s=>s.roles.join(",")},{header:"efforts",value:s=>s.efforts.join(",")||"-"},{header:"window",value:s=>String(s.context_window)},{header:"$/M in",value:s=>String(s.price.input_usd_per_mtok)},{header:"$/M out",value:s=>String(s.price.output_usd_per_mtok)}]),e.out.line(),e.out.line("Embedding models:"),e.out.table(n.embeddings,[{header:"model",value:s=>s.model},{header:"role",value:s=>s.role||"-"},{header:"dimensions",value:s=>String(s.dimensions)},{header:"$/M in",value:s=>String(s.input_usd_per_mtok)}]),e.out.line(),e.out.line("Picture models:"),e.out.table(n.images,[{header:"model",value:s=>s.model},{header:"role",value:s=>s.role||"-"},{header:"$/M picture out",value:s=>String(s.image_output_usd_per_mtok)}]),n.unavailable.length>0&&e.out.line(`Not listed just now (their models are missing until they answer): ${n.unavailable.join(", ")}`)})}},SP={free:"Free \u2014 its free dollar a month, then its own provider keys",pay_as_you_go:"Pay as you go \u2014 past the free dollar, the account's balance pays: the provider's price and 10%",exempt:"exempt \u2014 measured, not charged"},$P={name:"ai:spend",summary:"what Palbase's models cost the environment this month: the free dollar and what is left of it, the monthly budget, and whether they serve it now \u2014 and if not, why",details:["Palbase's models are the platform's provider keys. A month brings a dollar of them free: a Free environment's own, a paying account's once for all its projects. Past it a Free environment uses its own provider keys (ai:providers:set); a Pay as you go one pays from the account's balance, at the provider's price and 10%.","Its own provider keys always serve it, and are not counted here: their provider bills them."],scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/spend");e.out.result(n,()=>{if(e.out.line(`month ${n.month} (UTC; starts again ${n.resets_on})`),e.out.line(`plan ${SP[n.plan]??n.plan}`),e.out.line(`spent ${zr(n.spent_usd)} on Palbase's models, at what it is billed, before the free dollar`),e.out.line(`free ${Br(n.free_usd)} a month, the environment's own${n.free_left_usd===null?"":` \u2014 ${zr(n.free_left_usd)} left`}`),e.out.line(`monthly budget ${n.monthly_budget_usd===null?"none":Br(n.monthly_budget_usd)}`),n.platform_keys){e.out.line("Palbase's models serve the environment now");return}n.reason==="budget"?(e.out.line(`Palbase's models do not serve the environment: its monthly budget is spent \u2014 until ${n.resets_on}, or until it is raised`),e.out.line(` ${Bo[0].toUpperCase()}${Bo.slice(1)}.`)):n.reason==="payment"?(e.out.line("Palbase's models do not serve the environment: the free dollar is spent and the account has no balance to pay"),e.out.line(` ${Fo[0].toUpperCase()}${Fo.slice(1)}.`)):(e.out.line("Palbase's models do not serve the environment"),e.out.line(" Connect the environment's own key for a provider: palbase ai:providers:set <provider>."))})}},PP={name:"ai:providers:list",summary:"the providers the environment calls with its own key (never the keys): its calls go out with them instead of the platform's",scope:"environment",async run(e){let t=await f(e),{providers:n}=await h(e,t,"GET","/ai/providers");e.out.result({providers:n},()=>{n.length===0&&e.out.line("No key of the environment's own: its calls go out with the platform's."),e.out.table(n,[{header:"provider",value:s=>s.name},{header:"verified",value:s=>s.verified?"yes":"no"},{header:"key",value:()=>"set"}])})}},AP={name:"ai:providers:set",summary:"call a provider with the environment's own key: the key is piped in or typed unseen, never shown or put on the command line",usage:"<provider> [--key-stdin] < key.txt",details:["Without --key-stdin the key is asked for at the terminal, and not echoed as it is typed or pasted."],args:{min:1,max:1},flags:{"key-stdin":{type:"boolean",description:"read the key from stdin (a pipe, a file)"}},scope:"environment",async run(e){let t=e.args[0];if(!/^[a-z][a-z0-9-]{0,31}$/.test(t))throw m(`${JSON.stringify(t)} is not a provider's name (openai, anthropic, google\u2026)`);if(e.flags["key-stdin"]!==!0&&!e.interactive)throw m(`${t}'s key is read from stdin`,`pipe it in: palbase ai:providers:set ${t} --key-stdin < key.txt`);let n=await f(e),s=await Me(e,`${t}'s key`);await v(e,n,`call ${t} with the environment's own key`);let a=await h(e,n,"PUT",`/ai/providers/${k(t)}`,{key:s});e.out.result(a,()=>e.out.line(`${a.name}'s calls go out with the environment's own key from now on${a.verified?"":" (the provider did not confirm it: check the key)"}; it is kept sealed.`))}},RP={name:"ai:providers:remove",summary:"forget the environment's own key for a provider: its calls go out with the platform's again",usage:"<provider>",args:{min:1,max:1},scope:"environment",async run(e){let t=e.args[0],n=await f(e);await v(e,n,`forget the environment's own ${t} key`),await h(e,n,"DELETE",`/ai/providers/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`${t}'s calls go out with the platform's key again.`))}};function rm(e,t){e.out.line(`clients ${t.clients?"on: the app's users talk to the agents with their own session":"off: the server starts and writes every chat"}`),e.out.line(`messages per minute ${t.user_messages_per_minute} a user`),e.out.line(`history kept ${t.history_bytes} bytes of ${t.history_limit_bytes===null?"no fixed limit":`${t.history_limit_bytes} at most`}`);let n=t.monthly_budget_usd??null;e.out.line(`monthly budget ${n===null?"none: Palbase's models are not capped":`${Br(n)} a month on Palbase's models`}`)}function TP(e){if(e===void 0)return;let t=String(e);if(t==="none")return null;let n=/^\d{1,7}(\.\d{1,2})?$/.test(t)?Number(t):NaN;if(!(n>=.01&&n<=1e6))throw m(`--monthly-budget is US dollars from 0.01 to 1000000, cents at most, or none \u2014 not ${JSON.stringify(t)}`,"palbase ai:settings:set --monthly-budget 25");return n}var EP={name:"ai:settings:get",summary:"what the environment allows its chats \u2014 whether the app's users talk to the agents, how often, how much history is kept \u2014 and its monthly AI budget",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/settings");e.out.result(n,()=>rm(e,n))}},CP={name:"ai:settings:set",summary:"change what the environment allows its chats, and its monthly AI budget: a setting left out is left as it is",usage:"[--clients on|off] [--messages-per-minute <n>] [--history-limit <bytes>|none] [--monthly-budget <usd>|none]",details:["--monthly-budget is the most the environment spends a month on Palbase's models (the platform's provider keys), at what it is billed: past it a call they would serve is refused (ai_budget_reached) until the month turns (UTC) or the budget is raised, and the organization's owners are emailed once. Its own provider keys are not counted: their provider bills them. palbase ai:spend says what is spent.","Only an owner or an admin of the organization sets it, as themselves: a member's change of it is refused, and so is a key's or a token's \u2014 what spends the money does not raise its own limit."],flags:{clients:{type:"string",value:"on|off",description:"the app's users talk to the agents of their chats with their own session"},"messages-per-minute":{type:"string",value:"n",description:"how many messages one user sends a minute (1\u2013600)"},"history-limit":{type:"string",value:"bytes|none",description:"the chat history kept at most, in bytes; none: the plan's"},"monthly-budget":{type:"string",value:"usd|none",description:"the most spent a month on Palbase's models, in US dollars (0.01\u20131000000, cents at most); none: no budget"}},scope:"environment",async run(e){let t={},n=e.flags.clients;if(n!==void 0){if(!["on","off"].includes(n))throw m("--clients is on or off");t.clients=n==="on"}let s=jt(e.flags["messages-per-minute"],"messages-per-minute");s!==void 0&&(t.user_messages_per_minute=s);let a=e.flags["history-limit"];a!==void 0&&(a==="none"?t.history_limit_bytes=null:t.history_limit_bytes=jt(a,"history-limit"));let r=TP(e.flags["monthly-budget"]);if(r!==void 0&&(t.monthly_budget_usd=r),Object.keys(t).length===0)throw m("nothing to change","pass --clients, --messages-per-minute, --history-limit or --monthly-budget");let i=await f(e),o=Object.keys(t).filter(p=>p!=="monthly_budget_usd"),l=[...o.length>0?[`change what the chats are allowed: ${o.join(", ")}`]:[],...r===void 0?[]:[r===null?"take away the monthly AI budget":`set the monthly AI budget to ${Br(r)}`]];await v(e,i,l.join("; "));let d;try{d=await h(e,i,"PUT","/ai/settings",t)}catch(p){throw r===void 0?p:jP(p)}e.out.result(d,()=>rm(e,d))}};function jP(e){return e instanceof j?e.status===403?new y(e.code,"only an owner or an admin of the organization sets the environment's monthly AI budget, as themselves: a member's change of it is refused, and so is a key's or a token's",{hint:"ask an owner or an admin to set it \u2014 in Studio \u203A AI \u203A Settings, or with palbase ai:settings:set --monthly-budget signed in as themselves (palbase login)"}):e.code==="no_billing"?new y(e.code,"this stack bills no AI, so it keeps no monthly AI budget: nothing was changed"):e.code==="registry_unavailable"?new y(e.code,"Palbase's billing did not answer: nothing was changed",{hint:"run the command again shortly"}):e:e}function Jo(e){return e.source==="code"?`the backend's code (machine ${e.machine??"backend"})`:e.published_version===null||e.published_version===void 0?`a draft (revision ${e.revision??"?"}), not published`:`version ${e.published_version} published${e.unpublished_changes?`, the draft (revision ${e.revision}) changed since`:""}`}var OP={name:"ai:agents:list",summary:"the environment's agents: its backend's (its build's contract names them) and those it keeps, a draft and published versions",scope:"environment",async run(e){let t=await f(e),{agents:n}=await h(e,t,"GET","/ai/agents");e.out.result({agents:n},()=>{n.length===0&&e.out.line("No agent yet: an ai.agent of the backend, or ai:agents:create <name>."),e.out.table(n,[{header:"name",value:s=>s.name},{header:"source",value:s=>s.source},{header:"published",value:s=>s.source==="code"?"-":s.published_version?`v${s.published_version}`:"no"},{header:"draft",value:s=>s.source==="code"?"-":s.unpublished_changes?`changed (r${s.revision})`:`r${s.revision}`},{header:"machine",value:s=>s.machine??"-"},{header:"description",value:s=>s.description??""}])})}};async function MP(e,t,n,s){let a;for(let r=0;r<100;r++){let i=await h(e,t,"GET",`/ai/agents/${k(n)}/versions${un({limit:100,cursor:a})}`),o=i.versions.find(l=>l.version===s);if(o)return o;if(!i.next_cursor||i.versions.every(l=>l.version<s))break;a=i.next_cursor}throw new y("not_found",`${n} has no version ${s}`,{hint:`palbase ai:agents:versions ${n}`})}function ms(e,t,n){if(e.out.line(t),n===void 0)e.out.line(" (none yet)");else for(let s of JSON.stringify(n,null,2).split(`
|
|
641
|
+
`,{mode:384,flag:"wx"}),e.out.result({file:t,keys:s.keys.map(a=>a.id)},()=>e.out.line(`Wrote the keys of ${N(n)} (${s.keys.map(a=>a.id).join(", ")}) to ${t}.`))}};function Go(e,t){e.out.table(t.keys,[{header:"key",value:n=>`${n.id}${n.id===t.current?" (in use)":""}`},{header:"made",value:n=>n.created_at.slice(0,19).replace("T"," ")},{header:"made by",value:n=>n.created_by},{header:"retired",value:n=>n.retired_at?`${n.retired_at.slice(0,19).replace("T"," ")} by ${n.retired_by}`:""}])}var W$={name:"transit:list",summary:"the keys the encrypted fields of a request to the environment's backend are sealed to \u2014 their public halves alone",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/secrets/transit");e.out.result(n,()=>Go(e,n))}},V$={name:"transit:rotate",summary:"make a new key for the encrypted fields of requests to the backend (an owner's or an admin's)",usage:"[--retire-previous]",details:["Apps seal to it from their next fetch of the project's keys (within the hour), or at once with --retire-previous: every older key is retired with it \u2014 what to do when one may have been taken \u2014 and an app that seals to a retired one is told so, fetches the keys again and sends its call once more.","No app needs a new build: an app holds the project's signing key, not its keys."],flags:{"retire-previous":{type:"boolean",description:"retire every older key at once"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["retire-previous"]===!0;await v(e,t,n?"rotate the transit keys, retiring every older one":"rotate the transit keys");let s=await h(e,t,"POST","/secrets/transit/rotate",{retire_previous:n});e.out.result(s,()=>{e.out.line(`Made ${s.current} in ${N(t)}: apps seal to it from now${n?"; every older key is retired":""}.`),Go(e,s)})}},H$={name:"transit:retire",summary:"retire a transit key that is not the one in use: no request sealed to it opens from then on",usage:"<key>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=e.args[0];if(!/^t[1-9][0-9]{0,5}$/.test(n))throw m(`${n} is not a transit key's id`,"palbase transit:list names them (t1, t2, \u2026)");await v(e,t,`retire the transit key ${n}`);let s=await h(e,t,"POST",`/secrets/transit/${n}/retire`);e.out.result(s,()=>{e.out.line(`Retired ${n} in ${N(t)}.`),Go(e,s)})}},Gg=[D$,N$,K$,q$,G$,U$,z$,B$,F$,J$,W$,V$,H$];var Y$=/^[A-Z][A-Z0-9_]{0,127}$/;function pn(e,t){return`/environments/${D("the environment's ref",e.env_ref)}/webhooks${t!==void 0?`/${D("the webhook's id",t)}`:""}`}function Ug(e){return re(e.flags.events)}function Nr(e){let t=e.machine?`, the machine ${A(e.machine)}'s \u2014 kept by the platform to what its app takes`:"",n=e.created_by?`, made by ${A(e.created_by.name)}${e.created_by.id.startsWith("tok_")?" (access token)":e.created_by.id.startsWith("etk_")?" (environment token)":""}`:"";return`${A(e.url)} \u2014 ${e.events.length===0?"every event":A(e.events.join(", "))} (${A(e.id)}${t}${n})`}function X$(e,t){let n=t.find(a=>a.id===e.endpoint_id)?.url??`${e.endpoint_id} (no longer an endpoint)`,s=e.redelivered_at?`; redelivered ${A(e.redelivered_at)}`:e.redeliver_asked_at?"; redelivery asked":e.redelivery_error?`; redelivery failed: ${A(e.redelivery_error)}`:"";return`${A(e.id)} ${A(e.event_type)} ${A(e.event_id)} \u2192 ${A(n)} \u2014 ${A(e.last_error)} (${A(String(e.attempts))} tries, given up ${A(e.failed_at)})${s}`}var Z$={name:"webhooks:list",summary:"the environment's webhook endpoints: where its events go, which (never their secrets)",scope:"environment",async run(e){let t=await f(e),{endpoints:n}=await e.studio.get(pn(t));e.out.result({endpoints:n},()=>{n.length===0&&e.out.line("No webhook endpoint yet: palbase webhooks:create <url> makes one.");for(let s of n)e.out.line(Nr(s))})}},Q$={name:"webhooks:create",summary:"add a webhook endpoint; its signing secret is printed this once, or kept in a secret with --secret-to",usage:"<url> [--events <a,b>] [--secret-to <NAME> [--dev]]",details:["A machine is given its own endpoint and its secret (PALBASE_WEBHOOK_SECRET) by the platform, for the events its app takes: this is for another receiver. --secret-to keeps the secret in the environment's secrets (production; --dev: development) and prints it nowhere."],args:{min:1,max:1},flags:{events:{type:"string",value:"list",description:"the events it is sent, comma-separated (user.deleted, \u2026); none: every event"},"secret-to":{type:"string",value:"NAME",description:"keep its signing secret in the environment's secret NAME instead of printing it"},dev:{type:"boolean",description:"with --secret-to: the development secrets, not production"}},scope:"environment",async run(e){let t=await f(e),n=e.flags["secret-to"];if(n!==void 0&&!Y$.test(n))throw m(`--secret-to ${JSON.stringify(n)} is not a secret's name: capital letters, digits and _, a letter first`);if(e.flags.dev===!0&&n===void 0)throw m("--dev goes with --secret-to <NAME>");let s=e.flags.dev===!0?"development":"production";await v(e,t,`add the webhook endpoint ${e.args[0]}${n?`, its secret kept in ${n} (${s})`:""}`);let a=await e.studio.post(pn(t),{url:e.args[0],events:Ug(e)??[]}),{secret:r,...i}=a;if(n!==void 0){let o=await h(e,t,"PUT",`/secrets/${s}/${n}`,{value:r});e.out.result({endpoint:i,secret_to:{set:s,name:n,version:o.version}},()=>e.out.line(`Added ${Nr(i)}; its secret is ${n} (${s}, v${o.version}) in ${N(t)}.`));return}e.out.result(a,()=>{e.out.line(`Added ${Nr(i)}.`),e.out.line(`signing secret (shown this once): ${r}`)})}},eP={name:"webhooks:update",summary:"change a webhook endpoint's address or events (its secret stays)",usage:'<id> [--url <url>] [--events <a,b>|""]',args:{min:1,max:1},flags:{url:{type:"string",value:"url",description:"where it is delivered now"},events:{type:"string",value:"list",description:'the events it is sent now, comma-separated; "" for every event'}},scope:"environment",async run(e){let t=await f(e);if(e.flags.url===void 0&&e.flags.events===void 0)throw m("nothing to change: --url or --events");let{endpoints:n}=await e.studio.get(pn(t)),s=n.find(r=>r.id===e.args[0]);if(!s)throw m(`the environment has no webhook endpoint ${JSON.stringify(e.args[0])}`,"palbase webhooks:list shows them");await v(e,t,`change the webhook endpoint ${s.url}`);let a=await e.studio.request("PUT",pn(t,s.id),{url:e.flags.url??s.url,events:Ug(e)??s.events});e.out.result(a,()=>e.out.line(`Changed: ${Nr(a)}.`))}},tP={name:"webhooks:delete",summary:"remove a webhook endpoint: its events are no longer delivered there",usage:"<id>",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e);await v(e,t,`remove the webhook endpoint ${e.args[0]}`),await e.studio.request("DELETE",pn(t,e.args[0])),e.out.result({deleted:e.args[0]},()=>e.out.line(`Removed the webhook endpoint ${e.args[0]}.`))}},nP={name:"webhooks:deliveries:list",summary:"the deliveries the platform gave up on after its tries, still owed (--failed), or every one kept 30 days (--all)",usage:"[--failed] [--all]",flags:{failed:{type:"boolean",description:"those it gave up on and still owes (the default)"},all:{type:"boolean",description:"every one kept, those delivered again among them"}},scope:"environment",async run(e){let t=await f(e),[{deliveries:n},{endpoints:s}]=await Promise.all([e.studio.get(`${pn(t)}/deliveries${e.flags.all===!0?"?all=true":"?failed=true"}`),e.studio.get(pn(t))]);e.out.result({deliveries:n},()=>{n.length===0&&e.out.line("No failed delivery: every event reached its endpoints.");for(let a of n)e.out.line(X$(a,s))})}},sP={name:"webhooks:deliveries:redeliver",summary:"send a failed delivery again: the same event, its id and body, to the endpoint it failed at, within seconds",usage:"<id>",details:["As Stripe's Resend: the receiver takes an event more than once by its Palbase-Event-Id. An admin's; it is written to the organization's audit log."],args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e);await v(e,t,`send the failed delivery ${e.args[0]} again`);let n=await e.studio.post(`${pn(t)}/deliveries/${D("the delivery's id",e.args[0])}/redeliver`,{});e.out.result(n,()=>e.out.line(`Asked: ${A(n.event_type)} ${A(n.event_id)} goes to its endpoint again within seconds (palbase webhooks:deliveries:list --all shows when it went).`))}},zg=[Z$,Q$,eP,tP,nP,sP];import{constants as qr,createReadStream as aP}from"node:fs";import{mkdir as rP,open as iP,readdir as oP,realpath as Bg,stat as Vg}from"node:fs/promises";import{dirname as Fg,extname as lP,join as Hg,relative as pP,resolve as Jg,sep as Kr}from"node:path";import{Readable as Yg}from"node:stream";import{pipeline as dP}from"node:stream/promises";var Xg=600;function ua(e){let t=/^([a-z0-9][a-z0-9_-]{0,62}):(.*)$/.exec(e);return!t||e.startsWith("/")||e.startsWith(".")?null:{bucket:t[1],path:t[2].replace(/^\/+/,"")}}function Gr(e){if(e.split("/").some(n=>n===""||n==="."||n===".."))throw new y("unsafe_path",`the object ${JSON.stringify(e)} has a name no request can carry`);return encodeURIComponent(e)}async function Uo(e,t,n,s){let a=[],r;do{let i=new URLSearchParams({limit:"1000",...s?{prefix:s}:{},...r?{cursor:r}:{}}),o=await h(e,t,"GET",`/storage/buckets/${D("the bucket",n)}/objects?${i}`);a.push(...o.objects),r=o.next_cursor}while(r);return a}async function Zg(e,t,n,s){let a;try{a=await e.io.fetch(t,{...n,redirect:"manual"})}catch(r){throw new y("network_error",`${s}: ${r instanceof Error?r.message:String(r)}`)}if(!a.ok){let r=await a.json().catch(()=>({}));throw new y(r.error??`http_${a.status}`,`${s}: ${r.error_description??r.error??`answered ${a.status}`}`)}return a}var uP={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".heic":"image/heic",".heif":"image/heif",".avif":"image/avif",".svg":"image/svg+xml",".ico":"image/x-icon",".pdf":"application/pdf",".json":"application/json",".txt":"text/plain",".csv":"text/csv",".html":"text/html",".css":"text/css",".js":"text/javascript",".xml":"application/xml",".zip":"application/zip",".mp4":"video/mp4",".mov":"video/quicktime",".webm":"video/webm",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4"};function Wg(e){return uP[lP(e).toLowerCase()]}async function zo(e,t,n,s,a,r){let i=(await Vg(n)).size,o=await h(e,t,"POST",`/storage/buckets/${D("the bucket",s)}/upload-urls`,{path:a,expires_in:Xg,...r?{owner_id:r}:{}});return await Zg(e,o.url,{method:"PUT",body:Yg.toWeb(aP(n)),headers:{"content-length":String(i),...Wg(n)?{"content-type":Wg(n)}:{}},duplex:"half"},`${n} \u2192 ${s}:${a}`),i}function cP(e,t){let n=t.split("/");if(t===""||n.some(a=>a===""||a==="."||a===".."||a.includes("\\")||a.includes("\0")))throw new y("unsafe_path",`the object ${JSON.stringify(t)} has a name no file here can take`);let s=Hg(e,...n);if(!s.startsWith(e+Kr))throw new y("unsafe_path",`${t} would land outside ${e}`);return s}async function hP(e,t,n,s,a,r){let i=await h(e,t,"POST",`/storage/buckets/${D("the bucket",n)}/signed-urls`,{path:s,expires_in:Xg}),o=await Zg(e,i.url,{method:"GET"},`${n}:${s} \u2192 ${a}`);if(await rP(Fg(a),{recursive:!0}),r!==void 0){let[g,b]=await Promise.all([Bg(r),Bg(Fg(a))]);if(b!==g&&!b.startsWith(g+Kr))throw new y("unsafe_path",`${s} would land outside ${r} (through a link)`)}let d=(await iP(a,qr.O_WRONLY|qr.O_CREAT|qr.O_TRUNC|qr.O_NOFOLLOW)).createWriteStream();if(!o.body)return await new Promise((g,b)=>d.on("error",b).end(g)),0;let p=0,u=Yg.fromWeb(o.body).on("data",g=>{p+=g.length});return await dP(u,d),p}async function Qg(e){let t=[];for(let n of await oP(e,{withFileTypes:!0})){let s=Hg(e,n.name);n.isDirectory()?t.push(...await Qg(s)):n.isFile()&&t.push(s)}return t}var gP={name:"storage:ls",summary:"a bucket's objects, or those under a prefix: path, size, type",usage:"<bucket>[:prefix]",args:{min:1,max:1},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0].includes(":")?e.args[0]:`${e.args[0]}:`);if(!n)throw m(`${e.args[0]} is not a bucket (bucket or bucket:prefix)`);let s=await Uo(e,t,n.bucket,n.path);e.out.result({bucket:n.bucket,objects:s},()=>{s.length===0&&e.out.line(`Nothing in ${n.bucket}:${n.path}`);for(let a of s)e.out.line(`${a.path} ${a.size} ${a.content_type??""}`)})}},mP={name:"storage:cp",summary:"copy files between this computer and a bucket: a file, or a folder whole with -r",usage:"<from> <to> [-r] [--owner <user id>]",details:["bucket:path names an object of the environment; anything else is a path on this computer.","Up: a file to bucket:path (a path ending in / keeps the file name); with -r, a folder to bucket:prefix, its paths kept.","Down: bucket:path to a file; with -r, every object under bucket:prefix into a folder."],args:{min:2,max:2},flags:{recursive:{type:"boolean",short:"r",description:"a folder (up) or a prefix (down), whole"},owner:{type:"string",value:"user id",description:"the user uploaded objects belong to (deleted with them)"}},scope:"environment",async run(e){let t=await f(e),[n,s]=e.args,a=ua(n),r=ua(s),i=e.flags.recursive===!0,o=e.flags.owner;if(a&&r)throw m("one side is a bucket and the other this computer (bucket-to-bucket: the storage API copies within a bucket)");if(!a&&!r)throw m("one side is bucket:path");let l=0,d=0;if(r){let p=Jg(e.io.cwd,n),u=i?await Qg(p):[p];if(!i&&(await Vg(p)).isDirectory())throw m(`${n} is a folder: copy it with -r`);await v(e,t,`upload ${u.length} file${u.length===1?"":"s"} to ${r.bucket}:${r.path} in ${N(t)}`);for(let g of u){let b=i?pP(p,g).split(Kr).join("/"):g.split(Kr).pop(),w=i||r.path===""||r.path.endsWith("/")?`${r.path.replace(/\/?$/,r.path?"/":"")}${b}`:r.path;d+=await zo(e,t,g,r.bucket,w,o),l++,u.length>1&&e.out.note(`${l}/${u.length} ${w}`)}}else if(a){let p=Jg(e.io.cwd,s),u=i?await Uo(e,t,a.bucket,a.path):[{path:a.path,size:0}];for(let g of u){let b=i?g.path.slice(a.path.length).replace(/^\/+/,""):"",w=i?cP(p,b):p;d+=await hP(e,t,a.bucket,g.path,w,i?p:void 0),l++,u.length>1&&e.out.note(`${l}/${u.length} ${g.path}`)}}e.out.result({copied:l,bytes:d},()=>e.out.line(`Copied ${l} file${l===1?"":"s"} (${d} bytes).`))}},fP={name:"storage:rm",summary:"delete an object, or every object under a prefix with -r",usage:"<bucket:path> [-r]",args:{min:1,max:1},flags:{recursive:{type:"boolean",short:"r",description:"every object under the prefix"}},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0]);if(!n||!n.path&&e.flags.recursive!==!0)throw m(`${e.args[0]} is not bucket:path`);let s=e.flags.recursive===!0?(await Uo(e,t,n.bucket,n.path)).map(a=>a.path):[n.path];await v(e,t,`delete ${s.length} object${s.length===1?"":"s"} from ${n.bucket} in ${N(t)}`);for(let a of s)await h(e,t,"DELETE",`/storage/buckets/${D("the bucket",n.bucket)}/objects/${Gr(a)}`);e.out.result({deleted:s.length},()=>e.out.line(`Deleted ${s.length} object${s.length===1?"":"s"}.`))}},yP={name:"storage:url",summary:"a signed download URL for one object",usage:"<bucket:path> [--expires <seconds>]",args:{min:1,max:1},flags:{expires:{type:"string",value:"seconds",description:"how long it works (default 3600, at most 604800)"}},scope:"environment",async run(e){let t=await f(e),n=ua(e.args[0]);if(!n||!n.path)throw m(`${e.args[0]} is not bucket:path`);let s=e.flags.expires===void 0?3600:Number(e.flags.expires);if(!Number.isInteger(s)||s<1||s>604800)throw m("--expires is 1 to 604800 seconds");let a=await h(e,t,"POST",`/storage/buckets/${D("the bucket",n.bucket)}/signed-urls`,{path:n.path,expires_in:s});e.out.result(a,()=>e.out.line(a.url))}},em=[gP,mP,fP,yP];import{readFile as bP}from"node:fs/promises";import{resolve as wP}from"node:path";var Wo=/^[a-z][a-z0-9_-]{0,63}$/,tm=["backend","client","https","mcp","platform"];function dn(e,t,n=0){let s=e.args[n];if(!Wo.test(s))throw m(`${JSON.stringify(s)} is not ${t}'s name: lowercase letters, digits, _ and -, a letter first, at most 64`);return s}function jt(e,t){let n=te(e,t);if(n===0)throw m(`--${t} is a whole number from 1`);return n}function un(e){let t=new URLSearchParams;for(let[s,a]of Object.entries(e))a!==void 0&&t.set(s,String(a));let n=t.toString();return n===""?"":`?${n}`}async function nm(e,t,n){let s;try{s=await bP(wP(e.io.cwd,t),"utf8")}catch(a){throw new y("unreadable_file",`${t} could not be read: ${a instanceof Error?a.message:String(a)}`)}return ue(s,n)}var Ur=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),zr=e=>`$${e<.01&&e>0?e.toPrecision(2):e.toFixed(e<1?4:2)}`,Br=e=>`$${e.toFixed(2)}`,sm="or connect the environment's own key for the model's provider, which its provider bills: palbase ai:providers:set <provider>",Bo=`raise the budget in Studio \u203A AI \u203A Settings or with palbase ai:settings:set --monthly-budget <usd>; ${sm}`,Fo=`move the environment to Pay as you go in Studio \u203A Billing or with palbase billing:env --plan pay_as_you_go (its organization paid for by a payment account with a card, not held); ${sm}`;function am(e){if(!(e instanceof j))return e;switch(e.code){case"ai_budget_reached":return new y(e.code,"the environment's monthly AI budget is spent: Palbase's models serve it again when the month turns (UTC) or the budget is raised",{hint:Bo});case"ai_payment_required":return new y(e.code,"the month's free AI dollar is spent and the environment has nothing to pay for Palbase's models with: it is on Free, or its payment account is held",{hint:Fo});case"provider_key_required":return new y(e.code,"Palbase's models do not serve the environment, and it has no key of its own for the model's provider",{hint:"connect one: palbase ai:providers:set <provider> (palbase ai:spend says why)"});case"billing_unavailable":return new y(e.code,`Palbase's billing did not say whether its models serve the environment${e.retryAfter!==void 0?`: try again in ${e.retryAfter} s`:"; try again shortly"}`);default:return e}}function vP(e){if(e===void 0)return;let t={};for(let n of Array.isArray(e)?e:[e]){let s=String(n),a=s.indexOf("=");if(a<=0)throw m(`--metadata is key=value, not ${JSON.stringify(s)}`);Object.defineProperty(t,s.slice(0,a),{value:s.slice(a+1),enumerable:!0,writable:!0,configurable:!0})}return t}var _P={name:"ai:usage",summary:"the environment's uses of models, newest first: what each was, what it cost, and whose key it went out with",usage:"[--kind text|embedding|image] [--metadata key=value]\u2026 [--limit <n>] [--cursor <cursor>]",flags:{kind:{type:"string",value:"kind",description:"only uses of this kind: text, embedding or image"},metadata:{type:"string",multiple:!0,value:"key=value",description:"only uses carrying this label (repeat for several): a product's spend, a task's"},limit:{type:"string",value:"n",description:"at most this many (1\u2013500, default 50)"},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}},scope:"environment",async run(e){let t=e.flags.kind;if(t!==void 0&&!["text","embedding","image"].includes(t))throw m("--kind is text, embedding or image");let n=vP(e.flags.metadata),s=jt(e.flags.limit,"limit"),a=await f(e),r=await h(e,a,"GET",`/ai/usage${un({kind:t,limit:s,cursor:e.flags.cursor,metadata:n&&JSON.stringify(n)})}`);e.out.result(r,()=>{r.usage.length===0&&e.out.line("No use of a model yet."),e.out.table(r.usage,[{header:"when",value:i=>i.created_at},{header:"kind",value:i=>i.kind},{header:"model",value:i=>i.model},{header:"tokens",value:i=>`${i.kind==="image"?`${i.items} picture(s)`:`${i.input_tokens} in / ${i.output_tokens} out`}${i.estimated?" (estimate)":""}`},{header:"cost",value:i=>zr(i.cost_nano_usd/1e9)},{header:"key",value:i=>i.connection_owner==="project"?"project's":"platform's"},{header:"labels",value:i=>Object.entries(i.metadata??{}).map(([o,l])=>`${o}=${l}`).join(" ")}]),r.usage.some(i=>i.estimated)&&e.out.line("(estimate): a reasoning call cut before its answer \u2014 its provider never said what its hidden thinking came to, so it is billed as the time the model thought at its measured speed, rounded down."),r.next_cursor&&e.out.line(`More: palbase ai:usage --cursor ${r.next_cursor}`)})}},kP={name:"ai:models",summary:"the models the environment may name \u2014 every text model with its rungs, window and price, and the embedding and picture models",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/models");e.out.result(n,()=>{e.out.line("Roles (an agent names one to get the model Palbase picks for it now):"),e.out.table(n.text,[{header:"role",value:s=>s.role},{header:"what it is for",value:s=>s.description}]),e.out.line(),e.out.line("Text models (an agent's or an answer's model by its id):"),n.chat.length===0?e.out.line("None listed for the environment just now: name a role, or connect a provider's key (ai:providers:set) to list its models."):e.out.table(n.chat,[{header:"id",value:s=>`${s.id}${s.default?" (default)":""}`},{header:"roles",value:s=>s.roles.join(",")},{header:"efforts",value:s=>s.efforts.join(",")||"-"},{header:"window",value:s=>String(s.context_window)},{header:"$/M in",value:s=>String(s.price.input_usd_per_mtok)},{header:"$/M out",value:s=>String(s.price.output_usd_per_mtok)}]),e.out.line(),e.out.line("Embedding models:"),e.out.table(n.embeddings,[{header:"model",value:s=>s.model},{header:"role",value:s=>s.role||"-"},{header:"dimensions",value:s=>String(s.dimensions)},{header:"$/M in",value:s=>String(s.input_usd_per_mtok)}]),e.out.line(),e.out.line("Picture models:"),e.out.table(n.images,[{header:"model",value:s=>s.model},{header:"role",value:s=>s.role||"-"},{header:"$/M picture out",value:s=>String(s.image_output_usd_per_mtok)}]),n.unavailable.length>0&&e.out.line(`Not listed just now (their models are missing until they answer): ${n.unavailable.join(", ")}`)})}},SP={free:"Free \u2014 its free dollar a month, then its own provider keys",pay_as_you_go:"Pay as you go \u2014 past the free dollar, the account's balance pays: the provider's price and 10%",exempt:"exempt \u2014 measured, not charged"},$P={name:"ai:spend",summary:"what Palbase's models cost the environment this month: the free dollar and what is left of it, the monthly budget, and whether they serve it now \u2014 and if not, why",details:["Palbase's models are the platform's provider keys. A month brings a dollar of them free: a Free environment's own, a paying account's once for all its projects. Past it a Free environment uses its own provider keys (ai:providers:set); a Pay as you go one pays from the account's balance, at the provider's price and 10%.","Its own provider keys always serve it, and are not counted here: their provider bills them."],scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/spend");e.out.result(n,()=>{if(e.out.line(`month ${n.month} (UTC; starts again ${n.resets_on})`),e.out.line(`plan ${SP[n.plan]??n.plan}`),e.out.line(`spent ${zr(n.spent_usd)} on Palbase's models, at what it is billed, before the free dollar`),e.out.line(`free ${Br(n.free_usd)} a month, the environment's own${n.free_left_usd===null?"":` \u2014 ${zr(n.free_left_usd)} left`}`),e.out.line(`monthly budget ${n.monthly_budget_usd===null?"none":Br(n.monthly_budget_usd)}`),n.platform_keys){e.out.line("Palbase's models serve the environment now");return}n.reason==="budget"?(e.out.line(`Palbase's models do not serve the environment: its monthly budget is spent \u2014 until ${n.resets_on}, or until it is raised`),e.out.line(` ${Bo[0].toUpperCase()}${Bo.slice(1)}.`)):n.reason==="payment"?(e.out.line("Palbase's models do not serve the environment: the free dollar is spent and it has no paid rights now (it is on Free, at its month's limit, or its payment account has an unpaid charge)"),e.out.line(` ${Fo[0].toUpperCase()}${Fo.slice(1)}.`)):(e.out.line("Palbase's models do not serve the environment"),e.out.line(" Connect the environment's own key for a provider: palbase ai:providers:set <provider>."))})}},PP={name:"ai:providers:list",summary:"the providers the environment calls with its own key (never the keys): its calls go out with them instead of the platform's",scope:"environment",async run(e){let t=await f(e),{providers:n}=await h(e,t,"GET","/ai/providers");e.out.result({providers:n},()=>{n.length===0&&e.out.line("No key of the environment's own: its calls go out with the platform's."),e.out.table(n,[{header:"provider",value:s=>s.name},{header:"verified",value:s=>s.verified?"yes":"no"},{header:"key",value:()=>"set"}])})}},AP={name:"ai:providers:set",summary:"call a provider with the environment's own key: the key is piped in or typed unseen, never shown or put on the command line",usage:"<provider> [--key-stdin] < key.txt",details:["Without --key-stdin the key is asked for at the terminal, and not echoed as it is typed or pasted."],args:{min:1,max:1},flags:{"key-stdin":{type:"boolean",description:"read the key from stdin (a pipe, a file)"}},scope:"environment",async run(e){let t=e.args[0];if(!/^[a-z][a-z0-9-]{0,31}$/.test(t))throw m(`${JSON.stringify(t)} is not a provider's name (openai, anthropic, google\u2026)`);if(e.flags["key-stdin"]!==!0&&!e.interactive)throw m(`${t}'s key is read from stdin`,`pipe it in: palbase ai:providers:set ${t} --key-stdin < key.txt`);let n=await f(e),s=await Me(e,`${t}'s key`);await v(e,n,`call ${t} with the environment's own key`);let a=await h(e,n,"PUT",`/ai/providers/${k(t)}`,{key:s});e.out.result(a,()=>e.out.line(`${a.name}'s calls go out with the environment's own key from now on${a.verified?"":" (the provider did not confirm it: check the key)"}; it is kept sealed.`))}},RP={name:"ai:providers:remove",summary:"forget the environment's own key for a provider: its calls go out with the platform's again",usage:"<provider>",args:{min:1,max:1},scope:"environment",async run(e){let t=e.args[0],n=await f(e);await v(e,n,`forget the environment's own ${t} key`),await h(e,n,"DELETE",`/ai/providers/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`${t}'s calls go out with the platform's key again.`))}};function rm(e,t){e.out.line(`clients ${t.clients?"on: the app's users talk to the agents with their own session":"off: the server starts and writes every chat"}`),e.out.line(`messages per minute ${t.user_messages_per_minute} a user`),e.out.line(`history kept ${t.history_bytes} bytes of ${t.history_limit_bytes===null?"no fixed limit":`${t.history_limit_bytes} at most`}`);let n=t.monthly_budget_usd??null;e.out.line(`monthly budget ${n===null?"none: Palbase's models are not capped":`${Br(n)} a month on Palbase's models`}`)}function TP(e){if(e===void 0)return;let t=String(e);if(t==="none")return null;let n=/^\d{1,7}(\.\d{1,2})?$/.test(t)?Number(t):NaN;if(!(n>=.01&&n<=1e6))throw m(`--monthly-budget is US dollars from 0.01 to 1000000, cents at most, or none \u2014 not ${JSON.stringify(t)}`,"palbase ai:settings:set --monthly-budget 25");return n}var EP={name:"ai:settings:get",summary:"what the environment allows its chats \u2014 whether the app's users talk to the agents, how often, how much history is kept \u2014 and its monthly AI budget",scope:"environment",async run(e){let t=await f(e),n=await h(e,t,"GET","/ai/settings");e.out.result(n,()=>rm(e,n))}},CP={name:"ai:settings:set",summary:"change what the environment allows its chats, and its monthly AI budget: a setting left out is left as it is",usage:"[--clients on|off] [--messages-per-minute <n>] [--history-limit <bytes>|none] [--monthly-budget <usd>|none]",details:["--monthly-budget is the most the environment spends a month on Palbase's models (the platform's provider keys), at what it is billed: past it a call they would serve is refused (ai_budget_reached) until the month turns (UTC) or the budget is raised, and the organization's owners are emailed once. Its own provider keys are not counted: their provider bills them. palbase ai:spend says what is spent.","Only an owner or an admin of the organization sets it, as themselves: a member's change of it is refused, and so is a key's or a token's \u2014 what spends the money does not raise its own limit."],flags:{clients:{type:"string",value:"on|off",description:"the app's users talk to the agents of their chats with their own session"},"messages-per-minute":{type:"string",value:"n",description:"how many messages one user sends a minute (1\u2013600)"},"history-limit":{type:"string",value:"bytes|none",description:"the chat history kept at most, in bytes; none: the plan's"},"monthly-budget":{type:"string",value:"usd|none",description:"the most spent a month on Palbase's models, in US dollars (0.01\u20131000000, cents at most); none: no budget"}},scope:"environment",async run(e){let t={},n=e.flags.clients;if(n!==void 0){if(!["on","off"].includes(n))throw m("--clients is on or off");t.clients=n==="on"}let s=jt(e.flags["messages-per-minute"],"messages-per-minute");s!==void 0&&(t.user_messages_per_minute=s);let a=e.flags["history-limit"];a!==void 0&&(a==="none"?t.history_limit_bytes=null:t.history_limit_bytes=jt(a,"history-limit"));let r=TP(e.flags["monthly-budget"]);if(r!==void 0&&(t.monthly_budget_usd=r),Object.keys(t).length===0)throw m("nothing to change","pass --clients, --messages-per-minute, --history-limit or --monthly-budget");let i=await f(e),o=Object.keys(t).filter(p=>p!=="monthly_budget_usd"),l=[...o.length>0?[`change what the chats are allowed: ${o.join(", ")}`]:[],...r===void 0?[]:[r===null?"take away the monthly AI budget":`set the monthly AI budget to ${Br(r)}`]];await v(e,i,l.join("; "));let d;try{d=await h(e,i,"PUT","/ai/settings",t)}catch(p){throw r===void 0?p:jP(p)}e.out.result(d,()=>rm(e,d))}};function jP(e){return e instanceof j?e.status===403?new y(e.code,"only an owner or an admin of the organization sets the environment's monthly AI budget, as themselves: a member's change of it is refused, and so is a key's or a token's",{hint:"ask an owner or an admin to set it \u2014 in Studio \u203A AI \u203A Settings, or with palbase ai:settings:set --monthly-budget signed in as themselves (palbase login)"}):e.code==="no_billing"?new y(e.code,"this stack bills no AI, so it keeps no monthly AI budget: nothing was changed"):e.code==="registry_unavailable"?new y(e.code,"Palbase's billing did not answer: nothing was changed",{hint:"run the command again shortly"}):e:e}function Jo(e){return e.source==="code"?`the backend's code (machine ${e.machine??"backend"})`:e.published_version===null||e.published_version===void 0?`a draft (revision ${e.revision??"?"}), not published`:`version ${e.published_version} published${e.unpublished_changes?`, the draft (revision ${e.revision}) changed since`:""}`}var OP={name:"ai:agents:list",summary:"the environment's agents: its backend's (its build's contract names them) and those it keeps, a draft and published versions",scope:"environment",async run(e){let t=await f(e),{agents:n}=await h(e,t,"GET","/ai/agents");e.out.result({agents:n},()=>{n.length===0&&e.out.line("No agent yet: an ai.agent of the backend, or ai:agents:create <name>."),e.out.table(n,[{header:"name",value:s=>s.name},{header:"source",value:s=>s.source},{header:"published",value:s=>s.source==="code"?"-":s.published_version?`v${s.published_version}`:"no"},{header:"draft",value:s=>s.source==="code"?"-":s.unpublished_changes?`changed (r${s.revision})`:`r${s.revision}`},{header:"machine",value:s=>s.machine??"-"},{header:"description",value:s=>s.description??""}])})}};async function MP(e,t,n,s){let a;for(let r=0;r<100;r++){let i=await h(e,t,"GET",`/ai/agents/${k(n)}/versions${un({limit:100,cursor:a})}`),o=i.versions.find(l=>l.version===s);if(o)return o;if(!i.next_cursor||i.versions.every(l=>l.version<s))break;a=i.next_cursor}throw new y("not_found",`${n} has no version ${s}`,{hint:`palbase ai:agents:versions ${n}`})}function ms(e,t,n){if(e.out.line(t),n===void 0)e.out.line(" (none yet)");else for(let s of JSON.stringify(n,null,2).split(`
|
|
642
642
|
`))e.out.line(` ${s}`)}var xP={name:"ai:agents:show",summary:"one agent: where it stands and what it is \u2014 a kept one's published version (or its draft, or a version), a backend's last turn",usage:"<name> [--draft | --version <n>]",args:{min:1,max:1},flags:{draft:{type:"boolean",description:"the draft, as the next publish makes a version of it"},version:{type:"string",value:"n",description:"that version, as it was published"}},scope:"environment",async run(e){let t=dn(e,"an agent"),n=jt(e.flags.version,"version");if(n!==void 0&&e.flags.draft===!0)throw m("--draft or --version, not both");let s=await f(e);if(n!==void 0){let r=await MP(e,s,t,n);e.out.result(r,()=>ms(e,`${t} version ${r.version}, published ${r.published_at} by ${r.published_by}:`,r.definition));return}let a=await h(e,s,"GET",`/ai/agents/${k(t)}`);if(e.flags.draft===!0&&a.source==="code")throw new y("agent_defined_in_code",`${t} is the backend's code's: it has no draft \u2014 it changes with the code`);e.out.result(a,()=>{e.out.line(`${a.name}: ${Jo(a)}`),a.description&&e.out.line(a.description),a.updated_at&&e.out.line(`Draft saved ${a.updated_at}${a.updated_by?` by ${a.updated_by}`:""}.`),e.out.line(),a.source==="code"?ms(e,`Its last turn${a.defined_at?` (${a.defined_at})`:""} was defined as:`,a.definition):e.flags.draft===!0||a.published===void 0?ms(e,`The draft (revision ${a.revision}):`,a.draft):ms(e,`Version ${a.published_version}, the one its chats talk to:`,a.published)})}};function IP(e){let t=e.indexOf(":"),n=t>0?e.slice(0,t):"",s=e.slice(t+1);if(!tm.includes(n)||s==="")throw m(`--tool is source:name \u2014 ${tm.join(", ")} \u2014 not ${JSON.stringify(e)}`,"palbase ai:tools lists the tools an agent may be given");let a=n==="backend"?s.indexOf("/"):-1;if(a<0)return{source:n,name:s};let r=s.slice(0,a);if(s=s.slice(a+1),!Wo.test(r)||s==="")throw m(`--tool backend:<machine>/<name> names one of the environment's machines and its tool, not ${JSON.stringify(e)}`);return{source:"backend",name:s,machine:r}}async function LP(e){let t={},n=e.flags.from;if(n!==void 0){let a=await nm(e,n,n),r=Ur(a)&&Ur(a.definition)&&typeof a.name=="string"?a.definition:a;if(!Ur(r))throw m(`${n} is an agent's definition: a JSON object { instructions, model, tools, \u2026 }`);t=r}e.flags.instructions!==void 0&&(t.instructions=e.flags.instructions),e.flags.description!==void 0&&(t.description=e.flags.description),e.flags.model!==void 0&&(t.model=e.flags.model),e.flags.tool!==void 0&&(t.tools=e.flags.tool.map(IP));let s=e.flags["output-schema"];if(s!==void 0){let a=await nm(e,s,s);if(!Ur(a)||a.type!=="object")throw m(`${s} is the JSON Schema of the agent's one-shot answers: an object's ({ "type": "object", "properties": \u2026 })`);t.output=a}if(t.backend!==void 0)throw m("an agent the environment keeps names no backend: a backend's agent is its code's (ai.agent)");return t}async function DP(e,t,n){try{let s=await h(e,t,"GET",`/ai/agents/${k(n)}`);return s.source==="platform"&&s.draft&&s.revision!==void 0?{draft:s.draft,revision:s.revision}:void 0}catch(s){if(s instanceof j&&s.status===404)return;throw s}}async function im(e,t,n,s,a){return h(e,t,"PUT",`/ai/agents/${k(n)}${un({revision:a})}`,s)}async function Vo(e,t,n,s){return h(e,t,"POST",`/ai/agents/${k(n)}/publish`,s!==void 0?{revision:s}:{})}var NP={name:"ai:agents:create",summary:"save the draft of an agent the environment keeps \u2014 a new one's, or a change of one's \u2014 and publish it with --publish",usage:"<name> [--from <file.json>] [--instructions <text>] [--description <text>] [--model <model>] [--tool <source:name>]\u2026 [--output-schema <file.json>] [--revision <n>] [--publish]",details:["Its chats talk to the version published: a draft changes nothing until it is published (ai:agents:publish, or --publish here).","A change of an agent it keeps is made on its draft: each flag given replaces its field, and the rest stays.",'--from is the whole definition as JSON ({ "instructions", "model", "tools": [{ "source", "name" }], "subagents", "output", \u2026 }) \u2014 or what ai:agents:generate --json printed; each flag given replaces its field.',"--tool is source:name: backend:<ai.tool> (backend:<machine>/<ai.tool> for another machine's), client:<ai.clientTool>, https:<endpoint>, mcp:<server>, platform:web.search|web.fetch|workspace.fetch_archive (palbase ai:tools lists them). Given, the tools are those; a backend or client tool goes by its name alone, and the platform takes what it does from the build that declares it.","--revision is the draft's revision as it was read (ai:agents:show): when another was saved since, the save is refused (draft_changed) and nothing is lost."],args:{min:1,max:1},flags:{from:{type:"string",value:"file.json",description:"the definition, as JSON"},instructions:{type:"string",value:"text",description:"what the agent is told to do"},description:{type:"string",value:"text",description:"what it is for, in a sentence or two"},model:{type:"string",value:"model",description:"a role (fast, default, strong) or a model of ai:models"},tool:{type:"string",multiple:!0,value:"source:name",description:"a tool it is given (repeat for several)"},"output-schema":{type:"string",value:"file.json",description:"the JSON Schema of its one-shot answers"},revision:{type:"string",value:"n",description:"the draft's revision the change is made on"},publish:{type:"boolean",description:"publish the draft as the next version once it is saved"}},scope:"environment",async run(e){let t=dn(e,"an agent"),n=jt(e.flags.revision,"revision"),s=await LP(e);if(Object.keys(s).length===0)throw m("the agent is empty","pass --from <file.json>, or --instructions, --model, --tool\u2026");let a=await f(e),r=e.flags.from===void 0?await DP(e,a,t):void 0,i={...r?.draft,...s},o=e.flags.publish===!0;await v(e,a,`save the draft of the agent ${t}${o?" and publish it":""}`);let l=await im(e,a,t,i,n??r?.revision),d=o?await Vo(e,a,t,l.revision):l;e.out.result(d,()=>{e.out.line(`Saved the draft of ${t} (revision ${l.revision}).`),o?e.out.line(`Published ${t} as version ${d.published_version}: its chats talk to it from their next turn.`):e.out.line(`${Jo(d)[0].toUpperCase()}${Jo(d).slice(1)}: palbase ai:agents:publish ${t} makes its chats talk to it.`)})}},qP={name:"ai:agents:publish",summary:"make the draft of an agent the next version, the one its chats talk to from their next turn",usage:"<name> [--revision <n>]",args:{min:1,max:1},flags:{revision:{type:"string",value:"n",description:"the draft as it was read: refused when another was saved since"}},scope:"environment",async run(e){let t=dn(e,"an agent"),n=jt(e.flags.revision,"revision"),s=await f(e);await v(e,s,`publish the draft of the agent ${t}`);let a=await Vo(e,s,t,n);e.out.result(a,()=>e.out.line(`Published ${t} as version ${a.published_version}: its chats talk to it from their next turn.`))}},KP={name:"ai:agents:rollback",summary:"make a version an agent had the one its chats talk to again \u2014 and its draft that version",usage:"<name> <version>",args:{min:2,max:2},scope:"environment",async run(e){let t=dn(e,"an agent"),n=Number(e.args[1]);if(!Number.isSafeInteger(n)||n<1)throw m(`the version is a version the agent had, a whole number from 1 (not ${JSON.stringify(e.args[1])})`,`palbase ai:agents:versions ${t}`);let s=await f(e);await v(e,s,`make version ${n} of the agent ${t} the one its chats talk to (a draft not published is dropped)`);let a=await h(e,s,"POST",`/ai/agents/${k(t)}/rollback`,{version:n});e.out.result(a,()=>e.out.line(`${t}'s chats talk to version ${a.published_version} again; its draft is that version.`))}},GP={name:"ai:agents:versions",summary:"the versions an agent the environment keeps was published as, newest first, and the one its chats talk to",usage:"<name> [--limit <n>] [--cursor <cursor>]",args:{min:1,max:1},flags:{limit:{type:"string",value:"n",description:"at most this many (1\u2013100, default 20)"},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}},scope:"environment",async run(e){let t=dn(e,"an agent"),n=jt(e.flags.limit,"limit"),s=await f(e),a=await h(e,s,"GET",`/ai/agents/${k(t)}/versions${un({limit:n,cursor:e.flags.cursor})}`);e.out.result(a,()=>{a.versions.length===0&&e.out.line(`${t} has no version yet: palbase ai:agents:publish ${t}`),e.out.table(a.versions,[{header:"version",value:r=>`${r.version}${r.version===a.published_version?" (published)":""}`},{header:"published",value:r=>r.published_at},{header:"by",value:r=>r.published_by},{header:"model",value:r=>r.definition.model??"-"},{header:"description",value:r=>r.definition.description??""}]),a.next_cursor&&e.out.line(`More: palbase ai:agents:versions ${t} --cursor ${a.next_cursor}`)})}},UP={name:"ai:agents:generate",summary:"write an agent from a description \u2014 instructions, a model, the environment's tools that fit \u2014 and save it as a draft with --save",usage:'"<description>" [--name <name>] [--save] [--publish]',details:["Nothing is kept unless --save (or --publish) says so: the definition is printed, for ai:agents:create --from.","It is written as you \u2014 your sign-in, or PALBASE_TOKEN \u2014 20 an hour; the platform pays for it."],args:{min:1,max:1},flags:{name:{type:"string",value:"name",description:"the name it is to have (left out: one is suggested)"},save:{type:"boolean",description:"save it as the draft of an agent of that name"},publish:{type:"boolean",description:"save it and publish it as the next version"}},scope:"environment",async run(e){let t=e.args[0].trim();if(t===""||t.length>4e3)throw m("the description says the agent in words: 1 to 4000 characters");let n=e.flags.name;if(n!==void 0&&!Wo.test(n))throw m(`${JSON.stringify(n)} is not an agent's name: lowercase letters, digits, _ and -, a letter first, at most 64`);let s=await f(e),a=await h(e,s,"POST","/ai/agent-drafts",{description:t,...n!==void 0?{name:n}:{}}),r=e.flags.publish===!0;if(e.flags.save!==!0&&!r){e.out.result(a,()=>{ms(e,`${a.name}:`,a.definition),e.out.line(),e.out.line(`Nothing was kept. Save it: palbase ai:agents:generate "\u2026" --save, or palbase ai:agents:create ${a.name} --from <the definition, as ai:agents:generate --json prints it>`)});return}await v(e,s,`save the draft of the agent ${a.name}${r?" and publish it":""}`);let i=await im(e,s,a.name,a.definition,void 0),o=r?await Vo(e,s,a.name,i.revision):i;e.out.result({...a,agent:o},()=>{ms(e,`${a.name}:`,a.definition),e.out.line(),e.out.line(r?`Saved and published ${a.name} as version ${o.published_version}.`:`Saved the draft of ${a.name} (revision ${i.revision}): palbase ai:agents:publish ${a.name} makes its chats talk to it.`)})}},zP={name:"ai:agents:remove",summary:"forget an agent the environment keeps, its versions with it: its chats stay, and are refused a message until one of its name is there again",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=dn(e,"an agent"),n=await f(e);await v(e,n,`forget the agent ${t} and its versions`),await h(e,n,"DELETE",`/ai/agents/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`Forgot ${t} and its versions.`))}},BP={name:"ai:tools",summary:"the tools an agent may be given: the backend's (its build's contract), the environment's endpoints, and the platform's",scope:"environment",async run(e){let t=await f(e),{tools:n}=await h(e,t,"GET","/ai/tools");e.out.result({tools:n},()=>{e.out.table(n,[{header:"tool",value:s=>`${s.source}:${s.name}`},{header:"machine",value:s=>s.machine??"-"},{header:"approval",value:s=>s.approval?"yes":"no"},{header:"description",value:s=>s.description}]),e.out.line("An agent is given one as --tool source:name (ai:agents:create).")})}},FP={name:"ai:chats:list",summary:"the environment's chats, newest first \u2014 every one, or a member's",usage:"[--member <user id>] [--limit <n>] [--cursor <cursor>]",flags:{member:{type:"string",value:"user id",description:"only the chats this user is in"},limit:{type:"string",value:"n",description:"at most this many (1\u2013100, default 100)"},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}},scope:"environment",async run(e){let t=jt(e.flags.limit,"limit"),n=await f(e),s=await h(e,n,"GET",`/ai/chats${un({member:e.flags.member,limit:t,cursor:e.flags.cursor})}`);e.out.result(s,()=>{s.chats.length===0&&e.out.line("No chat."),e.out.table(s.chats,[{header:"id",value:a=>a.id},{header:"agent",value:a=>`${a.agent}${a.agent_version?`@${a.agent_version}`:""}`},{header:"title",value:a=>a.title},{header:"members",value:a=>String(a.members.length)},{header:"started",value:a=>a.created_at}]),s.next_cursor&&e.out.line(`More: palbase ai:chats:list --cursor ${s.next_cursor}`)})}},JP=/^chat_[0-9a-f]{32}$/;function om(e){let t=e.args[0];if(!JP.test(t))throw m(`${JSON.stringify(t)} is not a chat's id (chat_ and 32 hex digits)`,"palbase ai:chats:list");return t}var WP={name:"ai:chats:show",summary:"one chat: its agent and members, and what they wrote, newest last \u2014 with what each turn cost (--cost)",usage:"<chat id> [--limit <n>] [--cost]",args:{min:1,max:1},flags:{limit:{type:"string",value:"n",description:"its last this many messages (1\u2013100, default 50)"},cost:{type:"boolean",description:"what each turn a message started cost, its subagents counted"}},scope:"environment",async run(e){let t=om(e),n=jt(e.flags.limit,"limit")??50,s=await f(e),a=await h(e,s,"GET",`/ai/chats/${k(t)}`),r=await h(e,s,"GET",`/ai/chats/${k(t)}/messages${un({limit:n})}`),i=[...r.messages].reverse(),o=await VP(e,s,t,new Set(i.flatMap(p=>p.response_id?[p.response_id]:[]))),l=new Map;i.forEach((p,u)=>p.response_id&&l.set(p.response_id,u));let d=new Map;if(e.flags.cost===!0)for(let p of new Set(i.flatMap(u=>u.response_id?[u.response_id]:[])))d.set(p,await h(e,s,"GET",`/ai/chats/${k(t)}/turns/${k(p)}/cost`));e.out.result({chat:a,messages:i,answers:Object.fromEntries(o),...e.flags.cost===!0?{costs:Object.fromEntries(d)}:{}},()=>{e.out.line(`${a.id} ${a.title||"(untitled)"}`),e.out.line(`agent ${a.agent}${a.agent_version?` (stays on version ${a.agent_version})`:""}; started ${a.created_at}; ${a.history_bytes} bytes of history`),a.members.length>0&&e.out.line(`members: ${a.members.map(p=>`${p.name||p.user_id}${p.owner?" (owner)":""}`).join(", ")}`),e.out.line(),i.length===0&&e.out.line("Nothing written yet."),i.forEach((p,u)=>{if(e.out.line(`${p.created_at} ${p.author===null?"server":p.author_name||p.author}: ${p.text}${p.status==="failed"?` [failed: ${p.error??""}]`:""}`),!p.response_id||l.get(p.response_id)!==u)return;let g=o.get(p.response_id);g&&e.out.line(` ${a.agent}: ${g.replace(/\s*\n\s*/g," ")}`);let b=d.get(p.response_id);b&&e.out.line(` turn ${b.response_id}: ${zr(b.usd)}${b.unpriced?" at least":""}, ${b.input_tokens} in / ${b.output_tokens} out${b.final?"":" (still settling)"}`)}),r.next_cursor&&e.out.line(`(older messages are not shown: --limit ${Math.min(n*2,100)})`)})}};async function VP(e,t,n,s){let a=[],r;for(let o=0;o<20&&s.size>0;o++){let l=await h(e,t,"GET",`/ai/chats/${k(n)}/parts${un({limit:1e3,cursor:r})}`);if(a.unshift(l.parts),!l.next_cursor)break;r=l.next_cursor}let i=new Map;for(let o of a.flat()){let l=o.data.response_id,d=o.data.offset;if(typeof l!="string"||!s.has(l)||typeof d!="number")continue;let p=(i.get(l)??Buffer.alloc(0)).subarray(0,d);i.set(l,o.type==="text"&&typeof o.data.text=="string"?Buffer.concat([p,Buffer.from(o.data.text)]):o.type==="reset"?p:i.get(l)??p)}return new Map([...i].map(([o,l])=>[o,l.toString("utf8").trim()]))}var HP={name:"ai:chats:remove",summary:"delete a chat: a turn still running is stopped, and its history no longer counts",usage:"<chat id>",args:{min:1,max:1},scope:"environment",async run(e){let t=om(e),n=await f(e);await v(e,n,`delete the chat ${t}`),await h(e,n,"DELETE",`/ai/chats/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`Deleted the chat ${t}.`))}},YP={name:"ai:endpoints:list",summary:"the environment's HTTPS tools and MCP servers an agent may be given by name (their signing secrets are never said again)",scope:"environment",async run(e){let t=await f(e),{endpoints:n}=await h(e,t,"GET","/ai/endpoints"),s=n.map(({signing_secret:a,...r})=>r);e.out.result({endpoints:s},()=>{s.length===0&&e.out.line("No endpoint."),e.out.table(s,[{header:"name",value:a=>a.name},{header:"kind",value:a=>a.kind},{header:"url",value:a=>a.url},{header:"approval",value:a=>a.approval?"yes":"no"},{header:"description",value:a=>a.description}])})}},XP={name:"ai:endpoints:remove",summary:"forget an HTTPS tool or an MCP server: an agent that names it is given it no more",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=dn(e,"an endpoint"),n=await f(e);await v(e,n,`forget the endpoint ${t}`),await h(e,n,"DELETE",`/ai/endpoints/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`Forgot the endpoint ${t}.`))}},ZP={name:"ai:repos:list",summary:"the repositories a chat's workspace works in, by name, and the machine that gives their tokens",usage:"[--limit <n>] [--cursor <cursor>]",flags:{limit:{type:"string",value:"n",description:"at most this many (1\u20131000, default 100)"},cursor:{type:"string",value:"cursor",description:"the next page, as the last one said it"}},scope:"environment",async run(e){let t=jt(e.flags.limit,"limit"),n=await f(e),s=await h(e,n,"GET",`/ai/repos${un({limit:t,cursor:e.flags.cursor})}`);e.out.result(s,()=>{s.repos.length===0&&e.out.line("No repository."),e.out.table(s.repos,[{header:"name",value:a=>a.name},{header:"repository",value:a=>a.repository},{header:"branch",value:a=>a.default_branch},{header:"tokens from",value:a=>a.backend}]),s.next_cursor&&e.out.line(`More: palbase ai:repos:list --cursor ${s.next_cursor}`)})}},QP={name:"ai:repos:remove",summary:"forget a repository: a chat's workspace checks it out no more",usage:"<name>",args:{min:1,max:1},scope:"environment",async run(e){let t=dn(e,"a repository"),n=await f(e);await v(e,n,`forget the repository ${t}`),await h(e,n,"DELETE",`/ai/repos/${k(t)}`),e.out.result({removed:t},()=>e.out.line(`Forgot the repository ${t}.`))}},lm=[_P,$P,kP,PP,AP,RP,EP,CP,OP,xP,NP,qP,KP,GP,UP,zP,BP,FP,WP,HP,YP,XP,ZP,QP];var eA={name:"docs:search",summary:"search Palbase's documentation by meaning: the sections that answer it, each with its address and its text",usage:'"<what you need>" [--limit <n>]',details:["Ask in English, the docs' language: a question finds the section it means by its meaning and by its words \u2014 say what you need, not the words of a heading.","Each result is a section of a page: its title, its address with the section's anchor, and its text (the first 4000 characters of a longer one \u2014 the address has the rest).","Anyone may search, signed in or not: 10 searches in ten seconds, 30 a minute, 300 an hour. A sign-in or PALBASE_TOKEN is sent when there is one and is counted as itself; with none, your address is. When many search at once, a search waits its turn for a few seconds, or is told to search again in a moment."],args:{min:1,max:1},flags:{limit:{type:"string",value:"n",description:"how many sections (1 to 10, default 5)"}},scope:"none",async run(e){let t=e.args[0].trim();if([...t].length<2||[...t].length>500)throw m("say what you need from the docs: 2 to 500 characters");let n=e.flags.limit,s=n===void 0?5:Number(n);if(!Number.isInteger(s)||s<1||s>10)throw m("--limit is how many sections: 1 to 10");let{results:a}=await e.studio.open(`/docs/search?q=${encodeURIComponent(t)}&limit=${s}`);e.out.result(a,()=>{if(a.length===0){e.out.line('No section of the docs answers it: say it another way, or ask Palbase: palbase support:ask "<question>"');return}a.forEach((r,i)=>{i>0&&e.out.line(),e.out.line(`${i+1}. ${r.title===r.heading?r.title:`${r.title} \u203A ${r.heading}`}`),e.out.line(` ${r.url}`),e.out.line(),e.out.line(r.text)})})}},pm=[eA];var dm=3e4,Ho=3,tA=80;async function nA(e,t){let n=[],s;try{s=(await h(e,t,"GET","/machines?limit=100")).machines}catch{return"machines: not readable with this sign-in or token"}if(s.length===0)return"machines: none";for(let r of s.slice(0,Ho)){n.push(`machine ${r.name}: ${r.state}, size ${r.size}, version ${r.running_version===r.version?r.version:`${r.running_version} \u2192 ${r.version}`}`);let i;try{i=(await h(e,t,"GET",`/machines/${D("a machine's name",r.name)}/builds?limit=3`)).builds}catch{n.push(" builds: not readable with this sign-in or token");continue}for(let l of i)n.push(` build ${l.id} ${l.commit.slice(0,7)} ${l.state}${l.running?" (serving)":""}${l.reason?` \u2014 ${l.reason}`:""} ${l.created_at} "${l.message.split(`
|
|
643
643
|
`)[0].slice(0,80)}"`);let o=i.find(l=>l.state==="failed");if(o)try{let l=await h(e,t,"GET",`/machines/${D("a machine's name",r.name)}/builds/${D("a build's id",o.id)}/log`),d=typeof l=="string"?l:JSON.stringify(l);n.push(` the end of build ${o.id}'s log:`,...d.trimEnd().split(`
|
|
644
644
|
`).slice(-tA).map(p=>` ${p}`))}catch{n.push(` build ${o.id}'s log: not readable with this sign-in or token`)}}s.length>Ho&&n.push(`(${s.length-Ho} more machines not shown)`);let a=n.join(`
|