@fnmoa/cli 0.3.0 → 0.4.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/LICENSE +31 -0
- package/README.md +273 -70
- package/dist/assets/worker.js +216 -0
- package/dist/main.js +326 -0
- package/package.json +34 -27
- package/dist/args.js +0 -33
- package/dist/args.js.map +0 -1
- package/dist/client.js +0 -103
- package/dist/client.js.map +0 -1
- package/dist/command.js +0 -2
- package/dist/command.js.map +0 -1
- package/dist/commands/config.js +0 -129
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/github.js +0 -19
- package/dist/commands/github.js.map +0 -1
- package/dist/commands/login.js +0 -128
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/oracle.js +0 -86
- package/dist/commands/oracle.js.map +0 -1
- package/dist/commands/repos.js +0 -70
- package/dist/commands/repos.js.map +0 -1
- package/dist/commands/skills.js +0 -225
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/status.js +0 -78
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/tasks.js +0 -171
- package/dist/commands/tasks.js.map +0 -1
- package/dist/commands/track.js +0 -31
- package/dist/commands/track.js.map +0 -1
- package/dist/commands/whoami.js +0 -22
- package/dist/commands/whoami.js.map +0 -1
- package/dist/commands/workflows.js +0 -304
- package/dist/commands/workflows.js.map +0 -1
- package/dist/config.js +0 -65
- package/dist/config.js.map +0 -1
- package/dist/index.js +0 -105
- package/dist/index.js.map +0 -1
- package/dist/poll.js +0 -24
- package/dist/poll.js.map +0 -1
- package/dist/registry.js +0 -56
- package/dist/registry.js.map +0 -1
- package/dist/ui.js +0 -56
- package/dist/ui.js.map +0 -1
- package/src/skills/moa-oracle/SKILL.md +0 -35
- package/src/skills/moa-profiles/SKILL.md +0 -49
- package/src/skills/moa-review-pr/SKILL.md +0 -47
- package/src/skills/moa-run-task/SKILL.md +0 -44
- package/src/skills/moa-setup/SKILL.md +0 -60
- package/src/skills/moa-ship-issue/SKILL.md +0 -54
- package/src/skills/moa-triage-audit/SKILL.md +0 -47
package/dist/main.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
3
|
+
var Fi=Object.defineProperty;var Se=(e,t)=>()=>(e&&(t=e(e=0)),t);var Bi=(e,t)=>{for(var n in t)Fi(e,n,{get:t[n],enumerable:!0})};function D(e,t=fetch){let n=e.url.replace(/\/+$/,""),o=async(s,a,i,c)=>{let l=async()=>t(`${n}${a}`,{method:s,headers:{authorization:`Bearer ${e.token}`,...i===void 0?{}:{"content-type":"application/json"},...c===void 0?{}:{"idempotency-key":c}},...i===void 0?{}:{body:JSON.stringify(i)}}),d;try{d=await l()}catch(u){if(c!==void 0)try{d=await l()}catch{throw ts(n,u)}else throw ts(n,u)}if(!d.ok){let u=await d.text(),f=u,p=d.headers.get("x-request-id")??void 0;try{let m=JSON.parse(u);f=m.error??u,p=m.requestId??p}catch{}throw new re(d.status,f||`${s} ${a} failed`,p)}return d},r=async(s,a,i,c)=>{let l=await o(s,a,i,c);return l.status===204?void 0:await l.json()};return{url:n,get:s=>r("GET",s),post:(s,a,i)=>r("POST",s,a,i),patch:(s,a)=>r("PATCH",s,a),put:(s,a)=>r("PUT",s,a),del:async s=>{let a=await o("DELETE",s);if(a.status!==204)return await a.json().catch(()=>{})},stream:(s,a)=>t(`${n}${s}`,{headers:{authorization:`Bearer ${e.token}`,accept:"text/event-stream"},...a?{signal:a}:{}}),async rpc(s,a,i,c){let l=await t(s,{method:"POST",headers:{authorization:`Bearer ${a}`,"content-type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:i,...c?{params:c}:{}})}),d=await l.json().catch(()=>({}));if(!l.ok||d.error)throw new re(l.status,d.error?.message??`the server said ${l.status}`);return d.result}}}var re,bt,ts,Ie=Se(()=>{"use strict";re=class extends Error{status;requestId;constructor(t,n,o){super(n),this.name="ApiError",this.status=t,o&&(this.requestId=o)}},bt=class extends Error{constructor(t,n){super(`cannot reach ${t}: ${n instanceof Error?n.message:String(n)}`),this.name="Unreachable"}},ts=(e,t)=>new bt(e,t)});import{mkdir as Ki,readFile as Ji,writeFile as Wi}from"node:fs/promises";import{homedir as qi}from"node:os";import{dirname as zi,join as ns}from"node:path";async function Pe(e){try{let t=JSON.parse(await Ji(rs(e),"utf8"));return{current:t.current??"default",profiles:t.profiles??{}}}catch{return Vi()}}async function it(e,t){let n=rs(t);await Ki(zi(n),{recursive:!0}),await Wi(n,`${JSON.stringify(e,null,2)}
|
|
4
|
+
`,{mode:384})}async function ee(e,t=process.env){if(t.MOA_URL&&t.MOA_TOKEN)return{name:"env",url:t.MOA_URL,token:t.MOA_TOKEN};let n;try{n=await Pe(t)}catch(s){throw t.MOA_URL||t.MOA_TOKEN?new Error(os(t)):s}let o=e??n.current,r=n.profiles[o];if(!r)throw t.MOA_URL||t.MOA_TOKEN?new Error(os(t)):new Error(o==="default"&&Object.keys(n.profiles).length===0?"no workspace yet \u2014 run `moa login <url> --token <token>`":`no profile named ${o}`);return{name:t.MOA_URL||t.MOA_TOKEN?`${o}+env`:o,url:t.MOA_URL??r.url,token:t.MOA_TOKEN??r.token}}var Vi,rs,os,Ye=Se(()=>{"use strict";Vi=()=>({current:"default",profiles:{}}),rs=(e=process.env)=>e.MOA_CONFIG??ns(e.MOA_HOME??ns(qi(),".moa"),"config.json");os=e=>e.MOA_URL?"MOA_URL is set but MOA_TOKEN is not, and there is no saved profile to take one from":"MOA_TOKEN is set but MOA_URL is not, and there is no saved profile to take one from"});async function Rt(e,t,n,o,r){let s=Date.now()+n*1e3,a=250,i="",c=t,l;for(;;){let d=await e.get(`/v1/jobs/${c}`).catch(async u=>{if(u instanceof bt){if(l??=Date.now()+qo,Date.now()<l&&Date.now()<s)return;throw u}let p=u instanceof re&&u.status===404&&r!==void 0?await r():void 0;if(!p)throw u;return c=p.id,p});if(d===void 0){await new Promise(u=>setTimeout(u,500));continue}if(l=void 0,o&&d.progress){let{at:u,...f}=d.progress,p=JSON.stringify(f);p!==i&&(i=p,o(d.progress))}if(d.state!=="running"&&d.state!=="queued")return d;if(Date.now()>s)throw new Ln(t,n);await new Promise(u=>setTimeout(u,a)),a=Math.min(a*1.5,1e3)}}function an(e,t,n,o=(s,a)=>{process.on(s,a)},r=(s,a)=>{process.off(s,a)}){let s=!1,a=()=>{s&&(n("moa: leaving it running \u2014 see `moa jobs`"),process.exit(2)),s=!0,n(`moa: stopping ${t} \u2014 Ctrl-C again to leave it running`),e.post(`/v1/jobs/${t}/cancel`,{}).catch(()=>{n("moa: could not reach the workspace to stop it")})};return o("SIGINT",a),{stop:()=>r("SIGINT",a),asked:()=>s}}var Ln,qo,cn=Se(()=>{"use strict";Ie();Ln=class extends Error{jobId;constructor(t,n){super(`timed out after ${n}s \u2014 it is still going: ${t}`),this.name="StoppedWaiting",this.jobId=t}},qo=2e4});import{join as io}from"node:path";function Ue(e,t,n,o={}){let r={...e.env};return o.account&&(r.CLOUDFLARE_ACCOUNT_ID=o.account),r.WRANGLER_SEND_METRICS="false",r.NO_COLOR="1",e.exec(e.root?"pnpm":"wrangler",e.root?["exec","wrangler",...n]:n,{cwd:e.root?io(t,"apps","workspace"):t,env:r,timeoutMs:o.timeoutMs??6e4})}function Bt(e){let t=e.search(/[[{]/);if(t!==-1)try{return JSON.parse(e.slice(t))}catch{return}}async function Ks(e,t){let n=await Ue(e,t,["whoami","--json"],{timeoutMs:3e4}),o=Bt(n.stdout||n.stderr);if(!o)return{ok:!1,why:Nn(n.stderr||n.stdout)||"wrangler did not answer"};if(!o.loggedIn)return{ok:!1,why:"wrangler is not logged in",loggedOut:!0};let r=o.accounts??[],s=e.env.CLOUDFLARE_ACCOUNT_ID?.trim(),a=s?r.find(i=>i.id===s):r[0];return a?{ok:!0,account:a,others:r.length-1,...o.email?{email:o.email}:{}}:{ok:!1,why:s?`CLOUDFLARE_ACCOUNT_ID ${s} is not an account this login can use`:"this login has no account"}}async function Js(e,t){let n=t.split(".");for(let o=0;o<n.length-1;o++){let r=n.slice(o).join("."),s;try{s=await(await e.fetch(`${jc}?name=${encodeURIComponent(r)}&type=NS`,{headers:{accept:"application/dns-json"},signal:AbortSignal.timeout(1e4)})).json()}catch(i){return{onCloudflare:!1,why:`DNS did not answer: ${i instanceof Error?i.message:String(i)}`}}let a=(s.Answer??[]).filter(i=>i.type===2).map(i=>i.data);if(a.length!==0)return{zone:r,onCloudflare:a.every(i=>/\.ns\.cloudflare\.com\.?$/i.test(i))}}return{onCloudflare:!1,why:`no DNS zone holds ${t}`}}async function Ws(e,t,n){let o=`https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(t)}`,r=s=>e.fetch(`${o}${s}`,{headers:{authorization:`Bearer ${n}`},signal:AbortSignal.timeout(15e3)});try{let s=await r("/d1/database?per_page=1");if(!s.ok){let c=(await s.json().catch(()=>({}))).errors?.[0]?.message??"";return{ok:!1,why:`${s.status} ${c}`.trim()}}return(await r("/workers/scripts")).ok?{ok:!1,why:"it can reach Workers too \u2014 the container gets this token, so it must be D1 only"}:{ok:!0}}catch(s){return{ok:!1,why:s instanceof Error?s.message:String(s)}}}async function qs(e,t,n){let o=async()=>{let i=await Ue(e,t,["d1","list","--json"],{account:n});return(Bt(i.stdout)??[]).find(l=>l.name===ct)?.uuid},r=await o();if(r)return{ok:!0,id:r,made:!1};let s=await Ue(e,t,["d1","create",ct],{account:n});if(!s.ok)return{ok:!1,why:Nn(s.stderr||s.stdout)||"d1 create failed"};let a=await o();return a?{ok:!0,id:a,made:!0}:{ok:!1,why:`${ct} was made but is not listed`}}async function zs(e,t,n){let o=await Ue(e,t,["r2","bucket","create",lt],{account:n}),r=`${o.stdout}
|
|
5
|
+
${o.stderr}`;return/already exists|already own/i.test(r)?{ok:!0,made:!1}:o.ok?{ok:!0,made:!0}:{ok:!1,why:Nn(r)||"r2 bucket create failed"}}async function tr(e,t,n){let o=await Ue(e,t,["containers","list","--json"],{account:n}),r=o.ok?Bt(o.stdout):void 0;return Array.isArray(r)?{ok:!0,apps:r}:{ok:!1,why:Nn(o.stderr||o.stdout)||"wrangler did not answer"}}function nr(e){let t=e.filter(s=>s.name===Le),n=t.length===1?t[0]:void 0;if(!n||typeof n.id!="string"||n.id==="")return;let o=typeof n.image=="string"?n.image.split("/").at(-1):void 0,r=o?.startsWith(`${Le}:`)?o:void 0;return{id:n.id,...r?{image:r}:{}}}async function Vs(e,t,n){let o=await tr(e,t,n);return o.ok?nr(o.apps):void 0}async function Mn(e,t,n){let o=await Ue(e,t,["containers","images","list","--json"],{account:n}),r=o.ok?Bt(o.stdout):void 0;if(!Array.isArray(r))return{ok:!1,why:Nn(o.stderr||o.stdout)||"wrangler did not answer"};let s=r.filter(a=>typeof a.name=="string"&&a.name.split("/").at(-1)===Le).flatMap(a=>Array.isArray(a.tags)?a.tags:[]).filter(a=>typeof a=="string"&&a!=="").map(a=>`${Le}:${a}`);return{ok:!0,images:[...new Set(s)]}}function Ys(e){let t=io(e.root,"apps","workspace"),n=Ft(e.domain);return{name:Ge,account_id:e.account,main:e.worker??io(t,"dist","worker.js"),compatibility_date:"2026-09-01",compatibility_flags:["nodejs_compat"],routes:[{pattern:n.workspace,custom_domain:!0},{pattern:n.console,custom_domain:!0}],d1_databases:[{binding:"DB",database_name:ct,database_id:e.databaseId}],r2_buckets:[{binding:"STATE",bucket_name:lt}],durable_objects:{bindings:[{name:"WORKSPACE",class_name:"WorkspaceBox"}]},migrations:[{tag:"v1",new_sqlite_classes:["WorkspaceBox"]}],containers:[{class_name:"WorkspaceBox",name:Le,image:e.image??io(t,"Dockerfile.cloudflare"),...e.image?{}:{image_build_context:e.root},instance_type:"standard-4",max_instances:1}],vars:e.vars}}var Ge,ct,lt,Le,$t,er,Ft,Dn,jc,Nn,jn=Se(()=>{"use strict";Ge="fnmoa-workspace",ct="fnmoa-workspace",lt="fnmoa-state",Le=`${Ge}-workspacebox`,$t=6,er=/^(?=.{4,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/,Ft=e=>({workspace:`workspace.${e}`,console:`console.${e}`}),Dn=e=>{let t=Ft(e);return{workspaceUrl:`https://${t.workspace}`,consoleUrl:`https://${t.console}`}};jc="https://cloudflare-dns.com/dns-query";Nn=e=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
6
|
+
`).filter(t=>t.trim()!=="").slice(-2).join(" \xB7 ").slice(0,240)});function Fc(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function co(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),n=atob(t+"=".repeat((4-t.length%4)%4)),o=new Uint8Array(n.length);for(let r=0;r<n.length;r++)o[r]=n.charCodeAt(r);return o}function Bc(e){let t=e.replace(/-----BEGIN [^-]+-----/,"").replace(/-----END [^-]+-----/,"").replace(/\\n/g,"").replace(/\s+/g,"");return co(t.replace(/\+/g,"-").replace(/\//g,"_"))}async function Kc(e){let t=e.trim();if(t.startsWith("{")){let n=JSON.parse(t),{kty:o,crv:r,x:s}=n;return crypto.subtle.importKey("jwk",{kty:o,crv:r,x:s},or,!0,["verify"])}return crypto.subtle.importKey("spki",Bc(t),or,!0,["verify"])}async function Jc(e){let t=await crypto.subtle.exportKey("raw",e),n=await crypto.subtle.digest("SHA-256",t);return Fc(new Uint8Array(n).slice(0,8))}async function Xs(e,t,n={}){let o=e.trim().split(".");if(o.length!==3)return{ok:!1,reason:"malformed"};let[r,s,a]=o,i,c,l;try{i=JSON.parse(new TextDecoder().decode(co(r))),c=JSON.parse(new TextDecoder().decode(co(s))),l=co(a)}catch{return{ok:!1,reason:"malformed"}}let d=i;if(d?.alg!=="EdDSA"||d?.typ!=="JWT")return{ok:!1,reason:"malformed"};if(d.kid!==void 0&&typeof d.kid!="string")return{ok:!1,reason:"malformed"};let u=await Promise.all((Array.isArray(t)?t:[t]).map(w=>typeof w=="string"?Kc(w):w)),f;if(typeof d.kid=="string")for(let w of u)await Jc(w)===d.kid&&(f=w);else u.length===1&&(f=u[0]);if(!f)return{ok:!1,reason:"unknown key"};if(!await crypto.subtle.verify(or,f,l,new TextEncoder().encode(`${r}.${s}`)))return{ok:!1,reason:"bad signature"};if(!Wc(c))return{ok:!1,reason:"malformed"};if(c.iss!==Gc)return{ok:!1,reason:"wrong issuer"};if(n.install!==void 0&&c.install!==n.install)return{ok:!1,reason:"wrong install"};let m=Math.floor((n.now??new Date).getTime()/1e3);return c.iat>m+Hc?{ok:!1,reason:"issued in the future"}:!n.allowExpired&&m>c.exp+(n.leewaySeconds??60)?{ok:!1,reason:"expired"}:{ok:!0,claims:c}}var Gc,or,Hc,Wc,rr=Se(()=>{"use strict";Gc="fnmoa-licence",or={name:"Ed25519"},Hc=300;Wc=e=>{if(!e||typeof e!="object")return!1;let t=e;return typeof t.iss=="string"&&typeof t.sub=="string"&&typeof t.key=="string"&&typeof t.install=="string"&&typeof t.plan=="string"&&(t.seats===null||typeof t.seats=="number")&&Array.isArray(t.targets)&&t.targets.every(n=>typeof n=="string")&&typeof t.iat=="number"&&typeof t.exp=="number"&&(t.lapsed===void 0||typeof t.lapsed=="boolean")&&(t.offline===void 0||typeof t.offline=="boolean")&&(t.grace_until===void 0||typeof t.grace_until=="number")}});function qc(e,t,n){switch(e){case"seats_full":return`All ${t??"available"} workspace seats are in use. Release a workspace on the account page or upgrade your plan.`;case"activation_released":return"This workspace was released. Run `moa setup licence` to activate it again.";case"unknown_activation":return"This install is not activated. Run `moa setup licence`.";case"key_replaced":return"Use the new key from your account page with `moa setup licence`.";case"lapsed":case"not_paid":return"Your subscription is unpaid. Renew it on your account page.";case"target_not_in_plan":return"This target is not included in your plan. Choose another target or upgrade.";case"licence_too_old":return"This licence expired over 30 days ago and can no longer renew. Run `moa setup licence` to activate again.";case"seat_lost":return"This workspace lost its seat. Run `moa setup licence` to take one again, or release another workspace on the account page.";case"bad_licence":return"The licence server did not accept this licence. Run `moa setup licence`.";case"unknown_key":return"The licence server does not know this key. Check it on your account page and run `moa setup licence`.";case"rate_limited":return"Too many licence requests. Wait a minute and try again.";case"not_set_up":return"The licence server is not set up yet. Try again later.";default:return n??`Licence request refused (${e}). Check your account page.`}}async function sr(e,t,n,o){let r=new URL(e);if(r.protocol!=="https:"&&!(r.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)))throw new Error("The licence server must use HTTPS.");let s=await o(new URL(`/v1/${t}`,r),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(15e3),redirect:"error"}),a=await s.json();if(!s.ok){let i=typeof a.error=="string"?a.error:"request_failed";throw new lo(i,s.status,qc(i,typeof a.seats=="number"?a.seats:void 0,typeof a.message=="string"?a.message:void 0))}return a}async function Zs(e,t,n,o=fetch){return Qs(await sr(e,t,n,o))}async function ea(e,t,n=fetch){let o=await sr(e,"device",t,n),{device_code:r,user_code:s,verification_uri_complete:a,expires_in:i,interval:c}=o;if(typeof r!="string"||typeof s!="string"||typeof a!="string")throw new Error("The licence server did not start a sign-in.");return{deviceCode:r,userCode:s,url:a,expiresIn:typeof i=="number"?i:600,interval:typeof c=="number"?c:5}}async function ta(e,t,n=fetch){try{return Qs(await sr(e,"device/token",{device_code:t},n))}catch(o){if(o instanceof lo&&o.code==="authorization_pending")return"pending";throw o}}async function Gn(e,t,n,o){let r=await Xs(e,t,{install:n});if(!r.ok)throw new Error(`Licence refused: ${r.reason}`);if(o&&!r.claims.targets.includes(o))throw new Error("Licence refused: target not in plan");return r.claims}var lo,Qs,na=Se(()=>{"use strict";rr();lo=class extends Error{constructor(n,o,r){super(r);this.code=n;this.status=o}};Qs=e=>{if(typeof e.licence!="string")throw new Error("The licence server returned no signed licence.");return{licence:e.licence,pullToken:typeof e.pullToken=="string"?e.pullToken:null}}});var zc,un,Hn,oa=Se(()=>{"use strict";zc=`-----BEGIN PUBLIC KEY-----
|
|
7
|
+
MCowBQYDK2VwAyEARMFG3IjOWjtjPhlxBemrNtNnN59FZNc3sDDipX1WKQA=
|
|
8
|
+
-----END PUBLIC KEY-----`,un=[zc,""].filter(Boolean),Hn="https://fnmoa.locusgraph.com"});var ra=Se(()=>{na();oa();rr()});import{access as Vc,mkdir as Yc,readFile as Xc,rename as la,writeFile as Qc}from"node:fs/promises";import{homedir as Zc}from"node:os";import{dirname as da,join as pn}from"node:path";function ha(e){if(typeof e!="object"||e===null)return po();let t=e,n=typeof t.answers=="object"&&t.answers!==null?t.answers:{},o={version:1,screen:sa(t.screen)?t.screen:"where",answers:dt({source:Qe(n.source,["image","checkout"]),runner:Qe(n.runner,ir),agentCli:Qe(n.agentCli,["claude","codex"]),tenant:F(n.tenant,64),owner:F(n.owner,64),repo:F(n.repo,140),workspacePort:aa(n.workspacePort),consolePort:aa(n.consolePort),appOrg:F(n.appOrg,39),githubApp:pa(n.githubApp),people:ma(n.people),removed:nl(n.removed),loginsFor:Qe(n.loginsFor,ir),loggedIn:Array.isArray(n.loggedIn)?[...new Set(n.loggedIn.map(g=>Qe(g,["claude","codex"])).filter(g=>g!==void 0))]:void 0,domain:F(n.domain,253),cloudflareAccount:F(n.cloudflareAccount,64),vercelTeam:F(n.vercelTeam,100),vercelPlan:Qe(n.vercelPlan,["hobby","pro","enterprise"]),vercelScope:tl(n.vercelScope),store:Qe(n.store,["postgres","sqlite"]),sandbox:fa(n.sandbox),githubToken:Qe(n.githubToken,["own","pasted"])}),passed:Array.isArray(t.passed)?[...new Set(t.passed.filter(sa))]:[]},r=Qe(t.target,vt);r&&(o.target=r);let s=dt({deployed:ga(t.deployed),sandbox:o.answers.sandbox,cloudflare:ur(t.cloudflare),githubApp:o.answers.githubApp}),a=typeof t.deployments=="object"&&t.deployments!==null?t.deployments:{},i={};for(let g of vt){let k=al(a[g]);k&&(g===r?fo(s)||(s=k):i[g]=k)}let c=(g,k)=>{let O=s[g];if(O===void 0||r===void 0||k===void 0||k===r)return;let P=i[k]??{};if(P[g]!==void 0)return;i[k]={...P,[g]:O};let{[g]:R,...Y}=s;s=Y},l=s.deployed?.runner,d=l==="vercel"?"vercel":l==="cloudflare"?"cloudflare":l&&"local",u=s.githubApp?.consoleUrl,f=u===void 0||u.startsWith("http://localhost:")?"local":u===s.sandbox?.consoleUrl?"vercel":u===s.deployed?.consoleUrl?d:void 0;c("githubApp",f),c("deployed",d),c("sandbox","vercel"),c("cloudflare","cloudflare"),s.deployed&&(o.deployed=s.deployed),s.cloudflare&&(o.cloudflare=s.cloudflare),o.answers=dt({...o.answers,sandbox:s.sandbox,githubApp:s.githubApp}),Object.keys(i).length>0&&(o.deployments=i);let p=typeof t.down=="object"&&t.down!==null?t.down:void 0,m=Qe(p?.target,vt),w=g=>{if(!Array.isArray(g))return;let k=g.map(O=>F(O,300)).filter(O=>O!==void 0);return k.length>0?k:void 0};p&&m&&(o.down=dt({target:m,at:F(p.at)??new Date(0).toISOString(),removed:w(p.removed)??[],left:w(p.left)}));let b=F(t.updatedAt);return b&&(o.updatedAt=b),o}async function il(e,t){let n=pn(da(ot(t)),"local"),o=c=>Vc(pn(n,c)).then(()=>!0,()=>!1);if(!await o(lr))return;let r=new Map;for(let c of[e.answers.githubApp,...vt.map(l=>e.deployments?.[l]?.githubApp)])c&&r.set(c.id,c);let s=[];for(let c of r.values())await o(uo(c))||s.push(c);let[a,...i]=s;!a||i.length>0||await la(pn(n,lr),pn(n,uo(a)))}async function Kn(e){let t;try{t=ha(JSON.parse(await Xc(ot(e),"utf8")))}catch{return po()}return await il(t,e).catch(()=>{}),t}async function Lt(e,t,n=new Date){let o=ot(t);await Yc(da(o),{recursive:!0});let r=`${JSON.stringify({...ha(e),updatedAt:n.toISOString()},null,2)}
|
|
9
|
+
`,s=`${o}.${process.pid}.tmp`;await Qc(s,r,{mode:384}),await la(s,o)}function rt(e,t){if(t===e.target)return e;let{deployed:n,cloudflare:o,deployments:r,...s}=e,{sandbox:a,githubApp:i,...c}=e.answers,l=wa(e),d=t?e.deployments?.[t]:void 0,u={};for(let w of vt){let b=w===e.target?l:w===t?void 0:e.deployments?.[w];b&&(u[w]=b)}let f=e.answers.runner!=="cloudflare"?e.answers.runner:void 0,p=d?.deployed?.runner;return{...s,...t?{target:t}:{},answers:dt({...c,runner:t==="cloudflare"?"cloudflare":f??(p==="node"||p==="compose"?p:void 0),sandbox:d?.sandbox,githubApp:d?.githubApp}),...d?.deployed?{deployed:d.deployed}:{},...d?.cloudflare?{cloudflare:d.cloudflare}:{},...Object.keys(u).length>0?{deployments:u}:{}}}function mo(e,t){if(!t)return"";let{deployed:n}=t;if(e==="vercel"){let o=t.sandbox?.name;return n?`deployed, ${o??n.consoleUrl}`:o?`sandbox ${o} made, not deployed yet`:""}if(e==="cloudflare"){let o=t.cloudflare??n?.cloudflare;if(n)return`deployed, ${o?.worker??n.consoleUrl}`;if(!o)return"";let r=o.worker?`Worker ${o.worker}`:[o.d1?"D1":void 0,o.r2?"R2":void 0].filter(Boolean).join(" and ");return r?`${r} made, not deployed yet`:`account ${o.account} used, not deployed yet`}return n?`deployed, ${n.runner==="compose"?"docker compose":"node"} \u2014 ${n.consoleUrl}`:""}var nt,Bn,vt,ir,el,dr,Kt,fn,po,ot,sa,F,aa,cr,Qe,ua,pa,fa,tl,ma,nl,ar,ol,rl,sl,Fn,ia,ca,ur,ga,fo,al,dt,uo,lr,wa,Jn,pr,ka,fr,ya,ba,$a,K,Ze=Se(()=>{"use strict";nt=["where","checks","github","memory","logins","access","deploy"],Bn={where:"Where should fnMoa run?",checks:"Checks & account",github:"GitHub",logins:"Agent logins (owner only)",memory:"Memory & database",access:"Console access",deploy:"Deploy & done"},vt=["vercel","cloudflare","local"],ir=["node","compose","cloudflare"],el=[...ir,"vercel","image"],dr=20,Kt=50,fn=e=>e.answers.source??(e.deployed&&e.deployed.runner!=="image"?"checkout":"image"),po=()=>({version:1,screen:"where",answers:{},passed:[]}),ot=(e=process.env)=>pn(e.MOA_HOME??pn(Zc(),".moa"),"setup.json"),sa=e=>typeof e=="string"&&nt.includes(e),F=(e,t=200)=>typeof e=="string"&&e.length>0&&e.length<=t?e:void 0,aa=e=>typeof e=="number"&&Number.isInteger(e)&&e>0&&e<65536?e:void 0,cr=e=>typeof e=="number"&&Number.isInteger(e)&&e>0?e:void 0,Qe=(e,t)=>typeof e=="string"&&t.includes(e)?e:void 0,ua=/^[a-z\d](?:[a-z\d-]{0,38})$/i,pa=e=>{if(typeof e!="object"||e===null)return;let t=e,n=cr(t.id),o=F(t.slug,100),r=F(t.clientId,100),s=F(t.settingsUrl);if(!n||!o||!r||!s)return;let a=F(t.consoleUrl);return{id:n,slug:o,clientId:r,settingsUrl:s,...t.deviceFlow===!0?{deviceFlow:!0}:{},...a?{consoleUrl:a}:{}}},fa=e=>{if(typeof e!="object"||e===null)return;let t=e,n=F(t.name,63),o=F(t.workspaceUrl),r=F(t.consoleUrl);if(!(!n||!o||!r))return dt({name:n,workspaceUrl:o,consoleUrl:r,snapshotId:F(t.snapshotId,100),teamId:F(t.teamId,100),projectId:F(t.projectId,100),revision:F(t.revision,64)})},tl=e=>{if(typeof e!="object"||e===null)return;let t=e,n=F(t.teamId,100),o=F(t.projectId,100);return n&&o?{teamId:n,projectId:o}:void 0},ma=e=>{if(!Array.isArray(e))return;let t=new Map;for(let n of e){if(typeof n!="object"||n===null)continue;let o=n,r=typeof o.login=="string"&&ua.test(o.login)?o.login:void 0,s=Qe(o.role,["member","viewer"]),a=o.dailyCapUsd;!r||!s||t.set(r.toLowerCase(),{login:r,role:s,dailyCapUsd:typeof a=="number"&&Number.isFinite(a)&&a>=0?a:dr})}return t.size>0?[...t.values()]:void 0},nl=e=>{if(!Array.isArray(e))return;let t=[...new Set(e.filter(n=>typeof n=="string"&&ua.test(n)).map(n=>n.toLowerCase()))];return t.length>0?t:void 0},ar=/^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/,ol=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,rl=/^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/,sl=/^[a-z0-9]+(?:[._-][a-z0-9]+)*:[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/,Fn=(e,t)=>typeof e=="string"&&t.test(e)?e:void 0,ia=(e,t,n)=>{if(typeof e!="object"||e===null)return;let o=e,r=Fn(o[t],n);if(!(!r||typeof o.made!="boolean"))return{[t]:r,made:o.made,...o.unsure===!0&&!o.made?{unsure:!0}:{}}},ca=e=>Array.isArray(e)?[...new Set(e.map(t=>Fn(t,sl)).filter(t=>t!==void 0))]:[],ur=e=>{if(typeof e!="object"||e===null)return;let t=e,n=Fn(t.account,ar);if(!n)return;let o=ca(t.images),r=ca(t.unsureImages).filter(s=>!o.includes(s));return dt({account:n,worker:Fn(t.worker,ol),containerId:Fn(t.containerId,ar),images:o.length>0?o:void 0,unsureImages:r.length>0?r:void 0,older:t.older===!0?!0:void 0,imagesUnlisted:t.imagesUnlisted===!0?!0:void 0,d1:ia(t.d1,"id",ar),r2:ia(t.r2,"name",rl)})},ga=e=>{if(typeof e!="object"||e===null)return;let t=e,n=Qe(t.runner,el),o=F(t.workspaceUrl),r=F(t.consoleUrl);if(!(!n||!o||!r))return dt({runner:n,workspaceUrl:o,consoleUrl:r,workspacePid:cr(t.workspacePid),consolePid:cr(t.consolePid),workspaceStarted:F(t.workspaceStarted,64),consoleStarted:F(t.consoleStarted,64),consoleFor:F(t.consoleFor),workspaceFor:F(t.workspaceFor),image:F(t.image),proofRunId:F(t.proofRunId),memory:F(t.memory),people:ma(t.people),memoryKept:t.memoryKept===!0?!0:void 0,cloudflare:n==="cloudflare"?ur(t.cloudflare):void 0,at:F(t.at)??new Date(0).toISOString()})},fo=e=>e!==void 0&&(e.deployed!==void 0||e.sandbox!==void 0||e.cloudflare!==void 0||e.githubApp!==void 0),al=e=>{if(typeof e!="object"||e===null)return;let t=e,n=dt({deployed:ga(t.deployed),sandbox:fa(t.sandbox),cloudflare:ur(t.cloudflare),githubApp:pa(t.githubApp)});return fo(n)?n:void 0},dt=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0));uo=e=>`github-app.${e.id}.json`,lr="github-app.json";wa=e=>{let t=dt({deployed:e.deployed,sandbox:e.answers.sandbox,cloudflare:e.cloudflare,githubApp:e.answers.githubApp});return fo(t)?t:void 0},Jn=(e,t)=>t===e.target?wa(e):e.deployments?.[t],pr=e=>vt.filter(t=>fo(Jn(e,t)));ka=(e,t)=>{if(!t)return!1;if(e==="vercel")return t.sandbox!==void 0;if(e==="cloudflare"){let n=t.cloudflare??t.deployed?.cloudflare;return n!==void 0&&(n.worker!==void 0||n.containerId!==void 0||(n.images??[]).length>0||(n.unsureImages??[]).length>0||n.imagesUnlisted===!0||n.d1?.made===!0||n.d1?.unsure===!0||n.r2?.made===!0||n.r2?.unsure===!0)}return!1};fr=e=>nt.indexOf(e),ya=e=>nt[Math.min(fr(e)+1,nt.length-1)],ba=e=>nt[Math.max(fr(e)-1,0)],$a=e=>e==="where"?"":`${fr(e)}/${nt.length-1}`,K="\u2022\u2022\u2022"});import{randomUUID as va}from"node:crypto";import{chmod as cl,mkdir as ll,readFile as dl,rename as ul,writeFile as pl}from"node:fs/promises";import{hostname as fl}from"node:os";import{dirname as Sa,join as ml}from"node:path";import{readFileSync as gl}from"node:fs";async function Wn(e,t){try{let n=JSON.parse(await dl(xa(e,t),"utf8"));if(!/^[A-Za-z0-9_-]{16,64}$/.test(n.install))throw new Error("Invalid install id");return n}catch(n){if(n.code==="ENOENT")return;throw n}}async function mr(e,t,n){let o=xa(e,t);await ll(Sa(o),{recursive:!0,mode:448});let r=`${o}.${va()}.tmp`;await pl(r,`${JSON.stringify(n)}
|
|
10
|
+
`,{mode:384}),await cl(r,384),await ul(r,o)}async function Aa(e){if(!Hn||un.length===0)throw new Error("This build has no licence-server URL or verification key. Ask the publisher for a configured release.");let t=e.state.target;if(!t)throw new Error("Choose a deployment target first.");let n=await Wn(e.sys.env,t),o=n??{install:va()};return n||await mr(e.sys.env,t,o),{target:t,saved:o}}async function gr(e,t){let{target:n,saved:o}=await Aa(e),r=await Zs(Hn,"activate",{key:t,install:o.install,version:Ut(),target:n},e.sys.fetch);if((await Gn(r.licence,un,o.install,n)).lapsed)throw new Error("The subscription is unpaid. Renew it before installing or updating.");let a={install:o.install,key:t,licence:r.licence};return await mr(e.sys.env,n,a),a}async function go(e,t,n){let{target:o,saved:r}=await Aa(e),s=await ea(Hn,{install:r.install,version:Ut(),target:o,machine:fl().slice(0,64)},e.sys.fetch);t({url:s.url,code:s.userCode}),e.sys.exec(process.platform==="darwin"?"open":"xdg-open",[s.url]);let a=Date.now()+s.expiresIn*1e3;for(;Date.now()<a;){if(await e.sys.sleep(s.interval*1e3),n.aborted)throw new Error("Stopped before it was approved.");let i=await ta(Hn,s.deviceCode,e.sys.fetch);if(i==="pending")continue;if((await Gn(i.licence,un,r.install,o)).lapsed)throw new Error("The subscription is unpaid. Renew it before installing or updating.");let l={install:r.install,licence:i.licence};return await mr(e.sys.env,o,l),l}throw new Error("The code ran out before it was approved. Press c to try again.")}function Ut(){return hl}async function ut(e){if(Ea(e)&&!e.secrets.licence)return[];try{let t=e.state.target;if(!t)throw new Error("Choose a target first.");let n=await Wn(e.sys.env,t);if(!e.secrets.licence&&n?.licence){let a=await Gn(n.licence,un,n.install,t).then(i=>i.lapsed?void 0:i).catch(()=>{});if(a)return[{label:"licence",mark:"ok",detail:`${a.plan} - install ${n.install}`}]}let o=e.secrets.licence??n?.key;if(!o)throw new Error("Not connected: press c to connect your fnMoa account in the browser, or l to paste a licence key.");let r=await gr(e,o);return[{label:"licence",mark:"ok",detail:`${(await Gn(r.licence,un,r.install,t)).plan} - install ${r.install}`}]}catch(t){return[{label:"licence",mark:"fail",detail:t instanceof Error?t.message:String(t),fix:"Check your account or run `moa setup licence`."}]}}async function Dt(e){if(!e.state.target)return{};let t=await Wn(e.sys.env,e.state.target);if(!t?.licence){if(Ea(e))return{};throw new Error("Activate this install with `moa setup licence`.")}return{MOA_LICENCE:t.licence,MOA_INSTALL_ID:t.install}}var xa,hl,Ea,St=Se(()=>{"use strict";ra();Ze();xa=(e,t)=>ml(Sa(ot(e)),`licence-${t}.json`);hl=JSON.parse(gl(new URL(import.meta.url.endsWith("/main.js")?"../package.json":"../../package.json",import.meta.url),"utf8")).version,Ea=e=>!!e.sys.root&&fn(e.state)==="checkout"});import{join as wl}from"node:path";import{parseEnv as kl}from"node:util";async function _a(e,t){let n=await e.exec("docker",xt(t,"build","workspace"),{cwd:t,env:Jt(e),timeoutMs:12e5});return n.ok?{ok:!0}:{ok:!1,why:ho(n.stderr||n.stdout)||"no answer"}}function Jt(e,t){let n={...e.env,MOA_TOKENS:"setup-check:setup"};for(let o of[...Ta,"MOA_CLI_HOME","LOCUSGRAPH_AGENT_SECRET"])delete n[o];return t&&(n.CLAUDE_CODE_OAUTH_TOKEN=t),n}async function hr(e,t,n){let o;if(n==="here"){let r={...e.env};for(let s of Ta)delete r[s];o=await e.exec("claude",["-p",mn],{env:{...r,CLAUDE_CODE_OAUTH_TOKEN:t},timeoutMs:12e4})}else o=await e.exec("docker",Pa(n.container,"claude","-p",mn),{cwd:n.container,env:Jt(e,t),timeoutMs:12e5});return o.ok&&/\bok\b/i.test(o.stdout)?{ok:!0}:{ok:!1,why:ho(o.stdout||o.stderr)||"no answer"}}async function Oa(e,t){let n=await e.exec("docker",Pa(t,"codex","login","status"),{cwd:t,env:Jt(e),timeoutMs:12e5}),o=`${n.stdout}
|
|
11
|
+
${n.stderr}`;return n.ok?{loggedIn:!0,says:yl(n.stdout)||"logged in"}:/not logged in/i.test(o)?{loggedIn:!1,says:"not logged in"}:{loggedIn:!1,says:"",broken:ho(o)||"the container did not start"}}function qn(e){let t=e.replace(bl,""),n=/https:\/\/\S*(device|auth)\S*/i.exec(t);if(!n)return{};let o=t.slice(n.index+n[0].length),r=/\b[A-Z0-9]{4,}-[A-Z0-9]{4,}\b/.exec(o)?.[0];return{url:n[0].replace(/[).,]+$/,""),...r?{code:r}:{}}}function Ra(e,t,n){let o="",r=!1,s,a=Jt(e),i=()=>e.exec("docker",["rm","-f",Ca],{cwd:t,env:a,timeoutMs:3e4});return{done:i().then(()=>r?{ok:!1,stdout:"",stderr:"stopped"}:(s=e.run("docker",xt(t,"run","--rm","--no-deps","-T","--name",Ca,"workspace","codex","login","--device-auth"),{cwd:t,env:a,timeoutMs:16*6e4},l=>{o+=l,n(qn(o))}),s.done)),cancel:()=>{r=!0,s?.cancel(),i()}}}async function gn(e,t,n){let o={Authorization:`Bearer ${t.secret}`,"Content-Type":"application/json"},r=async(c,l,d=!1)=>{try{let u=await e.fetch(c,{...l,headers:o,signal:AbortSignal.timeout(15e3)});if(u.ok||d&&u.status===409)return{ok:!0};let f=(await u.text().catch(()=>"")).split(t.secret).join("\u2022\u2022\u2022");return{ok:!1,why:`${u.status} ${ho(f)}`.trim()}}catch(u){return{ok:!1,why:u instanceof Error?u.message:String(u)}}},s=encodeURIComponent(t.graph),a=await r(`${t.server}/v1/contexts/${s}?type=workspace&page_size=1`,{method:"GET"});if(!a.ok)return{read:a,write:{ok:!1,why:"not tried"}};let i=await r(`${t.server}/v1/events`,{method:"POST",body:JSON.stringify({graph_id:t.graph,context_id:`workspace:${n}`,event_kind:"fact",source:"system",payload:{data:`The ${n} workspace`}})},!0);return{read:a,write:i}}function La(e){let t={};for(let[n,o]of Object.entries(kl(e??"")))o&&(t[n]=o);return t}var ue,xt,Pa,mn,Ta,ho,yl,bl,Ca,Ia,Wt=Se(()=>{"use strict";ue="fnmoa-setup",xt=(e,...t)=>["compose","--env-file","/dev/null","-f",wl(e,"apps","workspace","compose.yaml"),"-p",ue,...t],Pa=(e,...t)=>xt(e,"run","--rm","--no-deps","-T","workspace",...t);mn="Reply with the single word ok and nothing else.",Ta=["ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","CLAUDE_CODE_OAUTH_TOKEN"];ho=e=>e.trim().split(`
|
|
12
|
+
`).slice(-2).join(" \xB7 ").slice(0,200);yl=e=>e.trim().split(`
|
|
13
|
+
`)[0]?.trim()??"",bl=new RegExp("\x1B\\[[0-9;?]*[A-Za-z]","g");Ca=`${ue}-codex-login`;Ia="https://api.locusgraph.com"});import{randomBytes as $l}from"node:crypto";import{mkdir as vl,readFile as Sl,rm as wr,writeFile as xl}from"node:fs/promises";import{dirname as Al,join as El}from"node:path";async function Ua(e,t,n,o){let r=wo(e,t);await vl(Al(r),{recursive:!0}),await wr(r,{force:!0}),await xl(r,JSON.stringify({url:n,token:o}),{mode:384})}async function Da(e,t,n){try{let o=JSON.parse(await Sl(wo(e,t),"utf8"));return o.url===n&&typeof o.token=="string"&&Cl.test(o.token)?o.token:void 0}catch{return}}async function Mt(e,t,n){let o=await Pe(e.env),r=o.profiles[t],s=o.current,a=!!o.profiles[s],i=await Da(e,t,n),c=r?.url===n?r.token:void 0;if(i&&c)return s!==t&&await it({...o,current:t},e.env),{token:i,pending:!0,before:s,had:a};let l=i??c??kr();return(r?.token!==l||r?.url!==n||s!==t)&&await it({current:t,profiles:{...o.profiles,[t]:{url:n,token:l}}},e.env),i&&await wr(wo(e,t),{force:!0}),{token:l,pending:!1,before:s,had:a}}async function Nt(e,t,n,o){if(await Da(e,t,n)!==o)return;let r=await Pe(e.env);await it({...r,profiles:{...r.profiles,[t]:{url:n,token:o}}},e.env),await wr(wo(e,t),{force:!0})}var Cl,kr,wo,hn=Se(()=>{"use strict";Ye();qe();Cl=/^moa_[A-Za-z0-9_-]{16,128}$/,kr=()=>`moa_${$l(24).toString("base64url")}`,wo=(e,t)=>ie(e,El("rotate",`${t}.json`))});import{Writable as Pl}from"node:stream";function ft(e){return e==="hobby"?{vcpus:4,memoryGb:8,sessionMs:45*6e4,maxRuns:1}:{vcpus:8,memoryGb:16,sessionMs:24*36e5,maxRuns:3}}function Ga(e){let t=e.memoryGb*24*ko.memoryGbHour;return{idle:t,busy:t+e.vcpus*24*ko.cpuHour,perGb:ko.transferGb,storagePerGbMonth:ko.storageGbMonth}}async function Ba(e,t,n){let o=Object.entries(t).filter(a=>a[1]!==void 0),r={MOA_KEYS:o.map(([a])=>a).join(" "),MOA_DROP:n.join(" "),START:Tl};o.forEach(([,a],i)=>{r[`V_${i}`]=a});let s=await _e(e,Ol,r);return s.ok?{ok:!0,changed:s.stdout.trim().endsWith("1")}:{ok:!1,changed:!1,why:mt(s.stderr||s.stdout)}}async function br(e,t){if(t){let n=await _e(e,`exec sh "${pt}/start.sh" stop`,{},12e4);if(!n.ok)throw new Error(mt(n.stderr||n.stdout)||"the servers did not stop")}await e.spawn("sh",["-c",`exec sh "${pt}/start.sh"${t?" restart":""}`])}async function Ka(e){await _e(e,`rm -f "${pt}/restart"`)}async function $o(e){let t=await _e(e,`if [ -f "${pt}/start.sh" ]; then exec sh "${pt}/start.sh" stop; fi`,{},12e4);if(!t.ok)throw new Error(mt(t.stderr||t.stdout)||"the servers did not stop")}function Na(e){return{writeFiles:t=>e.writeFiles(t),name:e.name,status:e.status,...e.vcpus?{vcpus:e.vcpus}:{},domain:t=>e.domain(t),exec:async(t,n,o={})=>{try{let r=await e.runCommand({cmd:t,args:n,...o.env?{env:o.env}:{},...o.sudo?{sudo:!0}:{},signal:AbortSignal.timeout(o.timeoutMs??6e5)});return{ok:r.exitCode===0,stdout:await r.stdout(),stderr:await r.stderr()}}catch(r){return{ok:!1,stdout:"",stderr:yr(r)}}},spawn:async(t,n)=>{await e.runCommand({cmd:t,args:n,detached:!0})},stream:(t,n,o)=>{let r="",s="",a=new AbortController;return{done:e.runCommand({cmd:t,args:n,stdout:Ma(c=>{r+=c,o(c)}),stderr:Ma(c=>{s+=c,o(c)}),signal:a.signal}).then(c=>({ok:c.exitCode===0,stdout:r,stderr:s}),c=>({ok:!1,stdout:r,stderr:s||yr(c)})),cancel:()=>a.abort()}},snapshot:async()=>(await e.snapshot()).snapshotId,stop:async()=>{await e.stop()},delete:async()=>{await e.delete({deleteOrphanSnapshots:!0})}}}async function Rl(e,t,n){let o=await e(`https://api.vercel.com/v2/teams/${encodeURIComponent(n)}`,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(15e3)});if(!o.ok)throw new Error(`Vercel answered ${o.status} for team ${n}`);let s=(await o.json()).billing?.plan;return s==="pro"||s==="enterprise"?s:"hobby"}function Wa(e){let{env:t}=e,n=o=>({token:o.token,teamId:o.teamId,projectId:o.projectId});return{account:async(o,r)=>{let s=await e.exec("vercel",["whoami"]),a=t.VERCEL_TOKEN?.trim();if(!a){if(s.missing)throw new Error("the vercel CLI is not installed");if(!s.ok)throw new Error("not signed in to Vercel");let{getAuth:f}=await import("@vercel/sandbox/dist/auth/index.js");if(a=f()?.token,!a)throw new Error("no Vercel token found for the vercel CLI's login")}let{inferScope:i}=await import("@vercel/sandbox/dist/auth/index.js"),c=t.VERCEL_TEAM_ID?.trim(),l=t.VERCEL_PROJECT_ID?.trim(),d=o??c,u=r?{...r,teamSlug:o??r.teamId}:d&&l&&(!o||o===c)?{teamId:d,projectId:l,teamSlug:d}:await i({token:a,cwd:e.scratch,...d?{teamId:d}:{}});return{user:s.stdout.trim().split(`
|
|
14
|
+
`).pop()?.trim()||"you",team:u.teamSlug??u.teamId,teamId:u.teamId,projectId:u.projectId,plan:await Rl(e.fetch,a,u.teamId),token:a}},get:async(o,r,s=!1)=>{let{Sandbox:a}=await import("@vercel/sandbox");try{return Na(await a.get({name:r,resume:s,...n(o)}))}catch(i){if(ja(i))return;throw i}},create:async(o,r)=>{let{Sandbox:s}=await import("@vercel/sandbox"),a={name:r.name,persistent:!0,ports:[st,qt],resources:{vcpus:r.size.vcpus},timeout:r.size.sessionMs,...r.env?{env:r.env}:{},...n(o)};return Na(await s.create(r.snapshotId?{...a,source:{type:"snapshot",snapshotId:r.snapshotId}}:r.image?{...a,image:r.image}:a))},deleteSnapshot:async(o,r)=>{let{Snapshot:s}=await import("@vercel/sandbox");try{await(await s.get({snapshotId:r,...n(o)})).delete()}catch(a){if(!ja(a))throw a}}}}var st,qt,zn,ko,yo,Ha,bo,Fa,pt,_l,Tl,Ol,_e,mt,wn,vo,Ja,yr,Ma,ja,zt=Se(()=>{"use strict";st=8787,qt=3100,zn=e=>`fnmoa-${e}`.slice(0,63);ko={cpuHour:.128,memoryGbHour:.0212,transferGb:.15,storageGbMonth:.08};yo=e=>`$${e<10?e.toFixed(2):e.toFixed(0)}`,Ha=(e,t)=>`${e==="hobby"?"Hobby":e==="pro"?"Pro":"Enterprise"} \u2014 ${t.vcpus} vCPU / ${t.memoryGb} GB, ${t.sessionMs>=36e5?`${t.sessionMs/36e5}h sessions`:`${t.sessionMs/6e4}-minute sessions`}, ${t.maxRuns===1?"one step at a time":`${t.maxRuns} steps side by side`}`,bo=e=>{let t=Ga(e);return`~${yo(t.idle)} a day idle, ~${yo(t.busy)} a day busy, + ${yo(t.perGb)} per GB served from its URLs (downloads free) \u2014 list prices`},Fa=e=>`+ ${yo(Ga(e).storagePerGbMonth)} per GB a month for its kept disk and its snapshot, stopped or not`,pt="$HOME/moa",_l=`set -eu
|
|
15
|
+
M="${pt}"
|
|
16
|
+
mkdir -p "$M"
|
|
17
|
+
missing() { ! command -v "$1" > /dev/null 2>&1; }
|
|
18
|
+
UPDATED=""
|
|
19
|
+
pkg() {
|
|
20
|
+
if command -v apt-get > /dev/null 2>&1; then
|
|
21
|
+
if [ -z "$UPDATED" ]; then sudo apt-get update -q; UPDATED=1; fi
|
|
22
|
+
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y -q "$@"
|
|
23
|
+
else
|
|
24
|
+
sudo dnf install -y -q "$@"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
if missing git; then pkg git; fi
|
|
28
|
+
if missing gh; then pkg gh; fi
|
|
29
|
+
if missing pg_ctl && ! ls /usr/lib/postgresql/*/bin/pg_ctl > /dev/null 2>&1; then
|
|
30
|
+
if command -v apt-get > /dev/null 2>&1; then pkg postgresql; else pkg postgresql-server; fi
|
|
31
|
+
fi
|
|
32
|
+
if missing claude; then sudo npm install -g --no-audit --no-fund @anthropic-ai/claude-code; fi
|
|
33
|
+
if missing codex; then sudo npm install -g --no-audit --no-fund @openai/codex; fi
|
|
34
|
+
if missing pnpm; then sudo npm install -g --no-audit --no-fund pnpm@12; fi
|
|
35
|
+
# A private checkout needs GitHub credentials: the token this deploy passes, or the one an earlier deploy kept.
|
|
36
|
+
if [ -z "\${GH_TOKEN:-}" ] && [ -s "$M/env/workspace/GH_TOKEN" ]; then GH_TOKEN=$(cat "$M/env/workspace/GH_TOKEN"); fi
|
|
37
|
+
if [ -n "\${GH_TOKEN:-}" ]; then export GH_TOKEN; gh auth setup-git > /dev/null; fi
|
|
38
|
+
if [ ! -d "$M/fnmoa/.git" ]; then git clone -q "$MOA_SOURCE" "$M/fnmoa"; fi
|
|
39
|
+
cd "$M/fnmoa"
|
|
40
|
+
git fetch -q "$MOA_SOURCE" "$MOA_REVISION"
|
|
41
|
+
git checkout -q --force FETCH_HEAD
|
|
42
|
+
pnpm install --frozen-lockfile
|
|
43
|
+
pnpm --filter "@fnmoa/workspace..." run build
|
|
44
|
+
pnpm --filter @fnmoa/console run build
|
|
45
|
+
printf %s "$MOA_REVISION" > "$M/image"
|
|
46
|
+
`,Tl=`#!/bin/sh
|
|
47
|
+
M="${pt}"
|
|
48
|
+
R="$M/run/$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || echo boot)"
|
|
49
|
+
mkdir -p "$M/logs" "$R" "$M/data/worktrees"
|
|
50
|
+
load() { for f in "$M/env/$1"/*; do if [ -f "$f" ]; then export "$(basename "$f")=$(cat "$f")"; fi; done; }
|
|
51
|
+
running() { [ -f "$R/$1.pid" ] && kill -0 "$(cat "$R/$1.pid")" 2> /dev/null; }
|
|
52
|
+
end() {
|
|
53
|
+
if running "$1"; then
|
|
54
|
+
pid=$(cat "$R/$1.pid")
|
|
55
|
+
kill "$pid"
|
|
56
|
+
for _ in $(seq 1 30); do kill -0 "$pid" 2> /dev/null || break; sleep 1; done
|
|
57
|
+
if kill -0 "$pid" 2> /dev/null; then
|
|
58
|
+
kill -9 "$pid"
|
|
59
|
+
for _ in $(seq 1 10); do kill -0 "$pid" 2> /dev/null || break; sleep 1; done
|
|
60
|
+
fi
|
|
61
|
+
# Still alive: keep its pid, and never start another on the same database.
|
|
62
|
+
if kill -0 "$pid" 2> /dev/null; then echo "$1 (pid $pid) did not stop" >&2; return 1; fi
|
|
63
|
+
fi
|
|
64
|
+
rm -f "$R/$1.pid"
|
|
65
|
+
}
|
|
66
|
+
case "\${1:-}" in
|
|
67
|
+
stop) end console && end workspace; exit $? ;;
|
|
68
|
+
restart) end console || exit 1; end workspace || exit 1 ;;
|
|
69
|
+
esac
|
|
70
|
+
if [ "$(cat "$M/env/store" 2> /dev/null)" = postgres ]; then
|
|
71
|
+
PG=$(dirname "$(command -v pg_ctl 2> /dev/null || ls -d /usr/lib/postgresql/*/bin/pg_ctl | tail -n 1)")
|
|
72
|
+
[ -f "$M/pg/PG_VERSION" ] || "$PG/initdb" -D "$M/pg" -U moa --auth=trust >> "$M/logs/postgres.log" 2>&1
|
|
73
|
+
if ! "$PG/pg_isready" -q -h 127.0.0.1 -p 5432; then
|
|
74
|
+
# Stopped with the sandbox, not shut down: its lock file names a pid of the last boot.
|
|
75
|
+
rm -f "$M/pg/postmaster.pid"
|
|
76
|
+
"$PG/pg_ctl" -D "$M/pg" -w -l "$M/logs/postgres.log" -o "-c listen_addresses=127.0.0.1 -c unix_socket_directories=$R" start
|
|
77
|
+
fi
|
|
78
|
+
DATABASE="postgres://moa@127.0.0.1:5432/postgres"
|
|
79
|
+
else
|
|
80
|
+
DATABASE="sqlite:$M/data/workspace.db"
|
|
81
|
+
fi
|
|
82
|
+
if ! running workspace; then
|
|
83
|
+
(
|
|
84
|
+
load workspace
|
|
85
|
+
export MOA_DATABASE_URL="$DATABASE" MOA_WORKSPACE_ROOT="$M/data/worktrees"
|
|
86
|
+
case "$DATABASE" in sqlite:*) export MOA_SQLITE_FILE="\${DATABASE#sqlite:}" ;; esac
|
|
87
|
+
if [ -n "\${GH_TOKEN:-}" ]; then
|
|
88
|
+
gh auth setup-git > /dev/null 2>&1
|
|
89
|
+
if [ -z "$(git config --global user.email)" ]; then
|
|
90
|
+
login=$(gh api user --jq .login 2> /dev/null)
|
|
91
|
+
id=$(gh api user --jq .id 2> /dev/null)
|
|
92
|
+
if [ -n "$login" ]; then
|
|
93
|
+
git config --global user.name "$login"
|
|
94
|
+
git config --global user.email "$id+$login@users.noreply.github.com"
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
cd "$M/fnmoa/apps/workspace" && exec node dist/node.js
|
|
99
|
+
) >> "$M/logs/workspace.log" 2>&1 &
|
|
100
|
+
echo $! > "$R/workspace.pid"
|
|
101
|
+
fi
|
|
102
|
+
if ! running console; then
|
|
103
|
+
(
|
|
104
|
+
load console
|
|
105
|
+
cd "$M/fnmoa/apps/console" && exec node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port ${qt}
|
|
106
|
+
) >> "$M/logs/console.log" 2>&1 &
|
|
107
|
+
echo $! > "$R/console.pid"
|
|
108
|
+
fi
|
|
109
|
+
wait
|
|
110
|
+
`,Ol=`set -eu
|
|
111
|
+
M="${pt}"
|
|
112
|
+
umask 077
|
|
113
|
+
changed=0
|
|
114
|
+
i=0
|
|
115
|
+
for path in $MOA_KEYS; do
|
|
116
|
+
value=$(printenv "V_$i")
|
|
117
|
+
i=$((i + 1))
|
|
118
|
+
mkdir -p "$(dirname "$M/env/$path")"
|
|
119
|
+
if [ ! -f "$M/env/$path" ] || [ "$(cat "$M/env/$path")" != "$value" ]; then
|
|
120
|
+
printf %s "$value" > "$M/env/$path"
|
|
121
|
+
changed=1
|
|
122
|
+
fi
|
|
123
|
+
done
|
|
124
|
+
for path in \${MOA_DROP:-}; do
|
|
125
|
+
if [ -f "$M/env/$path" ]; then rm -f "$M/env/$path"; changed=1; fi
|
|
126
|
+
done
|
|
127
|
+
if [ ! -f "$M/start.sh" ] || [ "$(cat "$M/start.sh")" != "$(printf %s "$START")" ]; then
|
|
128
|
+
printf %s "$START" > "$M/start.sh"
|
|
129
|
+
chmod 700 "$M/start.sh"
|
|
130
|
+
changed=1
|
|
131
|
+
fi
|
|
132
|
+
if [ "$changed" = 1 ]; then : > "$M/restart"; fi
|
|
133
|
+
if [ -f "$M/restart" ]; then echo 1; else echo 0; fi
|
|
134
|
+
`,_e=(e,t,n={},o)=>e.exec("sh",["-c",t],{env:n,...o?{timeoutMs:o}:{}});mt=(e,t=2)=>e.trim().split(`
|
|
135
|
+
`).slice(-t).join(" \xB7 ").slice(0,300);wn=async(e,t)=>{let n=await _e(e,`tail -n 5 "${pt}/logs/${t}.log" 2> /dev/null`);return mt(n.stdout,3)},vo=async e=>{let t=await _e(e,`cat "${pt}/image" 2> /dev/null`);return t.ok&&t.stdout.trim()||void 0},Ja=(e,t,n,o)=>_e(e,_l,{MOA_SOURCE:t,MOA_REVISION:n,...o?{GH_TOKEN:o}:{}},18e5),yr=e=>(e instanceof Error?e.message:String(e)).split(`
|
|
136
|
+
`)[0]?.slice(0,300)??"",Ma=e=>new Pl({write(t,n,o){e(String(t)),o()}});ja=e=>e.response?.status===404||/not.?found|404/i.test(yr(e))});import{execFile as Il,execFileSync as qa,spawn as za}from"node:child_process";import{existsSync as $r,mkdirSync as Ll,openSync as Ul,readFileSync as Dl}from"node:fs";import{connect as Ml}from"node:net";import{dirname as vr,join as Sr,resolve as Nl}from"node:path";import{fileURLToPath as jl}from"node:url";function At(e,t,n,o){let r=e.processOf(t);return r?n!==void 0?r.started===n:e.root!==void 0&&r.command.includes(e.root)?!0:r.command.startsWith("next-server")&&o!==void 0&&e.portsOf(t)?.includes(o)===!0:!1}function gt(e){try{let t=Number(new URL(e).port);return Number.isInteger(t)&&t>0?t:void 0}catch{return}}function Bl(e=process.cwd()){let t=vr(jl(import.meta.url));for(let n of[e,t]){let o=Nl(n);for(;;){if(Fl(o))return o;let r=vr(o);if(r===o)break;o=r}}}function kn(e=process.env){let t={env:e,root:Bl(),exec:(n,o,r={})=>new Promise(s=>{Il(n,o,{cwd:r.cwd,env:r.env??e,timeout:r.timeoutMs??15e3,maxBuffer:16777216},(a,i,c)=>{let l=a?.code;s({ok:a===null,stdout:String(i),stderr:String(c),...l==="ENOENT"?{missing:!0}:{}})})}),run:(n,o,r,s)=>{let a="",i="",c=za(n,o,{cwd:r.cwd,env:r.env??e,stdio:["ignore","pipe","pipe"]}),l=setTimeout(()=>c.kill("SIGTERM"),r.timeoutMs??9e5);return c.stdout.on("data",u=>{a+=String(u),s(String(u))}),c.stderr.on("data",u=>{i+=String(u),s(String(u))}),{done:new Promise(u=>{c.once("error",f=>{clearTimeout(l),u({ok:!1,stdout:a,stderr:i||f.message,...f.code==="ENOENT"?{missing:!0}:{}})}),c.once("close",f=>{clearTimeout(l),u({ok:f===0,stdout:a,stderr:i})})}),cancel:()=>c.kill("SIGTERM")}},exists:$r,read:n=>{try{return Dl(n,"utf8")}catch{return}},listening:n=>new Promise(o=>{let r=Ml({host:"127.0.0.1",port:n}),s=a=>{r.destroy(),o(a)};r.once("connect",()=>s(!0)),r.once("error",()=>s(!1)),r.setTimeout(1e3,()=>s(!1))}),alive:n=>{try{return process.kill(n,0),!0}catch(o){return o.code==="EPERM"}},processOf:n=>{try{let o=qa("ps",["-o","lstart=,command=","-p",String(n)],{env:{...e,LC_ALL:"C",TZ:"UTC"},encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}),r=Gl.exec(o.trim().split(`
|
|
137
|
+
`)[0]??"");return r?.[1]&&r[2]!==void 0?{started:r[1],command:r[2]}:void 0}catch{return}},portsOf:n=>{try{let r=qa("lsof",["-nP","-a","-p",String(n),"-iTCP","-sTCP:LISTEN","-Fn"],{env:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).split(`
|
|
138
|
+
`).map(s=>Hl.exec(s)?.[1]).filter(s=>s!==void 0).map(Number);return[...new Set(r)]}catch(o){return o.status===1?[]:void 0}},start:(n,o,r)=>{Ll(vr(r.log),{recursive:!0});let s=Ul(r.log,"a"),a=za(n,o,{cwd:r.cwd,env:r.env,detached:!0,stdio:["ignore",s,s]});if(a.unref(),a.pid===void 0)throw new Error(`could not start ${n}`);return a.pid},stop:(n,o)=>{for(let r of[-n,n])try{if(process.kill(r,o?"SIGKILL":"SIGTERM"),!o)return}catch{}},fetch:(...n)=>fetch(...n),sleep:n=>new Promise(o=>setTimeout(o,n))};return t.vercel=Wa({env:e,exec:(n,o)=>t.exec(n,o),fetch:t.fetch,scratch:Sr(ot(e),"..","local")}),t}var Gl,Hl,Fl,Vn=Se(()=>{"use strict";Ze();zt();Gl=/^\s*(\w{3} \w{3} [ \d]\d \d\d:\d\d:\d\d \d{4})\s+(.*)$/,Hl=/^n.*:(\d+)$/,Fl=e=>$r(Sr(e,"pnpm-workspace.yaml"))&&$r(Sr(e,"apps","workspace","compose.yaml"))});import{createHash as Kl}from"node:crypto";import{join as yn}from"node:path";async function Va(e,t,n){let o=Date.now()+n*1e3;for(;Date.now()<o;){if(await e.fetch(t,{signal:AbortSignal.timeout(2e3)}).then(()=>!0).catch(()=>!1))return!0;await e.sleep(500)}return!1}async function Jl(e,t,n){let o=Date.now()+n*1e3;for(;Date.now()<o;){if(!await e.listening(t))return!0;await e.sleep(500)}return!1}async function Wl(e,t,n){let o=Date.now()+n*1e3;for(;Date.now()<o;){if(!e.alive(t))return!0;await e.sleep(500)}return!1}async function So(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=ne=>{let Ce=s.findIndex(le=>le.label===ne.label);Ce===-1?s.push(ne):s[Ce]=ne,t([...s])},i,c=(ne,Ce,le)=>(a({label:ne,mark:"fail",detail:Ce,fix:le}),i?{ok:!1,deployed:i}:{ok:!1}),l=o.root;if(!l||De(e)||r.deployed?.runner==="image")return Ar(e,t,n);if(Zt(e))return c("memory","the last deploy kept memory in LocusGraph, and this run has no secret for it","Esc back to Memory & database and paste it again \u2014 or go on there without it");let d=r.answers.runner??"node",u=r.answers.agentCli??"claude",f=d==="compose"&&r.answers.loginsFor==="compose"?[...new Set([u,...r.answers.loggedIn??[]])]:[u],p=ze(e)?.token,m=ke(e),w=r.answers.tenant??xe(r.answers.owner),b=jt(r),g=Et(r),k=`http://127.0.0.1:${b}`,O=`http://localhost:${g}`,P=r.answers.owner,R=r.answers.githubApp,Y=R?Me(o,R):void 0;if(R&&!Y)return c("GitHub App",`the client secret for ${R.slug} is not on this machine`,"Esc back to Console access and press g to create a new app");let we=R&&Y?{AUTH_GITHUB_ID:R.clientId,AUTH_GITHUB_SECRET:Y.clientSecret,AUTH_SECRET:Y.authSecret,AUTH_URL:O}:{};if(d==="compose"&&f.includes("claude")&&!p)return c("claude token","none in this run \u2014 it is never saved","Esc back to Agent logins and press t (or choose codex there), then Enter");let I=yn(l,"apps","workspace","dist","node.js"),X=yn(l,"apps","console",".next"),U=o.exists(I),H=o.exists(yn(X,"BUILD_ID"));if(U&&H)a({label:"build",mark:"ok",detail:"workspace and console are built"});else{if(!H&&o.exists(X))return c("build","apps/console/.next holds no build \u2014 `next dev` may be using it, and a build would write over it",`stop the console's dev server, then run \`pnpm --filter @fnmoa/console build\` in ${l}, then Enter`);let ne=[...U?[]:[{what:"workspace",args:["--filter","@fnmoa/workspace...","run","build"]}],...H?[]:[{what:"console",args:["--filter","@fnmoa/console","run","build"]}]],Ce=ne.map(Je=>Je.what).join(" and ");a({label:"build",mark:"wait",detail:`building the ${Ce} \u2014 a few minutes`});let le=await o.exec("pnpm",["install"],{cwd:l,timeoutMs:6e5});if(!le.ok)return c("build",`pnpm install: ${$n(le.stderr||le.stdout)}`,`run \`pnpm install\` in ${l}, then Enter`);for(let Je of ne){let tt=await o.exec("pnpm",Je.args,{cwd:l,timeoutMs:9e5});if(!tt.ok)return c("build",`${Je.what}: ${$n(tt.stderr||tt.stdout)}`,`run \`pnpm ${Je.args.join(" ")}\` in ${l}, then Enter`)}a({label:"build",mark:"ok",detail:`${Ce} built`})}let{token:W,pending:de,before:_,had:Te}=await Mt(o,bn,k);a({label:"owner token",mark:"ok",detail:`${K} \u2014 profile ${bn} in ~/.moa/config.json${_===bn?"":Te?`; \`moa\` now talks to this workspace, not profile ${_} (\`moa use ${_}\` to go back)`:"; `moa` now talks to this workspace"}`});let pe=D({url:k,token:W},o.fetch),T=r.deployed?Sn(o,r.deployed):void 0,Ae=ql({runner:d,workspaceUrl:k,tenant:w,token:W,...r.answers.repo?{repo:r.answers.repo}:{},...P?{owner:P}:{},...R?{githubClientId:R.clientId}:{},...m?{locusgraph:m.secret,graph:m.graph,server:m.server}:{},...p?{claude:p}:{}}),M=T?.workspacePid!==void 0&&At(o,T.workspacePid,T.workspaceStarted,gt(T.workspaceUrl))?T.workspacePid:void 0,N=M?T?.workspaceStarted:void 0,se=T?.workspaceFor,fe=T?.consolePid!==void 0&&At(o,T.consolePid,T.consoleStarted,gt(T.consoleUrl))?T.consolePid:void 0,te=fe?T?.consoleStarted:void 0,me=T?.consoleFor,Fe=T?.memoryKept,ve=T?.workspaceUrl===k?T?.people:void 0,Oe=()=>{i={runner:d,workspaceUrl:k,consoleUrl:O,...M?{workspacePid:M,...N?{workspaceStarted:N}:{}}:{},...se?{workspaceFor:se}:{},...fe?{consolePid:fe,...te?{consoleStarted:te}:{},...me?{consoleFor:me}:{}}:{},...ve?{people:ve}:{},...Fe?{memoryKept:Fe}:{},at:new Date().toISOString()},n(i)};if((M||fe)&&Oe(),M!==void 0&&se!==Ae){if(a({label:"workspace",mark:"wait",detail:"restarting \u2014 it was started with other answers"}),o.stop(M),!await Wl(o,M,15))return c("workspace",`the old workspace (pid ${M}) is still running`,`stop it (\`kill ${M}\`), then Enter`);M=void 0,N=void 0,se=void 0,Oe()}let Ee=await pe.get("/v1/whoami").then(()=>"ours",ne=>ne instanceof re?"other":"down");if(Ee==="other")return c("workspace",`something else answers on :${b} and refuses this token`,"stop it, or run `moa setup --workspace-port <port>`");if(Ee==="ours"&&se!==Ae&&d==="node")return c("workspace",`the workspace on :${b} was not started with these answers`,T?.runner==="compose"?`run \`docker compose -p ${ue} down\`, then Enter`:"stop it, then Enter");if(Ee==="ours"&&se===Ae)a({label:"workspace",mark:"ok",detail:`${k} \u2014 already running`});else{a({label:"workspace",mark:"wait",detail:`starting on :${b}`});let ne={...o.env,...await Dt(e),MOA_TOKENS:`${W}:${w}`,PORT:String(b),MOA_PORT:String(b),MOA_CONSOLE_PORT:String(g),...r.answers.repo?{MOA_REPO:r.answers.repo}:{},...P?{MOA_OWNER_LOGIN:P}:{},...R?{MOA_GITHUB_CLIENT_ID:R.clientId}:{},...m?{LOCUSGRAPH_AGENT_SECRET:m.secret,LOCUSGRAPH_GRAPH_ID:m.graph,LOCUSGRAPH_SERVER_URL:m.server}:{}};for(let le of["MOA_DATABASE_URL","MOA_SQLITE_FILE","MOA_TOKEN_FILE","MOA_WORKSPACE_ROOT","MOA_SELF_URL",...P?[]:["MOA_OWNER_LOGIN"],...R?[]:["MOA_GITHUB_CLIENT_ID"]])delete ne[le];if(d==="compose"){for(let Je of["MOA_CLI_HOME","CLAUDE_CODE_OAUTH_TOKEN","ANTHROPIC_API_KEY"])delete ne[Je];p&&(ne.CLAUDE_CODE_OAUTH_TOKEN=p);let le=await o.exec("docker",xt(l,"up","-d","--build"),{cwd:l,env:ne,timeoutMs:12e5});if(!le.ok)return c("workspace",`docker compose: ${$n(le.stderr||le.stdout)}`,"fix that, then Enter");M=void 0,N=void 0,se=Ae,Fe=!!m,Oe()}else{let le=ie(o,"workspace.db");M=o.start(process.execPath,[I],{cwd:yn(l,"apps","workspace"),env:{...ne,...p?{CLAUDE_CODE_OAUTH_TOKEN:p}:{},MOA_DATABASE_URL:`sqlite:${le}`,MOA_D1_ACCOUNT_ID:"",MOA_D1_DATABASE_ID:"",MOA_D1_API_TOKEN:"",MOA_SQLITE_FILE:le,MOA_WORKSPACE_ROOT:ie(o,"worktrees"),MOA_SELF_URL:k,MOA_PID_FILE:Er(o)},log:ie(o,"workspace.log")}),N=o.processOf(M)?.started,se=Ae,Fe=!!m,Oe()}if(!await Va(o,`${k}/health`,d==="compose"?120:60))return c("workspace",`did not answer on :${b}`,d==="compose"?`look at \`docker compose -p ${ue} logs workspace\`, then Enter`:`look at ${ie(o,"workspace.log")}, then Enter`);if(!await pe.get("/v1/whoami").then(()=>!0,()=>!1))return c("workspace","it started but refuses the owner token","Enter to try again");a({label:"workspace",mark:"ok",detail:`${k}${M?` (pid ${M})`:""}`})}de&&await Nt(o,bn,k,W);let ge=await xr(o,k,a,d==="compose"?"postgres":"sqlite",d==="compose"?`look at \`docker compose -p ${ue} logs\`, then Enter`:`look at ${ie(o,"workspace.log")}, then Enter`);if(ge)return c(ge.label,ge.detail,ge.fix);let E=await Vt(pe,r,a,ve);if(E)return c(E.label,E.detail,E.fix);let V=ve;ve=r.answers.people??[],JSON.stringify(ve)!==JSON.stringify(V??[])&&Oe();let Be=zl(k,W,we),Ke=fe!==void 0&&await o.listening(g);if(Ke&&fe!==void 0&&me!==Be){if(a({label:"console",mark:"wait",detail:"restarting \u2014 it points at another workspace"}),o.stop(fe),fe=void 0,te=void 0,me=void 0,Oe(),!await Jl(o,g,15))return c("console",`the old console still holds :${g}`,"stop it, then Enter");Ke=!1}if(Ke)a({label:"console",mark:"ok",detail:`${O} \u2014 already running`});else{if(await o.listening(g))return c("console",`something else is on :${g}`,"stop it, or run `moa setup --console-port <port>`");if(a({label:"console",mark:"wait",detail:`starting on :${g}`}),fe=o.start(process.execPath,[yn(l,"apps","console","node_modules","next","dist","bin","next"),"start","--port",String(g),"--hostname","127.0.0.1"],{cwd:yn(l,"apps","console"),env:{...Vl(o.env),MOA_URL:k,MOA_TOKEN:W,...we},log:ie(o,"console.log")}),te=o.processOf(fe)?.started,me=Be,Oe(),!await Va(o,O,60))return c("console",`did not answer on :${g}`,`look at ${ie(o,"console.log")}, then Enter`);a({label:"console",mark:"ok",detail:`${O} (pid ${fe})${R?" \u2014 Sign in with GitHub":""}`})}let et=await Yt(pe,f,d==="compose"?"in the container":"on this machine",ne=>d==="compose"?"Esc back to Agent logins and log it in again, then Enter":`is ${ne} logged in on this machine? Run \`${ne}\` once and log in, then Enter`,a);if("label"in et)return c(et.label,et.detail,et.fix);let kt=et.runs[u],ce=await Xt(pe,a);if("label"in ce)return c(ce.label,ce.detail,ce.fix);let Ht=ce.memory;return ce.live!==void 0&&(Fe=ce.live),a({label:"Console",mark:"ok",detail:O}),a({label:"Workspace",mark:"ok",detail:k}),a({label:"Next",mark:"skip",detail:`moa whoami \xB7 moa agent list \xB7 moa run ${vn(u)} "\u2026"`}),{ok:!0,deployed:{runner:d,workspaceUrl:k,consoleUrl:O,...M?{workspacePid:M,...N?{workspaceStarted:N}:{}}:{},...se?{workspaceFor:se}:{},...fe?{consolePid:fe,...te?{consoleStarted:te}:{},...me?{consoleFor:me}:{}}:{},...kt?{proofRunId:kt}:{},memory:Ht,people:ve,...Fe?{memoryKept:Fe}:{},at:new Date().toISOString()}}}async function xr(e,t,n,o,r){let s=await e.fetch(`${t}/health`,{signal:AbortSignal.timeout(5e3)}).then(i=>i.json()).catch(i=>({ok:!1,error:String(i)})),a=s.database??o;if(s.ok!==!0)return{label:"database",detail:`${a} does not answer: ${$n(String(s.error??"no answer"),1)}`,fix:r};n({label:"database",mark:"ok",detail:`${a} \u2014 migrated, and it answers`})}async function Vt(e,t,n,o=[]){let r=t.answers.people??[],s=r.filter(c=>!o.some(l=>Yl(l,c)));for(let c of s){n({label:"people",mark:"wait",detail:`adding ${c.login}`});try{await e.put(`/v1/access/people/${encodeURIComponent(c.login)}`,{role:c.role,dailyCapUsd:c.dailyCapUsd})}catch(l){let d=l instanceof Error?l.message:String(l);return{label:"people",detail:`${c.login}: ${d}`,fix:l instanceof re&&l.status===404?"this workspace has no access list \u2014 `git pull`, then `pnpm run build`, then Enter":"Enter to try again"}}}let a=(t.answers.removed??[]).filter(c=>!r.some(l=>l.login.toLowerCase()===c));for(let c of a){n({label:"people",mark:"wait",detail:`removing ${c}`});try{await e.del(`/v1/access/people/${encodeURIComponent(c)}`)}catch(l){if(l instanceof re&&l.status===404)continue;let d=l instanceof Error?l.message:String(l);return{label:"people",detail:`removing ${c}: ${d}`,fix:"Enter to try again"}}}let i=r.length-s.length;(r.length>0||a.length>0)&&n({label:"people",mark:"ok",detail:[...s.map(c=>`${c.login} (${c.role})`),...a.map(c=>`${c} removed`),...i>0?[`${i} as before \u2014 left as the Access page has them`]:[]].join(", ")})}async function Yt(e,t,n,o,r){let s={};for(let a of t){let i=`proof: ${a} run`,c=vn(a);r({label:i,mark:"wait",detail:`${a} is answering ${n}`});try{if(!await e.post("/v1/agents",{name:c,cli:a,role:"Proves moa setup worked. Answers in one word."}).then(()=>!0,p=>{if(!(p instanceof re&&p.status===409))throw p;return!1})){let p=await e.get(`/v1/agents/${c}`);if(p.cli!==a)return{label:i,detail:`agent ${c} already exists and runs ${p.cli??"another CLI"}`,fix:`run \`moa agent rm ${c}\`, then Enter`}}let d=await e.post(`/v1/agents/${c}/runs`,{prompt:mn}),u=await Rt(e,d.id,300),f=u.result;if(u.state!=="done"||!f||!("runId"in f)||f.isError){let p=u.error??(f&&"result"in f?f.result:"no answer");return{label:i,detail:$n(String(p),2),fix:o(a)}}s[a]=f.runId,r({label:i,mark:"ok",detail:`${a} answered "${$n(f.result,1).slice(0,40)}" ${n} \u2014 moa logs ${f.runId}`})}catch(l){return{label:i,detail:l instanceof Error?l.message:String(l),fix:"Enter to try again"}}}return{runs:s}}async function Xt(e,t){try{let n=await e.get("/v1/memory"),o=(n.types??[]).reduce((i,c)=>i+(c.items?.length??0),0),r=typeof n.live=="boolean"?n.live:void 0,a=`${o} memor${o===1?"y":"ies"}, ${r===void 0?"kept where this older workspace build cannot say":r?"kept in LocusGraph":"in the workspace process"}`;return t({label:"proof: memory read",mark:"ok",detail:a}),r===void 0?{memory:a}:{memory:a,live:r}}catch(n){return{label:"proof: memory read",detail:n instanceof Error?n.message:String(n),fix:"Enter to try again"}}}var bn,vn,$n,Ya,ql,zl,Vl,Yl,Qt=Se(()=>{"use strict";Ie();cn();qe();ht();St();Wt();hn();Ze();Vn();bn="local",vn=e=>`setup-proof-${e}`,$n=(e,t=3)=>e.trim().split(`
|
|
139
|
+
`).slice(-t).join(" \xB7 ").slice(0,300);Ya=e=>Kl("sha256").update(e).digest("hex").slice(0,12),ql=e=>Ya(JSON.stringify([e.runner,e.workspaceUrl,e.tenant,e.repo??"",e.token,e.locusgraph??"",e.owner??"",e.githubClientId??"",e.graph??"",e.server??"",e.claude??""])),zl=(e,t,n)=>`${e} ${Ya(JSON.stringify([t,n.AUTH_GITHUB_ID??"",n.AUTH_GITHUB_SECRET??"",n.AUTH_SECRET??""]))}`,Vl=e=>Object.fromEntries(Object.entries(e).filter(([t])=>!t.startsWith("AUTH_")));Yl=(e,t)=>e.login.toLowerCase()===t.login.toLowerCase()&&e.role===t.role&&e.dailyCapUsd===t.dailyCapUsd});var _r={};Bi(_r,{artifactRoot:()=>xn,bundledWorker:()=>Cr,deployImage:()=>Ar,imageChecks:()=>An,imageCompose:()=>Yn,imageComposePath:()=>xo,imageConfig:()=>ei,releaseImage:()=>be,workerFromImage:()=>Pr});import{chmod as Xl,mkdir as Xa,readFile as Ql,writeFile as Zl}from"node:fs/promises";import{dirname as Qa,join as Za}from"node:path";import{fileURLToPath as ed}from"node:url";async function Pr(e){let t=await e.exec("docker",["create","--platform","linux/amd64",be()]);if(!t.ok)return{ok:!1,why:ye(t.stderr)||"docker create failed"};let n=ye(t.stdout),o=ie(e,Za("cloudflare","worker.js"));await Xa(Qa(o),{recursive:!0});let r=await e.exec("docker",["cp",`${n}:/repo/apps/workspace/dist/worker.js`,o]);return await e.exec("docker",["rm",n]),r.ok?{ok:!0,path:o}:{ok:!1,why:ye(r.stderr)||"the image holds no worker.js"}}async function An(e){let t=await e.sys.exec("docker",["info","--format","{{.ServerVersion}}"]);if(!t.ok)return[{label:"docker",mark:"fail",detail:`${t.missing?"not installed":"not running"} \u2014 it pulls the published image`,fix:"Start Docker, then check again."}];let n=await e.sys.exec("docker",["pull",...e.state.target==="local"?[]:["--platform","linux/amd64"],be()],{timeoutMs:6e5}),o=e.state.target==="cloudflare"?"pulls the published image, then pushes it to your Cloudflare registry":"pulls the published image; nothing is built here";return[{label:"docker",mark:"ok",detail:`${t.stdout.trim().split(`
|
|
140
|
+
`)[0]} \u2014 ${o}`},{label:"image",mark:n.ok?"ok":"fail",detail:be(),...n.ok?{}:{fix:"Sign in to ghcr.io with your account's package pull token, then check again."}}]}function ei(e){let t=`fnmoa-${e.install}`;return{services:{workspace:{image:e.image,restart:"unless-stopped",ports:[`127.0.0.1:${e.workspacePort}:8787`,`127.0.0.1:${e.consolePort}:3100`],volumes:[`${t}:/data`,`${t}-home:/home/moa`],environment:{...e.env,MOA_PORT:"8787",MOA_SELF_URL:"http://127.0.0.1:8787",MOA_WORKSPACE_ROOT:"/data/worktrees",MOA_LICENCE_FILE:"/data/licence.json",MOA_DATABASE_URL:e.postgres?"postgres://moa@postgres:5432/moa":"sqlite:/data/workspace.db",MOA_SQLITE_FILE:"/data/workspace.db",MOA_LOCAL_CONTAINER:"1"},...e.postgres?{depends_on:{postgres:{condition:"service_healthy"}}}:{}},...e.postgres?{postgres:{image:"postgres:17-alpine",restart:"unless-stopped",environment:{POSTGRES_USER:"moa",POSTGRES_DB:"moa",POSTGRES_HOST_AUTH_METHOD:"trust"},volumes:[`${t}-pg:/var/lib/postgresql/data`],healthcheck:{test:["CMD-SHELL","pg_isready -U moa"],interval:"5s",timeout:"5s",retries:20}}}:{}},volumes:{[t]:{name:t},[`${t}-home`]:{name:`${t}-home`},...e.postgres?{[`${t}-pg`]:{name:`${t}-pg`}}:{}}}}async function Ar(e,t,n){let o=[],r=_=>{o.push(_),t([...o])},s=_=>(r({label:"image",mark:"fail",detail:_}),{ok:!1}),a=await ut(e);for(let _ of a)r(_);if(a.some(_=>_.mark==="fail"))return{ok:!1};let i=await Wn(e.sys.env,"local");if(!i)return s("Activate the local install first.");for(let _ of await An(e))if(r(_),_.mark==="fail")return{ok:!1};let{sys:c,state:l}=e,d={};if(l.deployed?.runner==="image"){let _=JSON.parse(await Ql(xo(c),"utf8"));d=Object.fromEntries(Object.entries(_.services.workspace.environment).map(([Te,pe])=>[Te,pe.replaceAll("$$","$")]))}let u=`http://127.0.0.1:${jt(l)}`,f=`http://localhost:${Et(l)}`,{token:p}=await Mt(c,"local",u),m=l.answers.githubApp,w=m?Me(c,m):void 0;if(m&&!w)return s("The GitHub App client secret is missing.");let b=await c.exec("gh",["auth","token"]),g={...d,...await Dt(e),MOA_TOKENS:`${p}:${l.answers.tenant??xe(l.answers.owner)}`,...l.answers.owner?{MOA_OWNER_LOGIN:l.answers.owner}:{},...l.answers.repo?{MOA_REPO:l.answers.repo}:{},...b.ok?{GH_TOKEN:b.stdout.trim()}:{}},k=ze(e)?.token;k&&(g.CLAUDE_CODE_OAUTH_TOKEN=k);for(let _ of["ANTHROPIC_API_KEY","OPENAI_API_KEY"])c.env[_]&&(g[_]=c.env[_]);let O=ke(e);O&&Object.assign(g,{LOCUSGRAPH_AGENT_SECRET:O.secret,LOCUSGRAPH_GRAPH_ID:O.graph,LOCUSGRAPH_SERVER_URL:O.server}),m&&w&&Object.assign(g,{MOA_GITHUB_CLIENT_ID:m.clientId,AUTH_GITHUB_ID:m.clientId,AUTH_GITHUB_SECRET:w.clientSecret,AUTH_SECRET:w.authSecret,AUTH_URL:f});let P=xo(c),R=_=>typeof _=="string"?_.replaceAll("$","$$"):Array.isArray(_)?_.map(R):_&&typeof _=="object"?Object.fromEntries(Object.entries(_).map(([Te,pe])=>[Te,R(pe)])):_,Y=ei({image:be(),install:i.install,workspacePort:jt(l),consolePort:Et(l),postgres:l.answers.runner==="compose",env:g});if(await Xa(Qa(P),{recursive:!0,mode:448}),await Zl(P,JSON.stringify(R(Y)),{mode:384}),await Xl(P,384),!(await c.exec("docker",Yn(c,"up","-d","--wait"),{timeoutMs:6e5})).ok)return s("Docker Compose did not start the image. Run `moa setup logs` for details.");let I={runner:"image",workspaceUrl:u,consoleUrl:f,image:be(),at:new Date().toISOString()};n(I);let X=D({url:u,token:p},c.fetch),U=!1;for(let _=0;_<60;_++)try{await X.get("/v1/whoami"),U=!0;break}catch{await c.sleep(1e3)}if(!U)return{...s("The workspace did not become ready."),deployed:I};await Nt(c,"local",u,p);let H=await Vt(X,l,r,l.deployed?.people);if(H)return{...s(H.detail),deployed:I};let W=await Yt(X,[l.answers.agentCli??"claude"],"in the image",()=>"Set your own ANTHROPIC_API_KEY or OPENAI_API_KEY and run setup again.",r);if("label"in W)return{...s(W.detail),deployed:I};let de=await Xt(X,r);return"label"in de?{...s(de.detail),deployed:I}:(r({label:"console",mark:"ok",detail:f}),{ok:!0,deployed:{...I,people:l.answers.people,memory:de.memory,memoryKept:de.live}})}var be,xn,Cr,xo,Yn,ht=Se(()=>{"use strict";Ie();qe();Qt();St();hn();be=()=>`ghcr.io/fnlog0/fnmoa:${Ut()}`,xn=()=>ed(new URL(import.meta.url.endsWith("/main.js")?"../":"../../",import.meta.url)),Cr=()=>Za(xn(),"dist/assets/worker.js"),xo=e=>ie(e,"image/compose.json"),Yn=(e,...t)=>["compose","--project-name","fnmoa-image","--file",xo(e),...t]});import{join as Or}from"node:path";async function en(e,t,n,o=["--version"]){let r=await e.exec(t,o);return r.ok?v(t,Xn(r.stdout)):C(t,"not found",n)}function od(e=process){return e.platform==="darwin"&&e.env.MOA_KEEP_AWAKE!=="0"?v("Sleep","held off while a pipeline runs, as `caffeinate -i` does \u2014 a closed lid still sleeps, and work it cut short is run again"):v("Sleep",e.platform==="darwin"?"not held off, MOA_KEEP_AWAKE=0 \u2014 work a sleep cuts short is run again":"not held off on this system \u2014 work a sleep cuts short is run again")}function rd(e,t=process){switch(e.target){case"local":return{lines:[v("This machine","Docker or local, nothing in the cloud"),od(t)]};case"cloudflare":return{lines:[v("Cloudflare","a Worker and its container on D1, on your own domain \u2014 Workers Paid plan")]};case"vercel":return{lines:[v("Vercel Sandbox","one persistent VM, fnmoa-<tenant>, resumed by name \u2014 needs the vercel CLI, signed in")]};default:return{lines:[{label:"Pick one with \u2191 \u2193 and Enter",mark:"wait"}]}}}async function ti(e,t,n,o,r,s){return await e.sys.listening(n)?o!==void 0&&At(e.sys,o,r,n)?v(`port ${n}`,`the ${t} the last setup started`):C(`port ${n}`,"something else is listening there",`stop it, or run \`moa setup ${s} <port>\``):v(`port ${n}`,`free for the ${t}`)}async function ni(e){return[Number(process.versions.node.split(".")[0])>=22?v("node",process.versions.node):C("node",process.versions.node,"install node 22 or newer"),...await Promise.all([en(e,"pnpm","install pnpm: `corepack enable`"),en(e,"git","install git"),en(e,"gh","install the GitHub CLI: https://cli.github.com")])]}function Ir(e){if(e.secrets.cloudflare)return{token:e.secrets.cloudflare,from:"pasted in this run"};let t=e.sys.env.MOA_D1_API_TOKEN?.trim();return t?{token:t,from:"MOA_D1_API_TOKEN"}:void 0}function Lr(e){if(e.secrets.github)return{token:e.secrets.github,from:"pasted in this run"};let t=e.sys.env.GH_TOKEN?.trim();return t?{token:t,from:"GH_TOKEN"}:void 0}async function sd(e){let{sys:t,state:n}=e,o=await ni(t);if(De(e)){let{imageChecks:u}=await Promise.resolve().then(()=>(ht(),_r));o.push(...await u(e))}else{let u=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);o.push(u.ok?v("docker",`${ye(u.stdout)} \u2014 builds the container image from this checkout`):C("docker",u.missing?"not installed":"not running","wrangler builds the container image with Docker: start it, then r"))}let s=t.root??xn();o.push(v(t.root?"checkout":"release",s));let a=await Ue(t,s,["--version"],{timeoutMs:3e4});if(!a.ok)return o.push(C("wrangler","not installed in this checkout",`run \`pnpm install\` in ${s}, then r`)),{lines:o};o.push(v("wrangler",Xn(a.stdout)));let i=await Ks(t,s),c;i.ok?(c=i.account.id,o.push(v("account",`${i.account.name} (${i.account.id})${i.email?` as ${i.email}`:""}${i.others>0?" \u2014 CLOUDFLARE_ACCOUNT_ID picks another":""}`))):o.push(C("account",i.why,i.loggedOut?`run \`pnpm --dir ${Or(s,"apps","workspace")} exec wrangler login\`, then r`:"fix that, then r"));let l=n.answers.domain;if(!l)o.push(C("domain","none yet \u2014 the console and workspace need a name of yours","press d and type a domain on this Cloudflare account, like example.com"));else{let u=await Js(t,l),f=Dn(l);o.push(u.zone&&u.onCloudflare?v("domain",`${f.consoleUrl} and ${f.workspaceUrl} \u2014 zone ${u.zone}`):C("domain",u.why??`${u.zone} is not served by Cloudflare's name servers`,"add the domain to this Cloudflare account (its name servers must be Cloudflare's), or d for another"))}let d=Ir(e);if(!d)o.push(Rr(n)?J("D1 token","kept \u2014 the deployed Worker has it (k to replace it)"):C("D1 token","none \u2014 the container reaches D1 over Cloudflare's API with it","make an API token with Account \xB7 D1 \xB7 Edit on this account, then press k and paste it"));else if(c){let u=await Ws(t,c,d.token);o.push(u.ok?v("D1 token",`${K} (${d.from}) \u2014 D1 answers to it`):C("D1 token",`${K} (${d.from}) \u2014 D1 refused it: ${u.why}`,"make one with only Account \xB7 D1 \xB7 Edit on this account, then press k to paste it"))}return o.push(J("cost",`Workers Paid, plus the container (standard-4) while it runs \u2014 up to ${$t}h after the last request`)),{lines:o,...c?{answers:{cloudflareAccount:c}}:{}}}async function ad(e){let{sys:t,state:n}=e;if(De(e)&&n.target==="local"){let{imageChecks:a}=await Promise.resolve().then(()=>(ht(),_r));return{lines:await a(e)}}if(n.target==="cloudflare")return sd(e);let o=await ni(t),r=n.deployed?Sn(t,n.deployed):void 0,s=await t.exec("docker",["--version"]);return o.push(s.ok?v("docker",Xn(s.stdout)):J("docker","not found \u2014 only needed for Postgres with docker compose")),o.push(t.root?v("checkout",t.root):C("checkout","not inside a fnMoa checkout","run moa setup from a clone of https://github.com/fnLog0/fnmoa")),o.push(await ti(e,"workspace",jt(n),r?.workspacePid,r?.workspaceStarted,"--workspace-port"),await ti(e,"console",Et(n),n.deployed?.consolePid,n.deployed?.consoleStarted,"--console-port"),J("account","this machine \u2014 no account needed")),{lines:o}}async function id(e){let{sys:t,state:n}=e,o=n.target==="cloudflare"?{...t.env}:void 0;if(o)for(let c of["GH_TOKEN","GITHUB_TOKEN"])delete o[c];let r=await t.exec("gh",["api","user","--jq",".login"],o?{env:o}:{}),s=ye(r.stdout);if(!r.ok||!s)return{lines:[C("gh login",r.missing?"gh is not installed":"gh has no login on this machine","run `gh auth login`, then press r to check again")]};if(n.target==="cloudflare")return ld(e,s);let a=[v("gh login",`${s} \u2014 on this machine the workspace uses your own login`)],i=n.answers.repo;if(!i)a.push(J("repo","none \u2014 a pipeline can name any repository later (e to set one)"));else{let c=await t.exec("gh",["repo","view",i,"--json","viewerPermission","--jq",".viewerPermission"]),l=ye(c.stdout);a.push(c.ok&&["ADMIN","MAINTAIN","WRITE"].includes(l)?v("repo",`${i} \u2014 ${l.toLowerCase()}`):C("repo",c.ok?`${s} cannot push to ${i}`:`${i} is not a repository ${s} can see`,"press e to change it, or clear it to skip"))}return{lines:a,answers:{owner:s}}}async function cd(e,t,n,o){let r=await e.exec("gh",["repo","view",t,"--json","viewerPermission","--jq",".viewerPermission"],o?{env:o}:{}),s=ye(r.stdout);return r.ok&&["ADMIN","MAINTAIN","WRITE"].includes(s)?v("repo",`${t} \u2014 ${s.toLowerCase()} for ${n}`):C("repo",r.ok?`${n} cannot push to ${t}`:`${t} is not a repository ${n} can see`,"press e to change it, or clear it to skip")}async function ld(e,t){let{sys:n,state:o}=e,r=[v("gh login",`${t} \u2014 you, the first owner`)],s=Lr(e),a;if(!s)r.push(Rr(o)?J("token","kept \u2014 the deployed Worker has it (k to replace it)"):C("token","none \u2014 the container works on GitHub with a token of its own","make a fine-grained token (contents, pull requests, issues: read/write; checks, actions: read) \u2014 a bot account's, preferably \u2014 then press k"));else{let i={...n.env,GH_TOKEN:s.token};for(let d of["GITHUB_TOKEN"])delete i[d];let c=await n.exec("gh",["api","user","--jq",".login"],{env:i});a=ye(c.stdout)||void 0,r.push(c.ok&&a?v("token",`${K} (${s.from}) \u2014 GitHub sees ${a}`):C("token",`${K} (${s.from}) \u2014 GitHub refused it`,"press k to paste another"));let l=o.answers.repo;l&&a&&r.push(await cd(n,l,a,i))}return o.answers.repo||r.push(J("repo","none \u2014 a pipeline can name any repository later (e to set one)")),{lines:r,answers:{owner:t}}}function ze(e){if(e.secrets.claude)return{token:e.secrets.claude,from:"pasted in this run"};let t=e.sys.env.CLAUDE_CODE_OAUTH_TOKEN?.trim();return t?{token:t,from:"your environment"}:void 0}async function ud(e){let{sys:t,state:n}=e;if(De(e)&&n.target==="local"){let m=n.answers.agentCli??"claude",w=m==="claude"?"ANTHROPIC_API_KEY":"OPENAI_API_KEY";return{lines:[!!(t.env[w]||m==="claude"&&ze(e)||n.deployed?.runner==="image")?v(m,"Credential will be verified by the proof run inside the image"):C(m,`${w} is not set`,`Set your own ${w} and run setup again`)]}}if(n.target==="cloudflare")return fd(e);let o=dd(n),[r,s]=await Promise.all([t.exec("claude",["--version"]),t.exec("codex",["--version"])]),a=ze(e),i={claude:r.ok,codex:s.ok},c=[o?v("agents run","in the container \u2014 only what is carried in here reaches it"):J("agents run","on this machine, on your logins \u2014 Postgres on Memory & database would move them into a container")],l,d=o&&t.root?await _a(t,t.root):void 0;if(d&&!d.ok)i.codex=!1,l=C("codex",`the image did not build: ${d.why}`,"start Docker, or fix the build, then press r");else if(o&&t.root){let m=await Oa(t,t.root);i.codex=m.loggedIn,l=m.loggedIn?v("codex",`${m.says} \u2014 its own session in the container (l to log in again)`):m.broken?C("codex",`the container did not run: ${m.broken}`,"start Docker, then press r"):J("codex","not logged in inside the container \u2014 l to log in with a code")}else{let m=s.ok?await t.exec("codex",["login","status"]):void 0;i.codex=s.ok&&m?.ok===!0,l=s.ok?m?.ok?v("codex",`${Xn(s.stdout)} \u2014 ${ye(m.stdout)||"logged in"}`):J("codex","not logged in \u2014 run `codex login`, then press r"):J("codex","not found")}let u;if(a){let m=await hr(t,a.token,o&&t.root?{container:t.root}:"here");i.claude=m.ok,u=m.ok?v("claude",`token ${K} (${a.from}) \u2014 "reply ok" ${o?"in the container":"on it alone"}`):C("claude",`the token did not answer: ${m.why}`,"press t to make a new one with `claude setup-token`, or e to paste it again")}else o?(i.claude=!1,u=J("claude",r.ok?"no token carried in \u2014 t to make one with `claude setup-token`":"no token \u2014 install claude here to make one: npm i -g @anthropic-ai/claude-code")):u=r.ok?v("claude",`${Xn(r.stdout)} \u2014 your existing login (t to carry a token instead)`):J("claude","not found");c.push(u,l);let f=e.state.answers.agentCli??(i.claude?"claude":i.codex?"codex":void 0);f?c.push(i[f]?v("proof run",`uses ${f} (\u2191 \u2193 to change)`):C("proof run",`${f} is not ready ${o?"in the container":"here"}`,f==="claude"?o?"press t to carry a token in \u2014 or \u2191 \u2193 to choose codex":"install claude and log in \u2014 or \u2191 \u2193 to choose codex":o?"press l to log codex in \u2014 or \u2191 \u2193 to choose claude":"install codex and run `codex login` \u2014 or \u2191 \u2193 to choose claude")):c.push(C("agent",o?"neither claude nor codex is logged in inside the container":"neither claude nor codex is ready",o?"press t to carry a claude token in, or l to log codex in":"install one and log in, then press r")),c.push(J("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)"));let p=["claude","codex"].filter(m=>i[m]);return{lines:c,answers:{...f?{agentCli:f}:{},loggedIn:p,...Qn(c)?{loginsFor:o?"compose":"node"}:{}}}}async function pd(e){let t=e.state.deployed;if(t?.runner!=="cloudflare")return;let n=await Pe(e.sys.env),o=Object.values(n.profiles).find(r=>r.url===t.workspaceUrl);if(o)try{let r=await e.sys.fetch(`${t.workspaceUrl}/_moa/codex`,{headers:{authorization:`Bearer ${o.token}`},signal:AbortSignal.timeout(12e4)});return r.ok?(await r.json()).loggedIn===!0:void 0}catch{return}}async function fd(e){let{sys:t,state:n}=e,o=ze(e),r=[v("agents run","in the Cloudflare container \u2014 only what is carried in reaches it")],s={claude:!1,codex:!0};if(o){let l=await hr(t,o.token,"here");s.claude=l.ok,r.push(l.ok?v("claude",`token ${K} (${o.from}) \u2014 "reply ok" on it alone; a Worker secret`):C("claude",`the token did not answer: ${l.why}`,"press t to make a new one with `claude setup-token`, or e to paste it again"))}else Rr(n)&&n.answers.loggedIn?.includes("claude")?(s.claude=!0,r.push(J("claude","kept \u2014 the deployed Worker has the token (t to replace it)"))):r.push(J("claude","no token carried in \u2014 t to make one with `claude setup-token`"));let a=await pd(e);r.push(a===!0?v("codex","logged in inside the container \u2014 its login is kept in R2 across restarts"):J("codex",a===!1?"not logged in inside the container \u2014 Deploy shows a link and a code":"logged in inside the container at Deploy \u2014 a link and a code, approved in your browser"));let i=n.answers.agentCli??(s.claude?"claude":"codex");r.push(s[i]?v("proof run",`uses ${i} (\u2191 \u2193 to change)`):C("proof run",`${i} is not ready`,"press t to carry a claude token in \u2014 or \u2191 \u2193 to choose codex"),J("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)"));let c=["claude","codex"].filter(l=>s[l]&&(l==="claude"||a===!0));return{lines:r,answers:{agentCli:i,loggedIn:c,...Qn(r)?{loginsFor:"cloudflare"}:{}}}}function ke(e){let{sys:t,state:n,secrets:o}=e,r=md(e),s=t.env.LOCUSGRAPH_AGENT_SECRET,a=s?.trim(),i=o.locusgraph||(s!==void 0?a:r.LOCUSGRAPH_AGENT_SECRET?.trim());if(!i)return;let c=o.locusgraph?"pasted in this run":a?"your environment":"apps/workspace/.env",l=n.answers.tenant??xe(n.answers.owner);return{secret:i,from:c,graph:t.env.LOCUSGRAPH_GRAPH_ID?.trim()||r.LOCUSGRAPH_GRAPH_ID||`fnmoa-${l}`,server:t.env.LOCUSGRAPH_SERVER_URL?.trim()||r.LOCUSGRAPH_SERVER_URL||Ia}}async function gd(e){let{sys:t,state:n}=e,o=ke(e),r=[];if(!o)r.push(Zt(e)?C("LocusGraph","the last deploy kept memory in LocusGraph, and this run has no secret for it","press e to paste it again \u2014 or e, then Enter on nothing, to let memory stay in the workspace process"):e.secrets.noLocusgraph&&n.deployed?.memoryKept?J("LocusGraph","no secret \u2014 memory will not be kept in LocusGraph; it stays in the workspace process"):J("LocusGraph","no secret \u2014 memory stays in the workspace process (e to paste one)"));else{let i=n.answers.tenant??xe(n.answers.owner),c=await gn(t,o,i),l=`secret ${K} (${o.from}), graph ${o.graph}`;r.push(c.read.ok&&c.write.ok?v("LocusGraph",`${l} \u2014 one graph read, one event written`):C("LocusGraph",`${l} \u2014 ${c.read.ok?`the write failed: ${c.write.why}`:`the read failed: ${c.read.why}`}`,"e to paste the secret again, or check LOCUSGRAPH_GRAPH_ID \u2014 then r"))}if(n.target==="cloudflare")return r.push(v("database",`D1 \u2014 ${ct} on your account, made at deploy; the container reaches it over HTTP`)),{lines:r,answers:{runner:"cloudflare"}};let s=n.answers.runner??"node";if(s==="node")return r.push(v("database",`SQLite, ${ie(t,"workspace.db")} \u2014 migrated at deploy (\u2191 \u2193 for Postgres)`)),{lines:r,answers:{runner:s}};let a=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);return r.push(a.ok?v("database","Postgres, with docker compose \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):C("database",a.missing?"docker is not installed":"docker is not running","start Docker, or \u2191 \u2193 to choose SQLite")),r.push(J("agent logins","in the container \u2014 Agent logins, next, carries them in and proves them")),{lines:r,answers:{runner:s}}}function Me(e,t){try{let n=JSON.parse(e.read(Eo(e,t))??"");if(n.clientSecret&&n.authSecret)return{clientSecret:n.clientSecret,authSecret:n.authSecret}}catch{}}function Ur(e){let{state:t,sys:n}=e,o=t.answers.owner,r=t.answers.tenant??xe(o),s=t.answers.githubApp,a=[];s?t.target!=="vercel"&&(s.consoleUrl??Tr(t))!==Ao(t)?a.push(C("GitHub App",`${s.slug} signs people in at ${s.consoleUrl??Tr(t)}, not ${Ao(t)}`,"press g to create one for this console")):a.push(Me(n,s)?v("GitHub App",`${s.slug} (id ${s.id}) \u2014 client secret ${K} kept beside setup`):C("GitHub App",`the client secret for ${s.slug} is not on this machine`,"press g to create a new app \u2014 GitHub shows a secret only once"),s.deviceFlow?v("device flow","on \u2014 teammates run `moa login <url> --github`"):{label:"device flow",mark:"wait",detail:`GitHub cannot switch it on for us: tick "Enable Device Flow" on ${s.settingsUrl}, Save, then press d`}):a.push(t.target==="local"?J("GitHub App","none \u2014 the console stays on localhost, never exposed (g to create one anyway)"):C("GitHub App","none yet \u2014 teammates sign in with it","press g: GitHub opens with the app filled in, and you click Create")),a.push(o?v("owner",`${o} (you)`):C("owner","no GitHub login yet","Esc back to GitHub"),v("tenant",`${r} (e to change)`),v("owner token",`${K} \u2014 made at deploy, kept in ~/.moa/config.json`));let i=t.answers.people??[];i.length===0&&a.push(J("people","only you \u2014 a to add a teammate"));for(let c of i)a.push(v(c.login,`${hd(c.role)} \xB7 cap $${c.dailyCapUsd}/day \u2014 added at deploy`));return{lines:a,answers:{tenant:r}}}function Sn(e,t){if(t.runner!=="node")return t;let n=t.workspacePid;if(n!==void 0&&At(e,n,t.workspaceStarted,gt(t.workspaceUrl)))return t;let[o="",r=""]=(e.read(Er(e))??"").split(`
|
|
141
|
+
`),s=Number(o.trim()),a=r.trim();if(!Number.isSafeInteger(s)||s<=1||a===""||e.processOf(s)?.started!==a)return t;let i=gt(t.workspaceUrl);if(i===void 0)return t;let c=e.portsOf(s);return c!==void 0&&!c.includes(i)?t:{...t,workspacePid:s,workspaceStarted:a}}var De,td,nd,jt,Et,Tr,Ao,xe,Qn,v,C,J,ye,Xn,Rr,dd,md,Zt,Eo,hd,ie,Er,Co,qe=Se(()=>{"use strict";Ye();jn();ht();Wt();Ze();Vn();De=({sys:e,state:t})=>!e.root||fn(t)==="image",td=8787,nd=3100,jt=e=>e.answers.workspacePort??td,Et=e=>e.answers.consolePort??nd,Tr=e=>`http://localhost:${Et(e)}`,Ao=e=>e.target==="vercel"&&e.answers.sandbox?e.answers.sandbox.consoleUrl:e.target==="cloudflare"&&e.answers.domain?Dn(e.answers.domain).consoleUrl:Tr(e),xe=e=>(e??"").toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/^-+|-+$/g,"")||"local",Qn=e=>e.length>0&&e.every(t=>t.mark==="ok"||t.mark==="skip"),v=(e,t)=>({label:e,mark:"ok",...t?{detail:t}:{}}),C=(e,t,n)=>({label:e,mark:"fail",detail:t,fix:n}),J=(e,t)=>({label:e,mark:"skip",detail:t}),ye=e=>e.trim().split(`
|
|
142
|
+
`)[0]?.trim()??"",Xn=e=>ye(e).match(/\d+(\.\d+)+/)?.[0]??ye(e);Rr=e=>e.deployed?.runner==="cloudflare";dd=e=>e.answers.runner==="compose";md=e=>e.sys.root?La(e.sys.read(Or(e.sys.root,"apps","workspace",".env"))):{};Zt=e=>e.state.deployed?.memoryKept===!0&&!e.secrets.noLocusgraph&&ke(e)===void 0;Eo=(e,t)=>ie(e,uo(t));hd=e=>`${e[0]?.toUpperCase()??""}${e.slice(1)}`;ie=(e,t)=>Or(ot(e.env),"..","local",t),Er=e=>ie(e,"workspace.pid");Co={where:e=>rd(e.state,{platform:process.platform,env:e.sys.env}),checks:ad,github:id,logins:ud,memory:gd,access:Ur}});import{readFileSync as Gu,realpathSync as Hu}from"node:fs";import{fileURLToPath as Fu}from"node:url";function es(e){let t=[],n=new Map;for(let o=0;o<e.length;o++){let r=e[o];if(!r.startsWith("-")){t.push(r);continue}if(r==="--"){t.push(...e.slice(o+1));break}let s=r.indexOf("=");if(s!==-1){n.set(r.slice(0,s).replace(/^--?/,""),r.slice(s+1));continue}let a=r.replace(/^--?/,""),i=e[o+1];i!==void 0&&!i.startsWith("-")?(n.set(a,i),o+=1):n.set(a,!0)}return{positional:t,flags:n}}var x=(e,t)=>{let n=e.flags.get(t);return typeof n=="string"?n:void 0},S=(e,t)=>e.flags.has(t);function ae(e,t){let n=x(e,t);if(n===void 0)return;let o=Number(n);if(!Number.isFinite(o))throw new $(`--${t} must be a number`);return o}var $=class extends Error{constructor(t){super(t),this.name="Misuse"}};Ie();Ye();Ie();cn();var y={ok:0,failed:1,halted:2,misuse:3};function G(e,t=process.stdout,n=process.stderr){return{line(o){e||t.write(`${o}
|
|
143
|
+
`)},data(o){e&&t.write(`${JSON.stringify(o,null,2)}
|
|
144
|
+
`)},warn(o){n.write(`${o}
|
|
145
|
+
`)}}}function ss(e){if(e instanceof $)return{message:e.message,code:y.misuse};if(e instanceof Ln)return{message:e.message,code:y.halted};if(e instanceof re){let t=e.status>=500&&e.requestId?` (request ${e.requestId})`:"";if(e.status===503)return{message:`the workspace is up and cannot work \u2014 ${e.message}. Look at the database it was started with, not the process.${t}`,code:y.failed};if(e.status===429)return{message:e.message,code:y.halted};let n=e.status===401||e.status===403||e.status===400?y.misuse:y.failed;return{message:`${e.message}${t}`,code:n}}return{message:e instanceof Error?e.message:String(e),code:y.failed}}function he(e){if(e.length===0)return"";let t=[];for(let n of e)n.forEach((o,r)=>{t[r]=Math.max(t[r]??0,o.length)});return e.map(n=>n.map((o,r)=>r===n.length-1?o:o.padEnd(t[r]??0)).join(" ").trimEnd()).join(`
|
|
146
|
+
`)}var We=(e,t)=>`${e.padEnd(10)}${t}`,z=(e,t=60)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,zo=e=>{let t=e.split(/\n\s*\n/).filter(n=>n.trim().length>0);return(t[t.length-1]??e).trim()};import{hostname as Yi}from"node:os";Ie();Ye();var Xi="usage: moa login <url> --token <token> or moa login <url> --github";async function cs(e,t=process.env,n={}){let o=S(e,"json"),r=G(o),s=e.flags.get("github"),a=e.positional[1]??(typeof s=="string"?s:void 0);if(!a)throw new $(Xi);if(s!==void 0&&e.flags.has("token"))throw new $("--token and --github are two ways in \u2014 use one");let i=a.replace(/\/+$/,""),c=n.fetch??fetch;if(s!==void 0){let w=await Qi(i,k=>o?r.warn(k):r.line(k),{...n,fetch:c}),b=await as(t,x(e,"profile"),{url:i,token:w.token}),g=w.role==="viewer"?", read-only: you may look and nothing else":"";return r.line(`logged in to ${i} as ${w.login}, ${w.role} on ${w.tenant} (profile ${b}${g})`),r.data({url:i,tenant:w.tenant,profile:b,login:w.login,role:w.role}),y.ok}let l=x(e,"token")??t.MOA_TOKEN;if(!l)throw new $("--token is required (or set MOA_TOKEN), or sign in with --github");let d={url:i,token:l},u=await D(d,c).get("/v1/whoami"),f=await as(t,x(e,"profile"),d),p=u.scope==="read"?", read-only: it may look and nothing else":"";return r.line(`logged in to ${d.url} as ${u.tenant} (profile ${f}${p})`),r.data({url:d.url,tenant:u.tenant,profile:f,...u.scope===void 0?{}:{scope:u.scope}}),0}async function as(e,t,n){let o=t??"default",r=await Pe(e);return r.profiles[o]=n,r.current=o,await it(r,e),o}var is=async e=>{let t=await e.text().catch(()=>"");try{return JSON.parse(t).error??t}catch{return t}};async function Qi(e,t,n){let o=n.sleep??(d=>new Promise(u=>setTimeout(u,d))),r=async(d,u)=>{try{return await n.fetch(`${e}${d}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u??{})})}catch(f){throw new bt(e,f)}},s=await r("/v1/access/device");if(!s.ok){let d=await is(s);throw new Error(s.status===503||s.status===404?`${e} has no GitHub sign-in${d?` (${d})`:""} \u2014 ask its owner to create the GitHub App in \`moa setup\`, or log in with --token`:`${e} would not start a GitHub sign-in: ${d||s.status}`)}let a=await s.json();t(`open ${a.verificationUri} and enter ${a.userCode}`),t("waiting for GitHub\u2026");let i=`${(n.hostname??Yi)()} cli`,c=Math.max(a.interval??5,1),l=(a.expiresIn??900)+30;for(;;){await o(c*1e3),l-=c;let d=await r("/v1/access/device/token",{deviceCode:a.deviceCode,name:i});if(d.status===201)return await d.json();if(d.status===202){let f=await d.json().catch(()=>({}));if(c=Math.max(c,f.interval??c),l<=0)throw new Error("the code expired before it was entered \u2014 run moa login again");continue}if(d.status===429){if(c=Math.min(c*2,60),l<=0)throw new Error("the code expired before it was entered \u2014 run moa login again");continue}let u=await is(d)||`the workspace said ${d.status}`;throw d.status===403?new re(403,`${u} \u2014 ask an owner to add your GitHub login on Access`):d.status===410?new re(410,`${u} \u2014 run moa login again for a new code`):new re(d.status,u)}}function Xe(e,t=new Date){if(!e)return;let n=Date.parse(e);if(Number.isNaN(n))return;let o=Math.max(0,Math.round((t.getTime()-n)/1e3));if(o<60)return`${o}s`;let r=Math.floor(o/60);if(r<60)return`${r}m`;let s=Math.floor(r/60);return s<24?`${s}h ${r%60}m`:`${Math.floor(s/24)}d ${s%24}h`}function Vo(e,t=new Date){if(!e)return;let n=Date.parse(e);if(!(Number.isNaN(n)||n<=t.getTime()))return Xe(t.toISOString(),new Date(n))}async function ls(e,t=process.env){let n=G(S(e,"json")),o=await ee(x(e,"profile"),t),r=await D(o).get("/v1/whoami"),s=r.scope==="read"?", read-only":"";return n.line(`${r.tenant} at ${o.url} (profile ${o.name}${s})`),n.data({tenant:r.tenant,url:o.url,profile:o.name,...r.scope===void 0?{}:{scope:r.scope}}),0}async function ds(e,t=process.env){let n=G(S(e,"json")),o=e.positional[1]??x(e,"url"),r=o?{url:o,token:""}:await ee(x(e,"profile"),t);try{let s=await D(r).get("/health"),a=Xe(s.startedAt);return n.line(`${r.url} ${s.ok?"ok":"not ok"} (${s.database}, v${s.version}${a?`, up ${a}`:""})`),n.data(s),s.ok?y.ok:y.failed}catch(s){if(s instanceof re&&s.status===503)return n.warn(`moa: ${r.url} is up and cannot work \u2014 ${s.message}`),n.data({ok:!1,url:r.url,status:503,error:s.message}),y.failed;throw s}}function Zi(e,t,n){let o=e.missingTools??[],r=t.filter(s=>s.installed===!1).map(s=>s.cli);return[{name:"this token",ok:n!=="read",detail:n==="read"?"read-only: it may look, and every run, message and grant is refused":`${e.tenant}, and it may start work`},{name:"runs agents",ok:e.runsAgents,detail:e.runsAgents?`${e.database}, v${e.version}${Xe(e.startedAt)?`, up ${Xe(e.startedAt)}`:""}`:"a Worker: it serves the store, and the runs belong to the container beside it"},{name:"last restart",ok:!0,detail:(()=>{let s=Xe(e.restart?.at??e.startedAt),a=e.restart?.cutShort??0;return s?a===0?`${s} ago, and nothing was cut short by it`:`${s} ago; ${a} run${a===1?"":"s"} did not survive it`:"this workspace has not said when it started"})()},{name:"seat capacity",ok:!t.every(s=>s.full===!0),detail:(()=>{let s=t.filter(a=>a.full===!0).map(a=>a.cli);return s.length===0?"room on every seat":s.length===t.length?"every seat is spent \u2014 nothing runs until a window resets":`${s.join(", ")} spent; room on the rest`})()},{name:"whose login",ok:!0,detail:t.length===0?"no seats: this tenant cannot run anything":t.every(s=>s.hasCredential)?"your own credential on every CLI":t.some(s=>s.hasCredential)?`your own on ${t.filter(s=>s.hasCredential===!0).map(s=>s.cli).join(", ")}; the machine's login on the rest`:"the machine's login, shared with every tenant on this workspace"},{name:"run ceiling",ok:!0,detail:e.runTimeoutMinutes===void 0?"this workspace does not say":e.runTimeoutMinutes===0?"no cap: a run that hangs holds its agent until somebody stops it":`a run is stopped after ${e.runTimeoutMinutes} minute(s)`},{name:"agent CLIs",ok:r.length<t.length,detail:(()=>{let s=t.filter(a=>a.installed!==!1).map(a=>a.cli);return r.length===0?s.join(", "):s.length===0?`none: missing ${r.join(", ")}`:`${s.join(", ")} \u2014 missing ${r.join(", ")}`})()},...e.agentBudget?[{name:"agent budget",ok:e.agentBudget.ok,detail:e.agentBudget.ok?`$${e.agentBudget.spentUsd.toFixed(4)} of today's $${e.agentBudget.limitUsd.toFixed(2)} spent asking each other`:e.agentBudget.reason??"today's budget is spent"}]:[],...e.agentTools?[{name:"agent tools",ok:e.agentTools.reachable,detail:e.agentTools.reachable?`${e.agentTools.tools?.length??0} at ${e.agentTools.url}`+(e.agentTools.unwired.length>0?` \u2014 not on ${e.agentTools.unwired.join(", ")}`:""):`${e.agentTools.url}: ${e.agentTools.problem??"no answer"}`}]:[],{name:"repository",ok:!!e.repo,detail:e.repo??"none configured \u2014 pipelines need MOA_REPO"},{name:"checkout",ok:e.hasCheckout,detail:e.checkoutProblem??"a git checkout the pipeline can work in"},{name:"git and gh",ok:o.length===0,detail:o.length===0?"both on its PATH":`missing ${o.join(", ")}`},{name:"github login",ok:e.githubAuthenticated!==!1,detail:e.githubAuthenticated===!1?"gh has no token there \u2014 run `gh auth login` where the workspace runs":"gh has a token"}]}async function us(e,t=process.env){let n=G(S(e,"json")),o=D(await ee(x(e,"profile"),t)),[r,s,a]=await Promise.all([o.get("/v1/workspace"),o.get("/v1/seats"),o.get("/v1/whoami").catch(()=>({scope:void 0}))]),i=Zi(r,s.seats.map(l=>({...l,full:l.limit!==void 0&&(l.used??0)>=l.limit})),a.scope);for(let l of i)n.line(`${l.ok?"ok ":"NO "}${l.name.padEnd(14)}${l.detail}`);let c=i.filter(l=>!l.ok);return n.data({tenant:r.tenant,checks:i}),c.length===0?y.ok:y.halted}async function ps(e,t=process.env){let n=G(S(e,"json")),o=D(await ee(x(e,"profile"),t)),{seats:r}=await o.get("/v1/seats");if(r.length===0)return n.warn("no seats \u2014 this tenant cannot run anything"),n.data({seats:[]}),1;for(let s of r){let a=s.limit?`${s.used}/${s.limit}`:`${s.used}`,i=Vo(s.resetsAt),c=s.limit!==void 0&&s.used>=s.limit,l=s.resetsAt?`${c?" \xB7 full":""}, resets${i?` in ${i}`:" shortly"}`:"",d=s.installed===!1?" \xB7 not installed":"";n.line(`${s.cli.padEnd(8)} ${a.padEnd(8)} ${ec(s)}${l}${d}`)}return n.data({seats:r}),0}var ec=e=>e.hasCredential?"your own credential":"the machine's login, shared with every tenant here";async function fs(e,t=process.env){let n=G(S(e,"json")),o=await Pe(t),r=Object.keys(o.profiles);r.length===0&&n.line("no workspace yet \u2014 run `moa login <url> --token <token>`");for(let s of r)n.line(`${s===o.current?"*":" "} ${s} ${o.profiles[s]?.url}`);return n.data({current:o.current,profiles:r.map(s=>({name:s,url:o.profiles[s]?.url}))}),0}async function ms(e,t=process.env){let n=G(S(e,"json")),o=await Pe(t),r=e.positional[1]??o.current;if(!o.profiles[r]){let a=Object.keys(o.profiles);throw new $(a.length===0?"no workspace is saved":`no profile named ${r} \u2014 there is ${a.join(", ")}`)}delete o.profiles[r];let s=Object.keys(o.profiles);return o.current===r&&(o.current=s[0]??"default"),await it(o,t),n.data({forgot:r,current:o.current,profiles:s}),n.line(`forgot ${r} \u2014 its token is no longer on this machine`),s.length===0?n.line("no workspace left \u2014 `moa login <url> --token <token>`"):r!==o.current?n.line(`now using ${o.current}`):n.line(`now using ${o.current}`),y.ok}async function gs(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa use <profile>");let o=G(S(e,"json")),r=await Pe(t),s=r.profiles[n];if(!s){let i=Object.keys(r.profiles);throw new $(i.length===0?`no profile named ${n} \u2014 run \`moa login <url> --token <token>\` first`:`no profile named ${n} \u2014 there is ${i.join(", ")}`)}r.current=n,await it(r,t);let a=await D(s).get("/v1/whoami").catch(()=>{});return o.line(`now using ${n} \u2014 ${s.url}${a?` as ${a.tenant}`:""}`),o.data({profile:n,url:s.url,...a?{tenant:a.tenant}:{}}),y.ok}function tc(e,t=new Date){let n=/^(\d+)([hd])$/.exec(e.trim());if(!n?.[1])return e;let o=Number(n[1])*(n[2]==="d"?24:1);return new Date(t.getTime()-o*36e5).toISOString()}async function hs(e,t=process.env){let n=G(S(e,"json")),o=D(await ee(x(e,"profile"),t)),r=x(e,"since"),s=r===void 0?void 0:tc(r),a=s===void 0?"/v1/spend":`/v1/spend?since=${encodeURIComponent(s)}`,[i,c]=await Promise.all([o.get(a),o.get("/v1/workspace").catch(()=>{})]);n.data({...i,...c?.agentBudget?{agentBudget:c.agentBudget}:{}});let l=d=>`$${d.toFixed(4)}`;if(n.line(`${l(i.costUsd)} over ${i.runs} ${i.runs===1?"run":"runs"}`+(s===void 0?"":`, since ${s}`)),n.line("notional \u2014 what the CLIs reported, not a bill"),c?.agentBudget){let d=c.agentBudget;n.line(d.ok?`today, agents have spent $${d.spentUsd.toFixed(4)} of $${d.limitUsd.toFixed(2)} asking each other`:`agents are at today's ceiling: $${d.spentUsd.toFixed(4)} of $${d.limitUsd.toFixed(2)} \u2014 calls are refused until UTC midnight`)}return i.runs===0?y.ok:(n.line(""),n.line(he([["AGENT","RUNS","COST"],...i.byAgent.map(d=>[d.agent,String(d.runs),l(d.costUsd)])])),n.line(""),n.line(he([["WHAT ASKED","RUNS","COST"],...i.byCause.map(d=>[d.cause,String(d.runs),l(d.costUsd)])])),y.ok)}async function ks(e,t=process.env){let n=e.positional[1],o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json"));switch(n){case"create":return nc(o,e,r);case"list":case"ls":return oc(o,r);case"show":return rc(o,e,r);case"role":return sc(o,e,r);case"grant":return ws(o,e,r,"grant");case"revoke":return ws(o,e,r,"revoke");case"rm":case"remove":return ac(o,e,r);default:throw new $(`usage: moa agent <create|list|show|role|grant|revoke|rm> \u2026
|
|
147
|
+
moa agent create reviewer --cli codex --role "\u2026" --peers builder,planner`)}}async function nc(e,t,n){let o=t.positional[2],r=x(t,"cli");if(!o)throw new $("usage: moa agent create <name> --cli <cli> [--role \u2026] [--peers a,b]");if(!r)throw new $("--cli is required (claude, codex, cursor, amp, gemini, crush)");let s=(x(t,"peers")??"").split(",").map(i=>i.trim()).filter(Boolean),a=await e.post("/v1/agents",{name:o,cli:r,...x(t,"role")===void 0?{}:{role:x(t,"role")},...s.length===0?{}:{peers:s}});return n.line(`created ${a.name} (${a.cli})`),s.length>0&&n.line(` may open a thread with: ${s.join(", ")}`),n.data(a),0}async function oc(e,t){let[{agents:n},o]=await Promise.all([e.get("/v1/agents"),e.get("/v1/mail").catch(()=>{})]),r=new Map((o?.waiting??[]).map(s=>[s.to,s]));return n.length===0?t.line("no agents yet \u2014 moa agent create <name> --cli claude"):t.line(he([["NAME","CLI","STATUS","MAIL","ROLE"],...n.map(s=>{let a=r.get(s.name);return[s.name,s.cli,s.status,a?`${a.unread} \xB7${Xe(a.oldest)??"?"}`:"\u2014",z(s.role??"\u2014",40)]})])),t.data({agents:n,waiting:o?.waiting??[]}),0}async function rc(e,t,n){let o=t.positional[2];if(!o)throw new $("usage: moa agent show <name>");let[r,s]=await Promise.all([e.get(`/v1/agents/${o}`),e.get(`/v1/agents/${o}/history`)]);n.line(We("name",r.name)),n.line(We("cli",r.cli)),n.line(We("status",r.status)),n.line(We("role",r.role??"\u2014"));let a=s.peers.filter(c=>!c.revokedAt).map(c=>c.to),i=s.peers.filter(c=>c.revokedAt).map(c=>`${c.to} (revoked)`);return n.line(We("peers",[...a,...i].join(", ")||"\u2014")),s.history.length>0&&(n.line(""),n.line(he(s.history.map(c=>[c.at.slice(0,19).replace("T"," "),c.kind,c.actor,z(c.detail??"",40)])))),n.data({agent:r,...s}),0}async function sc(e,t,n){let o=t.positional[2],r=t.positional[3]??x(t,"role");if(!o||!r)throw new $('usage: moa agent role <name> "what it is for"');let s=await e.patch(`/v1/agents/${o}`,{role:r});return n.line(`${s.name} is now: ${z(s.role??"",60)}`),n.data(s),0}async function ws(e,t,n,o){let r=t.positional[2],s=t.positional[3];if(!r||!s)throw new $(`usage: moa agent ${o} <from> <to>`);return o==="grant"?(await e.post(`/v1/agents/${r}/peers`,{to:s}),n.line(`${r} may now open a thread with ${s}`)):(await e.del(`/v1/agents/${r}/peers/${s}`),n.line(`${r} may no longer open a thread with ${s}`)),n.data({from:r,to:s,granted:o==="grant"}),0}async function ac(e,t,n){let o=t.positional[2];if(!o)throw new $("usage: moa agent rm <name>");return await e.del(`/v1/agents/${o}`),n.line(`released ${o}'s workspace \u2014 its runs, mail and edges remain`),n.data({released:o}),0}async function ys(e,t=process.env){let n=D(await ee(x(e,"profile"),t)),o=G(S(e,"json")),r=S(e,"all"),{peers:s}=await n.get("/v1/peers"),a=r?s:s.filter(c=>!c.revokedAt);if(o.data({peers:a}),a.length===0)return o.line(r?"no edges have ever been granted":"no edges \u2014 mail bounces, and only a team loop's own replies move"),y.ok;let i=new Set(s.filter(c=>!c.revokedAt).map(c=>`${c.from}\0${c.to}`));return o.line(he([["FROM","TO","GRANTED","BY","NOTE"],...a.map(c=>[c.from,c.to,c.grantedAt.slice(0,19).replace("T"," "),c.grantedBy,c.revokedAt?`revoked ${c.revokedAt.slice(0,19).replace("T"," ")}`:i.has(`${c.to}\0${c.from}`)?"":"one way"])])),y.ok}Ie();Ye();var It=async(e,t)=>D(await ee(x(e,"profile"),t)),Yo="mesh";function bs(e){let t=/^(\S+) may not open a thread with (.+)$/.exec(e);return t?.[1]&&t[2]?{say:"Nobody granted that edge:",run:t[2].split(",").map(n=>`moa agent grant ${t[1]} ${n.trim()}`)}:/^fanout of \d+ exceeds/.test(e)?{say:"Too many recipients at once. Send to fewer, or send twice.",run:[]}:/^body of \d+ exceeds/.test(e)?{say:"The message is too long for the bus. Say less, or link to it.",run:[]}:/hit maxHops/.test(e)?{say:"This conversation is over its budget. Start a new thread.",run:[]}:/exceeds maxDepth/.test(e)?{say:"Delegations are nested too deep. Hand this one back up.",run:[]}:/cannot address itself/.test(e)?{say:"An agent cannot send itself mail. Give it work instead.",run:[]}:e==="no recipient"?{say:"Nobody was named.",run:[]}:{say:"The bus refused it. Nothing was delivered.",run:[]}}function ro(e){if(e.from===Yo)return/^undelivered:\s*(.+)$/s.exec(e.body)?.[1]?.trim()}async function $s(e,t=process.env){let[,n,o,r]=e.positional;if(!n||!o||!r)throw new $('usage: moa send <from> <a,b,c> "<body>"');let s=o.split(",").map(c=>c.trim()).filter(Boolean);if(s.length===0)throw new $("name at least one recipient");let a=G(S(e,"json")),i=await(await It(e,t)).post("/v1/messages",{from:n,to:s.length===1?s[0]:s,body:r,...x(e,"thread")===void 0?{}:{thread:x(e,"thread")}});return Xo(i,a,`${n} \u2192 ${s.join(", ")}`)}async function vs(e,t=process.env){let n=e.positional[1];if(!n)return ic(e,t);let o=G(S(e,"json")),{messages:r}=await(await It(e,t)).get(`/v1/agents/${n}/inbox`);if(r.length===0)o.line(`${n} has no unread mail`);else{o.line(he([["FROM","KIND","BODY"],...r.map(s=>{let a=ro(s);return[s.from,a?"undelivered":s.kind??"status",z(ln(a??s.body),60)]})]));for(let s of r){let a=ro(s);if(!a)continue;let i=bs(a);o.line(""),o.line(i.say);for(let c of i.run)o.line(` ${c}`)}}return o.data({messages:r}),0}async function ic(e,t){let n=G(S(e,"json")),{waiting:o}=await(await It(e,t)).get("/v1/mail");return n.data({waiting:o}),o.length===0?(n.line("nobody has unread mail"),y.ok):(n.line(he([["AGENT","UNREAD","WAITING","FROM"],...o.map(r=>[r.to,String(r.unread),Xe(r.oldest)??"\u2014",r.from])])),o.some(r=>r.from===Yo)&&(n.line(""),n.line(`${Yo} is the bus: that mail never arrived. moa inbox <name> says why.`)),y.ok)}async function Ss(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa mail <name> [--limit n]");let o=G(S(e,"json")),r=ae(e,"limit")??20,{messages:s}=await(await It(e,t)).get(`/v1/agents/${n}/messages?limit=${r}`);return o.data({messages:s}),s.length===0?(o.line(`nothing has been said to ${n} yet`),y.ok):(o.line(he([["WHEN","WITH","KIND","BODY"],...s.map(a=>[a.createdAt.slice(0,19).replace("T"," "),a.from===n?`\u2192 ${a.to}`:`\u2190 ${a.from}`,(ro(a)?"undelivered":a.kind??"status")+(a.deliveredAt?"":" \xB7unread"),z(ln(ro(a)??a.body),56)])])),y.ok)}async function xs(e,t=process.env){let[,n,o,r]=e.positional;if(!n||!o||!r)throw new $('usage: moa delegate <from> <to> "<task>"');let s=G(S(e,"json")),a=await(await It(e,t)).post("/v1/delegations",{from:n,to:o,task:r,...x(e,"parent")===void 0?{}:{parent:x(e,"parent")}});return a.delivered?(s.line(`${n} handed "${z(r,50)}" to ${o}`),s.line(` thread ${a.thread}`),s.line(` ${n} answers for it until ${o} says done`),s.data(a),y.ok):Xo(a,s,`${n} \u2192 ${o}`)}async function As(e,t=process.env){let n=G(S(e,"json")),o=await(await It(e,t)).get("/v1/delegations"),r=S(e,"blocked")?o.delegations.filter(s=>s.blockedBy):o.delegations;if(r.length===0)n.line(S(e,"blocked")?"nothing is blocked":"nothing is open");else{n.line(he([["FROM","TO","STATE","TASK"],...r.map(s=>[s.from,s.to,s.blockedBy?"BLOCKED":"open",z(ln(s.task),44)])]));for(let s of r.filter(a=>a.blockedBy))n.line(""),n.line(`${s.to} is waiting on an answer:`),n.line(` "${z(ln(s.blockedBy?.body??""),70)}"`),n.line(` moa reply ${s.thread} ${s.from} --kind answer "\u2026"`)}return n.data(o),0}async function Es(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa thread <thread>");let o=G(S(e,"json")),r=await(await It(e,t)).get(`/v1/delegations/${n}`),{delegation:s}=r;if(o.line(We("task",z(ln(s.task),60))),o.line(We("from",s.from)),o.line(We("to",s.to)),o.line(We("state",s.closedAt?"closed":s.blockedBy?"BLOCKED":"open")),s.hops!==void 0&&s.maxHops!==void 0){let a=s.hops>=s.maxHops;o.line(We("hops",a?`${s.hops}/${s.maxHops} \u2014 spent, nothing more can be said in it`:`${s.hops}/${s.maxHops}`))}if(s.depth!==void 0&&s.maxDepth!==void 0){let a=s.depth>=s.maxDepth;o.line(We("depth",a?`${s.depth}/${s.maxDepth} \u2014 it cannot be handed on again`:`${s.depth}/${s.maxDepth}`))}o.line("");for(let a of r.messages)o.line(`${a.from} \xB7 ${a.kind??"status"}`),o.line(` ${ln(a.body)}`);return o.data(r),0}async function Cs(e,t=process.env){let[,n,o,r]=e.positional,s=x(e,"kind");if(!n||!o||!r||!s)throw new $('usage: moa reply <thread> <from> --kind <question|answer|finding|done> "<body>"');let a=G(S(e,"json")),i=await(await It(e,t)).post(`/v1/delegations/${n}/messages`,{from:o,kind:s,body:r});if(!i.delivered)return Xo(i,a,`${o} \u2192 thread`);let c=i.delegation?.closedAt?"closed":i.delegation?.blockedBy?"BLOCKED":"open",l=s==="done"?"finished":`sent ${/^[aeiou]/.test(s)?"an":"a"} ${s}`;return a.line(`${o} ${l} \u2014 the delegation is ${c}`),a.data(i),y.ok}function Xo(e,t,n){if(e.delivered)return t.line(`${n}: delivered`),t.data(e),y.ok;let o=e.reason??"the bus refused it";t.warn(`moa: not delivered \u2014 ${o}`);let r=bs(o);t.warn(` ${r.say}`);for(let s of r.run)t.warn(` ${s}`);return t.data(e),y.halted}var ln=e=>e.replace(/\s+/g," ").trim();Ie();Ye();cn();var cc=`usage: moa manager say "<text>" [--timeout s]
|
|
148
|
+
moa manager proposals
|
|
149
|
+
moa manager approve <id>
|
|
150
|
+
moa manager dismiss <id>
|
|
151
|
+
moa manager watch`;async function _s(e,t=process.env){let n=e.positional[1],o={say:lc,proposals:fc,approve:(a,i,c)=>Ps(a,i,c,"approve"),dismiss:(a,i,c)=>Ps(a,i,c,"dismiss"),watch:wc},r=n!==void 0&&Object.hasOwn(o,n)?o[n]:void 0;if(!r)throw new $(cc);if(n!=="say"&&e.flags.has("timeout"))throw new $(`moa manager ${n} has no --timeout`);let s=D(await ee(x(e,"profile"),t));return r(s,G(S(e,"json")),e)}async function lc(e,t,n){let o=n.positional.slice(2).join(" ").trim();if(!o)throw new $('usage: moa manager say "<text>" [--timeout s]');let r=ae(n,"timeout");if(n.flags.has("timeout")&&(r===void 0||r<0))throw new $("--timeout takes a number of seconds, zero or more");let s=r??600,a=new Set((await e.get("/v1/manager/messages")).messages.map(u=>u.id)),{id:i}=await e.post("/v1/manager/messages",{body:o}),c=Date.now()+s*1e3,l=250,d;for(;;){let u=await e.get("/v1/manager/messages").then(p=>p.messages).catch(p=>{if(!(p instanceof bt))throw p;if(d??=Date.now()+qo,!(Date.now()<d&&Date.now()<c))throw p});u!==void 0&&(d=void 0);let f=u===void 0?void 0:typeof i=="string"?dc(u,i):uc(u,a,o);if(f)return pc(e,t,f);if(u!==void 0&&Date.now()>c)return t.warn(`moa: the Manager has not answered after ${s}s \u2014 it is still thinking, and its answer will be in the console's chat`),y.halted;await new Promise(p=>setTimeout(p,l)),l=Math.min(l*1.5,2e3)}}function dc(e,t){return e.find(n=>n.from==="manager"&&n.answers===t)}function uc(e,t,n){let o=e.filter(s=>!t.has(s.id)),r=o.findIndex(s=>s.from==="you"&&s.body.trim()===n);if(r!==-1)return o.slice(r+1).find(s=>s.from==="manager")}async function pc(e,t,n){let o=n.proposals??[],r=o.length===0?[]:await e.get("/v1/manager/proposals").then(l=>l.proposals).catch(()=>[]),s=o.map(l=>r.find(d=>d.id===l)??{id:l}),a=s.filter(l=>l.state==="waiting"),i=s.filter(l=>l.state===void 0||l.state==="open"),c=l=>l.title??l.kind??"a proposal";if(n.failed?t.warn(`moa: ${n.body}`):t.line(n.body),i.length>0){n.failed||t.line(""),t.line(n.failed?"Before it failed, it proposed:":"It proposes:");for(let l of i)t.line(` ${c(l)}`),t.line(` moa manager approve ${l.id}`)}if(a.length>0){t.line(""),t.line("Set up for later:");for(let l of a)t.line(` ${c(l)}`),t.line(` moa manager dismiss ${l.id}`)}return t.data({message:n,proposals:s}),n.failed?y.failed:y.ok}async function fc(e,t){let n=await e.get("/v1/manager/proposals?state=open");return t.data(n),n.proposals.length===0?(t.line("nothing is waiting for you"),y.ok):(t.line(he([["KIND","WHERE","WHAT","WHY","ID"],...n.proposals.map(o=>[o.kind,mc(o),z(o.title,50),z(o.why.replace(/\s+/g," "),50),o.id])])),y.ok)}var mc=e=>e.repo?`${e.repo}${e.issue?`#${e.issue}`:""}`:"";async function Ps(e,t,n,o){let r=n.positional[2];if(!r)throw new $(`usage: moa manager ${o} <id>`);let s=await hc(e,r,o);if(o==="dismiss"){let i=await e.post(`/v1/manager/proposals/${encodeURIComponent(s)}/dismiss`,{});return t.line(`dismissed: ${i.title}`),t.data(i),y.ok}let a;try{a=await e.post(`/v1/manager/proposals/${encodeURIComponent(s)}/approve`,{})}catch(i){let c=i instanceof re&&i.status===502?gc(i):void 0;if(!c)throw i;return t.warn(`moa: ${c.title} failed${c.result?` \u2014 ${c.result}`:""}`),t.data(c),y.failed}return t.line(`${a.state==="waiting"?"approved, for later":"approved"}: ${a.title}`),a.result&&t.line(` ${a.result}`),t.data(a),y.ok}function gc(e){try{let t=JSON.parse(e.message);return typeof t.id=="string"&&typeof t.title=="string"?t:void 0}catch{return}}async function hc(e,t,n){let o=n==="dismiss"?["open","waiting"]:["open"],s=(await Promise.all(o.map(i=>e.get(`/v1/manager/proposals?state=${i}`)))).flatMap(i=>i.proposals.map(c=>c.id));if(s.includes(t))return t;let a=s.filter(i=>i.startsWith(t));if(a.length>1)throw new $(`${t} could be ${a.length} proposals \u2014 type more of the id`);if(a[0])return a[0];if(!/^[A-Za-z0-9_-]+$/.test(t))throw new $(`${t} is not a proposal's id`);return t}async function wc(e,t){let n=await e.get("/v1/manager/watch");if(t.data(n),n.watching.length===0)return t.line("nothing is running"),y.ok;t.line(he([["HEALTH","KIND","WHAT","WHO","DOING","COST","JOB"],...n.watching.map(o=>[o.held?`${o.health} (held)`:o.health,o.kind,o.ref||z(o.title,30),o.worker??"",z(o.doing,40),`$${o.costUsd.toFixed(3)}`,o.job])]));for(let o of n.watching.filter(r=>r.health!=="ok"))t.line(""),t.line(`${o.ref||o.job}: ${o.note}`),o.suggest&&t.line(` it would say: ${o.suggest}`);return y.ok}import{chmod as kc,writeFile as yc}from"node:fs/promises";import{resolve as bc}from"node:path";Ie();Ye();async function Ts(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa mcp <agent> [--file <path>]");let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json"));if(S(e,"revoke")){let{revoked:f}=await o.del(`/v1/agents/${n}/mcp-token`);return r.data({revoked:f}),r.line(f===0?`${n} had no tokens out`:`took back ${f} token(s) \u2014 any config file holding one is now dead`),y.ok}let s=await o.get(`/v1/agents/${n}/mcp-token`),a=await o.post(`/v1/agents/${n}/mcp-token`,{}),c=(await o.rpc(a.url,a.token,"tools/list")).tools??[];r.data({url:a.url,expiresAt:a.expiresAt,tools:c.map(f=>f.name),outstanding:s.tokens.length}),r.line(`${n} reaches ${a.url}`);for(let f of c)r.line(` ${f.name.padEnd(12)}${f.description??""}`);c.length===0&&r.warn("moa: it answered, and offered no tools");let l=x(e,"file");if(!l)return await o.del(`/v1/agents/${n}/mcp-token/${a.id}`).catch(()=>{}),r.line(""),s.tokens.length>0&&r.line(`${s.tokens.length} token(s) already out, the last until ${s.tokens[s.tokens.length-1]?.expiresAt} \u2014 moa mcp ${n} --revoke takes them back`),r.line("--file <path> writes the config a CLI can load, with a token for this agent"),y.ok;let d={mcpServers:{fnmoa:{type:"http",url:a.url,headers:{Authorization:`Bearer ${a.token}`}}}},u=bc(l);return await yc(u,`${JSON.stringify(d,null,2)}
|
|
152
|
+
`,{mode:384}),await kc(u,384),r.line(""),r.line(`wrote ${u} \u2014 mode 0600, and it expires ${a.expiresAt}`),r.line(` claude --mcp-config ${u} --strict-mcp-config --allowedTools mcp__fnmoa`),y.ok}Ie();Ye();cn();async function*Un(e,t){let n=e.body;if(!n)return;let o=n.getReader(),r=new TextDecoder,s="";try{for(;!t?.aborted;){let{value:a,done:i}=await o.read();if(i)break;s+=r.decode(a,{stream:!0});let c=s.indexOf(`
|
|
153
|
+
|
|
154
|
+
`);for(;c!==-1;){let l=s.slice(0,c);s=s.slice(c+2);let d=$c(l);d&&(yield d),c=s.indexOf(`
|
|
155
|
+
|
|
156
|
+
`)}}}finally{await o.cancel().catch(()=>{})}}function $c(e){let t="message",n,o=[];for(let r of e.split(`
|
|
157
|
+
`)){if(r.startsWith(":")||r.trim()==="")continue;let s=r.indexOf(":"),a=s===-1?r:r.slice(0,s),i=s===-1?"":r.slice(s+1).replace(/^ /,"");a==="event"?t=i:a==="data"?o.push(i):a==="id"&&(n=i)}if(!(o.length===0&&t==="message"))return{event:t,data:o.join(`
|
|
158
|
+
`),...n===void 0?{}:{id:n}}}Ie();Ye();cn();async function Os(e,t=process.env){if(e.positional[1]!=="work")throw new $('usage: moa team work <a,b,c> [--watch] [--max-turns n] [--nudge "\u2026"]');let n=(e.positional[2]??"").split(",").map(u=>u.trim()).filter(Boolean);if(n.length===0)throw new $("usage: moa team work <a,b,c>");let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json")),s=new AbortController,a=S(e,"watch")?n.map(async u=>{let f=await o.stream(`/v1/agents/${u}/events`,s.signal);await xc(f,s,r,u)}):[],i;try{i=await o.post("/v1/team/work",{agents:n,...ae(e,"max-turns")===void 0?{}:{maxTurns:ae(e,"max-turns")},...ae(e,"max-cost")===void 0?{}:{maxCostUsd:ae(e,"max-cost")},...x(e,"nudge")===void 0?{}:{nudge:x(e,"nudge")},...S(e,"route-replies")?{routeReplies:!0}:{}},dn(e))}catch(u){throw s.abort(),u}let c=an(o,i.id,u=>r.warn(u)),l;try{l=await Rt(o,i.id,ae(e,"timeout")??3600,S(e,"watch")?u=>r.line(`\xB7 ${Qo(u)}`):void 0)}finally{c.stop()}if(s.abort(),await Promise.allSettled(a),l.state==="failed")return r.warn(`moa: ${l.error??"the team loop failed"}`),r.data(l),y.failed;let d=l.result;r.line(`${d.outcome}: ${d.reason}`),r.line(` ${d.turns} turn(s), $${d.costUsd.toFixed(3)} notional`);for(let u of d.open)r.line(` still open: ${u.from} \u2192 ${u.to} ${z(u.task,40)}`+(u.blockedBy?` BLOCKED: "${z(u.blockedBy.body,40)}"`:""));return r.data(l),d.outcome==="settled"?y.ok:y.halted}async function Rs(e,t=process.env){let n=Number(e.positional[1]);if(!Number.isInteger(n)||n<1)throw new $("usage: moa ship <issue>");let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json")),s=S(e,"watch"),a=await o.post("/v1/pipelines",{issue:n,...x(e,"repo")===void 0?{}:{repo:x(e,"repo")},...ae(e,"max-steps")===void 0?{}:{maxSteps:ae(e,"max-steps")},...ae(e,"max-cost")===void 0?{}:{maxCostUsd:ae(e,"max-cost")}},dn(e));r.line(`shipping #${n} \u2014 ${a.id}`);let i=an(o,a.id,d=>r.warn(d)),c;try{c=await Rt(o,a.id,ae(e,"timeout")??7200,s?d=>r.line(` ${Qo(d)}`):void 0)}finally{i.stop()}if(c.state==="failed")return r.warn(`moa: ${c.error??"the pipeline failed"}`),r.data(c),y.failed;let l=c.result;return r.line(`${l.outcome}: ${l.reason}`),r.line(` ${l.steps} step(s), $${l.costUsd.toFixed(3)} notional`),r.data(c),l.outcome==="merged"?y.ok:y.halted}function Qo(e){let t=[];return e.step&&t.push(e.step),e.steps!==void 0&&t.push(`${e.steps} step(s)`),e.costUsd!==void 0&&t.push(`$${e.costUsd.toFixed(3)}`),e.note&&t.push(e.note),t.join(" \xB7 ")}async function Is(e,t=process.env){let n=D(await ee(x(e,"profile"),t)),o=G(S(e,"json")),[r,s]=await Promise.all([n.get("/v1/jobs"),n.get("/v1/workspace").catch(()=>({runSlots:void 0}))]);o.data({...r,...s.runSlots?{runSlots:s.runSlots}:{}});let a=so(s.runSlots);if(r.jobs.length===0)return o.line("nothing has run yet"),a&&o.line(a),y.ok;let i=r.jobs.some(c=>(c.issue?.pipelines??0)>1);return o.line(he([["STATE","KIND","WHAT","WHERE",...i?["ISSUE IN ALL"]:[],"JOB"],...r.jobs.map(c=>[c.state,c.kind,c.subject??c.agent??"",z(Sc(c),44),...i?[vc(c)]:[],c.id])])),a&&(o.line(""),o.line(a)),y.ok}function vc(e){let t=e.issue;if(!t)return"";let n=`${t.pipelines} pipeline${t.pipelines===1?"":"s"}`;return`$${t.costUsd.toFixed(2)} \xB7 ${t.steps} step${t.steps===1?"":"s"} \xB7 ${n}`}function so(e){if(!e||e.ceiling===0||e.waiting===0&&e.running<e.ceiling)return;let t=e.waiting===0?"":`, ${e.waiting} ${e.waiting===1?"agent is":"agents are"} waiting for a slot`;return`${e.running} of ${e.ceiling} run slots in use${t}`}function Sc(e){if(e.error)return e.error;if(e.state==="running")return e.progress?Qo(e.progress):"";let t=e.result;return t?t.outcome?t.reason?`${t.outcome}: ${t.reason}`:t.outcome:t.reason?t.reason:t.answered?"answered":t.isError?"the agent reported an error":"":""}async function Ls(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa stop <jobId>");let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json")),s=await o.post(`/v1/jobs/${n}/cancel`,{});return r.line(s.cancelled?`stopping ${n} \u2014 after whatever it is doing now`:`${n} had already ${s.job.state==="running"?"gone":s.job.state}`),r.data(s),y.ok}async function xc(e,t,n,o){try{for await(let r of Un(e,t.signal)){if(r.event!=="activity")continue;let s=JSON.parse(r.data);n.line(` ${o}: ${s.tool?`${s.tool} `:""}${z(String(s.text??""),80)}`)}}catch{}}var dn=e=>x(e,"key")??crypto.randomUUID();function ao(e){if(!e)return;let t=/^`(\S+)` exceeded its (\d+)ms timeout$/.exec(e.trim());if(!t?.[1]||!t[2])return;let n=Number(t[2])/6e4,o=n>=1?`${Number(n.toFixed(1))} minute(s)`:`${Number(t[2])/1e3} second(s)`;return`${t[1]} ran for ${o} without finishing, and the workspace stopped it`}async function Ac(e,t,n,o,r){let s=await e.post(`/v1/agents/${t}/runs`,{prompt:n},o).catch(()=>{});if(s)return r(s.state==="done"||s.state==="failed"?"the workspace restarted; this is what became of the run it had already started":"the workspace restarted, and nothing had been recorded \u2014 asking again"),s}async function Ds(e,t=process.env){let n=e.positional[1],o=e.positional[2];if(!n||!o)throw new $('usage: moa run <name> "<prompt>" [--watch]');let r=D(await ee(x(e,"profile"),t)),s=G(S(e,"json")),a=S(e,"watch"),i=new AbortController,c=a?await r.stream(`/v1/agents/${n}/events?from=next`,i.signal):void 0,l;try{l=await r.post(`/v1/agents/${n}/runs`,{prompt:o},dn(e))}catch(m){throw i.abort(),m}if(S(e,"detach"))return i.abort(),s.line(`started ${l.id}`),s.data(l),y.ok;let d=c?Ns(c,i,s,r):void 0,u=Rc(r,i.signal,m=>s.line(m)),f=an(r,l.id,m=>s.warn(m)),p;try{p=await Rt(r,l.id,ae(e,"timeout")??3600,void 0,()=>Ac(r,n,o,dn(e),m=>s.warn(m)))}finally{f.stop()}return i.abort(),await d,await u,f.asked()?(s.data(p),y.halted):Ic(p,s)}var Ms="the workspace stopped while this run was going",Ec=e=>e===Ms||ao(e)!==void 0;async function Cc(e,t,n=new Date,o){if(!t)return"nothing yet \u2014 it has not said anything";if(Ec(o))return;let r=await e.get("/v1/workspace").then(i=>i.keepActivityDays??0).catch(()=>0),s=Date.parse(t),a=Number.isNaN(s)?0:(n.getTime()-s)/864e5;return r>0&&a>r?`no lines \u2014 this workspace keeps them for ${r} day(s), and this run ended ${Math.floor(a)} day(s) ago`:"no lines \u2014 the CLI backing this agent does not stream, or it said nothing"}async function Pc(e,t){let n=D(await ee(x(e,"profile"),t)),o=G(S(e,"json")),r=(ae(e,"every")??2)*1e3,s=new AbortController,a=()=>s.abort();process.on("SIGINT",a);try{await _c(n,o,r,s.signal)}finally{process.off("SIGINT",a)}return y.ok}async function _c(e,t,n,o){let r=new Map,s,a=!0;for(;!o.aborted;){let[i,c]=await Promise.all([e.get("/v1/jobs").catch(()=>({jobs:[]})),e.get("/v1/workspace").catch(()=>({runSlots:void 0}))]);for(let d of[...i.jobs].reverse()){let u=r.get(d.id);if(u===d.state)continue;r.set(d.id,d.state);let f=u===void 0?a?"":"new":`was ${u}`,p=d.subject??d.agent??d.kind;t.line(`${d.state.padEnd(8)} ${p.padEnd(16)} ${f.padEnd(12)} ${Tc(d)}`.trimEnd()),t.data({job:d.id,kind:d.kind,state:d.state,was:u})}a=!1;let l=so(c.runSlots);if(l!==s&&(l&&t.line(` ${l}`),s=l),o.aborted)return;await new Promise(d=>{let u=setTimeout(d,n);o.addEventListener("abort",()=>{clearTimeout(u),d()},{once:!0})})}}var Tc=e=>{if(e.error)return z(e.error,60);let t=e.result;if(!t)return"";let n=t.outcome??t.result??"",o=t.costUsd?` $${t.costUsd.toFixed(4)}`:"";return`${z(String(n).replace(/\s+/g," "),52)}${o}`.trim()};async function Oc(e){let t="waiting for a rate-limit window";if(!e)return t;let o=(await e.get("/v1/seats").catch(()=>({seats:[]}))).seats.find(s=>s.limit!==void 0&&s.used>=s.limit);if(!o)return t;let r=Vo(o.resetsAt);return`waiting for a window \u2014 ${o.cli} ${o.used}/${o.limit}${r?`, resets in ${r}`:""}`}async function Rc(e,t,n,o=2500){if(await new Promise(a=>{let i=setTimeout(a,o);t.addEventListener("abort",()=>{clearTimeout(i),a()},{once:!0})}),t.aborted)return;let r=await e.get("/v1/workspace").catch(()=>({runSlots:void 0}));if(t.aborted)return;let s=so(r.runSlots);s&&n(` \xB7 ${s}`)}async function Ns(e,t,n,o){let r=!1;try{for await(let s of Un(e,t.signal)){let a=JSON.parse(s.data);if(s.event==="activity"){let i=typeof a.tool=="string"?a.tool:"";n.line(` ${i?`${i} `:""}${z(String(a.text??""),100)}`)}else s.event==="status"&&a.status==="rate-limited"&&!r&&(r=!0,n.line(` \xB7 ${await Oc(o)}`))}}catch{}}function Ic(e,t){if(e.state==="failed"){let o=e.result&&"result"in e.result?e.result.result:void 0;return t.warn(`moa: ${e.error??(o?ao(o)??o:"the run failed")}`),t.data(e),y.failed}let n=e.result;return t.data(e),n&&"stopped"in n?(t.warn(`moa: ${n.reason}`),y.halted):(n?.result&&t.line(ao(n.result)??n.result),n?.isError?y.failed:y.ok)}async function js(e,t=process.env){let n=e.positional[1];if(!n)throw new $("usage: moa logs <runId> [--tail n] [--since seq]");let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json")),s=ae(e,"tail"),a=ae(e,"since");if(s!==void 0&&a!==void 0)throw new $("moa logs takes --tail or --since, not both");let[i,c]=await Promise.all([o.get(`/v1/runs/${n}`),o.get(s!==void 0?`/v1/runs/${n}/activity?tail=${s}`:a===void 0?`/v1/runs/${n}/activity`:`/v1/runs/${n}/activity?since=${a}`)]),l=c.activity,d=c.total??l.length;if(r.line(`${i.agent}: ${z(zo(i.prompt).split(`
|
|
159
|
+
`)[0]??"",80)}`),l.length<d){let p=d-l.length;r.line(` \u2026 ${p} earlier ${p===1?"line":"lines"}, moa logs ${n} for all`)}for(let p of l)r.line(` ${p.tool?`${p.tool} `:""}${z(p.text,100)}`);let u=S(e,"watch")&&!i.endedAt;if(d===0&&!u){let p=await Cc(o,i.endedAt,new Date,i.result);p&&r.line(` ${p}`)}let f=u?await Lc(o,n,l[l.length-1]?.seq??-1,r,e):i;return f.result&&(r.line(""),r.line(ao(f.result)??f.result)),r.data({run:f,activity:l}),f.isError?y.failed:y.ok}async function Lc(e,t,n,o,r){let s=(ae(r,"every")??1)*1e3,a=new AbortController,i=()=>a.abort();process.on("SIGINT",i);let c=n;try{for(;;){let l=await e.get(c<0?`/v1/runs/${t}/activity`:`/v1/runs/${t}/activity?since=${c}`);for(let d of l.activity)c=d.seq,o.line(` ${d.tool?`${d.tool} `:""}${z(d.text,100)}`);if(l.endedAt||a.signal.aborted)return l;if(await new Promise(d=>{let u=setTimeout(d,s);a.signal.addEventListener("abort",()=>{clearTimeout(u),d()},{once:!0})}),a.signal.aborted)return{}}}finally{process.off("SIGINT",i)}}async function Gs(e,t=process.env){let n=e.positional[1],o=e.positional[2],r=e.positional[3];if(!n||!o||!r)throw new $('usage: moa ask <from> <to> "<question>" [--watch]');let s=D(await ee(x(e,"profile"),t)),a=G(S(e,"json")),i=ae(e,"timeout"),c=new AbortController,l=S(e,"watch")?await s.stream(`/v1/agents/${o}/events?from=next`,c.signal):void 0,d;try{d=await s.post("/v1/calls",{from:n,to:o,question:r,...i===void 0?{}:{timeoutMs:i*1e3}},dn(e))}catch(w){throw c.abort(),w}if(S(e,"detach"))return c.abort(),a.line(`started ${d.id}`),a.data(d),y.ok;let u=l?Ns(l,c,a,s):void 0,f=an(s,d.id,w=>a.warn(w)),p;try{p=await Rt(s,d.id,i??3600)}finally{f.stop()}if(c.abort(),await u,a.data(p),p.state==="failed")return a.warn(`moa: ${p.error??"the call failed"}`),y.failed;let m=p.result;return m?.answered?(a.line(m.answer),y.ok):(a.warn(`moa: ${m?.reason??"no answer"}`),y.halted)}var Zo=e=>e.endedAt===void 0?"running":e.isError?e.result===Ms?"cut short":"failed":"done";async function Uc(e){let t=await e.get("/v1/workspace").catch(()=>{}),n=Xe(t?.restart?.at),o=t?.restart?.cutShort??0;if(!(!n||o===0))return`the last restart was ${n} ago, and cut short ${o} ${o===1?"run":"runs"}`}var Us=["running","done","failed","interrupted"];function Dc(e){let t=x(e,"state");if(t!==void 0){if(!Us.includes(t))throw new $(`--state must be one of ${Us.join(", ")}`);return t}return S(e,"failed")?"failed":void 0}var Mc=(e,t,n,o)=>["moa runs",e,t===void 0?void 0:`--state ${t}`,n===10?void 0:`--limit ${n}`,`--before ${o}`].filter(r=>r!==void 0).join(" ");async function Hs(e,t=process.env){let n=e.positional[1],o=Dc(e),r=D(await ee(x(e,"profile"),t)),s=G(S(e,"json")),a=ae(e,"limit")??10,i=x(e,"before"),c=`limit=${a}${o===void 0?"":`&state=${o}`}${i===void 0?"":`&before=${encodeURIComponent(i)}`}`,l=n?(await r.get(`/v1/agents/${n}/runs?${c}`)).runs:(await r.get(`/v1/runs?${c}`)).runs;if(s.data({runs:l}),l.length===0){if(i!==void 0)return s.line(n?`nothing ${n} ran before that one`:"nothing ran before that one"),y.ok;if(o!==void 0){let m=o==="failed"?"ended with an error":o==="interrupted"?"was cut short by a restart":o==="running"?"is running":"has";return s.line(n?`nothing ${n} ran ${m}`:`nothing ${m}`),y.ok}return s.line(n?`${n} has not run yet`:"nothing has run yet"),y.ok}s.line(he([["AGE","STATE",...n?[]:["AGENT"],"CAUSE","COST","TASK","RUN"],...l.map(m=>[Xe(m.startedAt)??"\u2014",Zo(m),...n?[]:[m.agent??""],m.cause??"\u2014",m.costUsd===void 0?"\u2014":`$${m.costUsd.toFixed(4)}`,z(zo(m.prompt).split(`
|
|
160
|
+
`)[0]??"",44),m.id])]));let d=l.filter(m=>Zo(m)==="failed").length,u=l.filter(m=>Zo(m)==="cut short").length,f=u>0?await Uc(r):void 0;o===void 0&&d+u>0&&(s.line(`
|
|
161
|
+
${[d>0?`${d} of them ended with an error`:"",u>0?`${u} ${u===1?"was":"were"} cut short by a restart`:""].filter(Boolean).join(", ")}`),f&&s.line(f));let p=l[l.length-1]?.id;return l.length===a&&p!==void 0&&s.line(`
|
|
162
|
+
older: ${Mc(n,o,a,p)}`),y.ok}async function Fs(e,t=process.env){let n=e.positional[1];if(!n)return Pc(e,t);let o=D(await ee(x(e,"profile"),t)),r=G(S(e,"json")),s=new AbortController,a=()=>s.abort();process.on("SIGINT",a);let i=await o.stream(`/v1/agents/${n}/events`,s.signal);try{for await(let c of Un(i,s.signal)){let l=JSON.parse(c.data);if(c.event==="run")r.line(`\u2014 ${String(l.runId??"")} started ${String(l.startedAt??"")}`);else if(c.event==="activity"){let d=typeof l.tool=="string"?l.tool:"";r.line(` ${d?`${d} `:""}${z(String(l.text??""),100)}`)}else c.event==="status"?r.line(` \xB7 ${String(l.status??"")}`):c.event==="end"?r.line(` = ${z(String(l.result??""),100)}`):c.event==="closing"&&r.warn("moa: the workspace is shutting down");r.data({event:c.event,data:l})}}catch{}finally{process.off("SIGINT",a)}return y.ok}var Nc=["--json","--watch","--detach","--all","--limit","--since","--timeout","--nudge","--route-replies","--profile","--file","--revoke","--token","--github","--help"];function Bs(e,t){let n=[...t].sort().join(" "),o=Nc.join(" ");if(e==="bash")return`# moa completion for bash \u2014 eval "$(moa completion bash)"
|
|
163
|
+
_moa() {
|
|
164
|
+
local commands="${n}"
|
|
165
|
+
local flags="${o}"
|
|
166
|
+
if [ "\${COMP_CWORD}" -eq 1 ]; then
|
|
167
|
+
COMPREPLY=( $(compgen -W "\${commands}" -- "\${COMP_WORDS[1]}") )
|
|
168
|
+
else
|
|
169
|
+
COMPREPLY=( $(compgen -W "\${flags}" -- "\${COMP_WORDS[COMP_CWORD]}") )
|
|
170
|
+
fi
|
|
171
|
+
}
|
|
172
|
+
complete -F _moa moa
|
|
173
|
+
`;if(e==="zsh")return`# moa completion for zsh \u2014 eval "$(moa completion zsh)"
|
|
174
|
+
_moa() {
|
|
175
|
+
local -a commands flags
|
|
176
|
+
commands=(${n})
|
|
177
|
+
flags=(${o})
|
|
178
|
+
if (( CURRENT == 2 )); then
|
|
179
|
+
compadd -a commands
|
|
180
|
+
else
|
|
181
|
+
compadd -a flags
|
|
182
|
+
fi
|
|
183
|
+
}
|
|
184
|
+
compdef _moa moa
|
|
185
|
+
`;throw new $(`moa completion takes bash or zsh, not ${JSON.stringify(e)}`)}import{spawnSync as Ou}from"node:child_process";import{createInterface as Xr}from"node:readline/promises";import{Writable as Ru}from"node:stream";import{render as Iu}from"ink";import{createElement as Lu}from"react";qe();Qt();import{Box as He,Text as oe,useApp as Vd,useInput as Yd}from"ink";import{useEffect as ki,useMemo as Xd,useRef as wt,useState as at}from"react";Ie();qe();jn();Qt();ht();St();Wt();hn();Ze();import{createHash as wd}from"node:crypto";import{mkdir as kd,readFile as yd,rm as bd,writeFile as Dr}from"node:fs/promises";import{dirname as $d,join as Po}from"node:path";var En="cloudflare",Zn=(e,t=2)=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
186
|
+
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),oi=e=>wd("sha256").update(e).digest("hex").slice(0,12);async function Mr(e,t,n){let o=Date.now()+t*1e3;for(;Date.now()<o;){if(await n().catch(()=>!1))return!0;await e.sleep(2e3)}return!1}async function vd(e,t,n,o){o({label:"codex",mark:"wait",detail:"asking codex for a code inside the container\u2026"});let r=await e.fetch(`${t}/_moa/codex/login`,{method:"POST",headers:{authorization:`Bearer ${n}`},signal:AbortSignal.timeout(16*6e4)});if(!r.ok||!r.body)return{ok:!1,why:`${r.status} ${Zn(await r.text().catch(()=>""))}`};let s="",a=r.body.getReader(),i=new TextDecoder;for(;;){let{done:l,value:d}=await a.read();if(l)break;s+=i.decode(d,{stream:!0});let u=qn(s);u.url&&u.code&&o({label:"codex",mark:"wait",detail:`open ${u.url} and enter ${u.code} \u2014 waiting for you to approve it`})}return/\[moa\] codex login exited (\d+)/.exec(s)?.[1]==="0"?{ok:!0}:{ok:!1,why:Zn(s.replace(/\[moa\].*$/m,""))||"codex login did not finish"}}async function _o(e,t,n=()=>{},o=()=>{}){let{sys:r,state:s}=e,a=[],i=q=>{let Q=a.findIndex(je=>je.label===q.label);Q===-1?a.push(q):a[Q]=q,t([...a])},c,l=(q,Q,je)=>(i({label:q,mark:"fail",detail:Q,fix:je}),c?{ok:!1,deployed:c}:{ok:!1}),d=r.root??xn(),u=s.answers.domain,f=s.answers.cloudflareAccount;if(!u||!f)return l("account","no Cloudflare account or domain yet","Esc back to Checks & account, then Enter");if(Zt(e))return l("memory","the last deploy kept memory in LocusGraph, and this run has no secret for it","Esc back to Memory & database and paste it again \u2014 or go on there without it");let p=s.deployed?.runner==="cloudflare"?s.deployed:void 0,m=Ir(e)?.token,w=Lr(e)?.token,b=ze(e)?.token,g=s.answers.agentCli??"claude";if(!m&&!p)return l("D1 token","none in this run \u2014 it is never saved","Esc back to Checks & account and press k");if(!w&&!p)return l("GitHub token","none in this run \u2014 it is never saved","Esc back to GitHub and press k");if(g==="claude"&&!b&&!p)return l("claude token","none in this run \u2014 it is never saved","Esc back to Agent logins and press t (or choose codex there), then Enter");let k=s.answers.githubApp,O=k?Me(r,k):void 0;if(!k||!O)return l("GitHub App",k?`the client secret for ${k.slug} is not on this machine`:"none yet","Esc back to Console access and press g \u2014 on a domain, people sign in with it");let{workspaceUrl:P,consoleUrl:R}=Dn(u),Y=s.answers.tenant??xe(s.answers.owner),we=s.answers.owner,I=ke(e),X=Po(d,"apps","workspace"),U,H;if(De(e)){for(let h of await ut(e))if(i(h),h.mark==="fail")return{ok:!1};for(let h of await An(e))if(i(h),h.mark==="fail")return{ok:!1};let q=`fnmoa:${Ut()}`;if(!(await r.exec("docker",["tag",be(),q])).ok)return l("image","Could not tag the release image","Check Docker and retry");if(!(await Ue(r,d,["containers","push",q],{account:f,timeoutMs:9e5})).ok)return l("image","Could not push to the Cloudflare registry","Check your Cloudflare login and retry");if(U=`registry.cloudflare.com/${f}/${q}`,r.root){let h=await Pr(r);if(!h.ok)return l("worker",`Could not copy the Worker out of ${be()}: ${h.why}`,"Check Docker and retry");H=h.path}else H=Cr();i({label:"image",mark:"ok",detail:`${be()} \u2014 the Worker and the container`})}else{i({label:"build",mark:"wait",detail:"building the Worker"});let q=await r.exec("pnpm",["install"],{cwd:d,timeoutMs:6e5}),Q=q.ok?await r.exec("pnpm",["--filter","@fnmoa/workspace...","run","build"],{cwd:d,timeoutMs:9e5}):q;if(!Q.ok)return l("build",Zn(Q.stderr||Q.stdout),`run \`pnpm install && pnpm --filter @fnmoa/workspace... run build\` in ${d}, then Enter`);i({label:"build",mark:"ok",detail:"the Worker is built from this checkout"})}let{token:W,pending:de,before:_,had:Te}=await Mt(r,En,P);i({label:"owner token",mark:"ok",detail:`${K} \u2014 profile ${En} in ~/.moa/config.json${_===En||!Te?"":`; \`moa\` now talks to this workspace, not profile ${_} (\`moa use ${_}\` to go back)`}`});let pe=s.cloudflare??p?.cloudflare,T=pe?.account===f?pe:void 0,Ae=T!==void 0&&T.older!==!0&&(T.d1?.unsure===!0||T.r2?.unsure===!0),M=p!==void 0&&pe===void 0||T?.older===!0||Ae,N={...T,account:f,...M?{older:!0}:{}},se=q=>{N={...N,...q},o(N)},fe=async q=>{let Q=await Vs(r,d,f),je=Q?.image&&!(N.unsureImages??[]).includes(Q.image)?[Q.image]:[],h=[...new Set([...N.images??[],...je])];se({...q?{worker:Ge}:{},...Q?{containerId:Q.id}:{},...h.length>0?{images:h}:{}})};if(M&&(T===void 0||Ae||T.imagesUnlisted)){let q=await Mn(r,d,f);if(!q.ok)se({imagesUnlisted:!0}),i({label:"images",mark:"skip",detail:`could not list Cloudflare's registry \u2014 ${q.why}; the next deploy, or \`down\`, lists it again for the tags an older moa pushed`});else{let{imagesUnlisted:Q,...je}=N,h=[...new Set([...N.unsureImages??[],...q.images.filter(A=>!(N.images??[]).includes(A))])];N={...je,...h.length>0?{unsureImages:h}:{}},o(N),h.length>0&&i({label:"images",mark:"ok",detail:`${h.length} tag${h.length===1?"":"s"} of ${Le} already in Cloudflare's registry \u2014 an older moa pushed them, so \`down\` asks before deleting them`})}}i({label:"D1",mark:"wait",detail:"finding the database, or making it"});let te=await qs(r,d,f);if(!te.ok)return l("D1",te.why,"fix that, then Enter");let me=te.made||T?.d1?.id===te.id&&T.d1.made,Fe=!me&&(M||T?.d1?.id===te.id&&T.d1.unsure===!0);se({d1:{id:te.id,made:me,...Fe?{unsure:!0}:{}}}),i({label:"D1",mark:"ok",detail:`${te.made?"made":"found"} fnmoa-workspace (${te.id})`});let ve=await zs(r,d,f);if(!ve.ok)return l("R2",ve.why,"R2 must be enabled on the account (dashboard \u2192 R2), then Enter");let Oe=ve.made||T?.r2?.name===lt&&T.r2.made,Ee=!Oe&&(M||T?.r2?.name===lt&&T.r2.unsure===!0);se({r2:{name:lt,made:Oe,...Ee?{unsure:!0}:{}}}),i({label:"R2",mark:"ok",detail:"fnmoa-state \u2014 codex's login, kept across restarts"});let ge=Ft(u),E={MOA_D1_ACCOUNT_ID:f,MOA_D1_DATABASE_ID:te.id,MOA_CONSOLE_HOST:ge.console,MOA_PUBLIC_URL:P,MOA_IDLE_HOURS:String($t),AUTH_GITHUB_ID:k.clientId,AUTH_URL:R,MOA_GITHUB_CLIENT_ID:k.clientId,...we?{MOA_OWNER_LOGIN:we}:{},...s.answers.repo?{MOA_REPO:s.answers.repo}:{},...I?{LOCUSGRAPH_GRAPH_ID:I.graph,LOCUSGRAPH_SERVER_URL:I.server}:{}},V={...await Dt(e),MOA_TOKENS:`${W}:${Y}`,AUTH_GITHUB_SECRET:O.clientSecret,AUTH_SECRET:O.authSecret,...m?{MOA_D1_API_TOKEN:m}:{},...w?{GH_TOKEN:w}:{},...b?{CLAUDE_CODE_OAUTH_TOKEN:b}:{},LOCUSGRAPH_AGENT_SECRET:I?I.secret:""},Be=await r.exec("git",["rev-parse","HEAD"],{cwd:d}),Ke=oi(JSON.stringify([E,Be.stdout.trim()])),et=oi(JSON.stringify(V)),kt=ie(r,Po("cloudflare","wrangler.json")),ce=ie(r,Po("cloudflare","secrets.json")),Ht=ie(r,Po("cloudflare","secrets.fingerprint")),ne=(await yd(Ht,"utf8").catch(()=>"")).trim();await kd($d(kt),{recursive:!0}),await Dr(kt,`${JSON.stringify(Ys({root:d,account:f,databaseId:te.id,domain:u,vars:E,...U?{image:U,worker:H}:{}}),null,2)}
|
|
187
|
+
`),i({label:"deploy",mark:"wait",detail:"the Worker, the container image and your two names \u2014 the first build takes several minutes"});let Ce;try{await Dr(ce,JSON.stringify(V),{mode:384}),Ce=await Ue(r,d,["deploy","--config",kt,"--secrets-file",ce],{account:f,timeoutMs:24e5})}finally{await bd(ce,{force:!0})}if(!Ce.ok){let q=N.worker!==void 0||(await Ue(r,d,["deployments","list","--name",Ge,"--json"],{account:f})).ok;await fe(q);let Q=`${Ce.stdout}
|
|
188
|
+
${Ce.stderr}`;return l("deploy",Zn(Q.split(ce).join("secrets.json")),/containers?.*(plan|paid|not enabled|entitle)/i.test(Q)?"containers need the Workers Paid plan: upgrade the account, then Enter":/docker/i.test(Q)?"start Docker, then Enter":"fix that, then Enter")}await fe(!0);let le=!!I,Je=s.deployed?.workspaceUrl===P?s.deployed.people:void 0;c={runner:"cloudflare",workspaceUrl:P,consoleUrl:R,...Je?{people:Je}:{},...p?.workspaceFor?{workspaceFor:p.workspaceFor}:{},...le?{memoryKept:le}:{},cloudflare:N,at:new Date().toISOString()},n(c),i({label:"deploy",mark:"ok",detail:`Worker ${Ge} \u2014 ${ge.workspace}, ${ge.console}`});let tt=D({url:P,token:W},r.fetch),Rn=(q,Q={})=>r.fetch(`${P}${q}`,{...Q,headers:{authorization:`Bearer ${W}`},signal:AbortSignal.timeout(3e4)});if(i({label:"container",mark:"wait",detail:`waiting for ${ge.workspace} to answer`}),!await Mr(r,600,async()=>(await Rn("/_moa/box")).ok))return l("container",`${P} did not answer the owner token in 10 minutes`,"a new name can take a while for its certificate: Enter to try again");(p?.workspaceFor!==Ke||ne!==et)&&await Rn("/_moa/restart",{method:"POST"}).catch(()=>{}),i({label:"container",mark:"wait",detail:"starting the workspace and console in it"});let Pt={};if(!await Mr(r,600,async()=>(Pt=await(await r.fetch(`${P}/health`,{signal:AbortSignal.timeout(13e4)})).json().catch(()=>({})),Pt.ok===!0)))return l("container",`the workspace did not come up: ${Zn(String(Pt.error??"no answer"),1)}`,`look at \`pnpm --dir ${X} exec wrangler tail ${Ge}\`, then Enter`);if(await Dr(Ht,`${et}
|
|
189
|
+
`,{mode:384}),c={...c,workspaceFor:Ke},n(c),i({label:"container",mark:"ok",detail:`${P} \u2014 kept up ${$t}h after the last request`}),i({label:"database",mark:"ok",detail:`${Pt.database??"d1"} \u2014 migrated, and it answers`}),!await tt.get("/v1/whoami").then(()=>!0,()=>!1))return l("workspace","it answers but refuses the owner token","Enter to try again");de&&await Nt(r,En,P,W);let _t=await Vt(tt,s,i,Je);if(_t)return l(_t.label,_t.detail,_t.fix);c={...c,people:s.answers.people??[]},n(c);let Ko=[g];if(g==="codex")if((await Rn("/_moa/codex").then(Q=>Q.json()).catch(()=>({}))).loggedIn)i({label:"codex",mark:"ok",detail:"logged in inside the container \u2014 kept in R2"});else{let Q=await vd(r,P,W,i).catch(je=>({ok:!1,why:je instanceof Error?je.message:String(je)}));if(!Q.ok)return l("codex",Q.why,"Enter to log in again");i({label:"codex",mark:"ok",detail:"logged in inside the container \u2014 saved to R2"})}let Ot=await Yt(tt,Ko,"in the Cloudflare container",q=>q==="claude"?"Esc back to Agent logins and press t for a new token, then Enter":"Enter to log codex in again",i);if("label"in Ot)return l(Ot.label,Ot.detail,Ot.fix);let Re=await Xt(tt,i);if("label"in Re)return l(Re.label,Re.detail,Re.fix);let sn=Re.live??le;if(!await Mr(r,180,async()=>(await r.fetch(R,{redirect:"manual",signal:AbortSignal.timeout(13e4)})).status<500))return l("console",`${R} did not answer`,`look at \`pnpm --dir ${X} exec wrangler tail ${Ge}\`, then Enter`);i({label:"Console",mark:"ok",detail:`${R} \u2014 Sign in with GitHub`}),i({label:"Workspace",mark:"ok",detail:P}),i({label:"Next",mark:"skip",detail:`moa whoami \xB7 moa agent list \xB7 moa run ${vn(g)} "\u2026"`});let no=Ot.runs[g];return{ok:!0,deployed:{runner:"cloudflare",workspaceUrl:P,consoleUrl:R,workspaceFor:Ke,...no?{proofRunId:no}:{},memory:Re.memory,people:s.answers.people??[],...sn?{memoryKept:!0}:{},cloudflare:N,at:new Date().toISOString()}}}qe();import{randomBytes as ri}from"node:crypto";import{chmod as Sd,mkdir as xd,rm as Ad,writeFile as Ed}from"node:fs/promises";import{createServer as Cd}from"node:http";import{dirname as Pd}from"node:path";function _d(e,t,n){let o=e.replace(/\/+$/,"");return{name:t,url:o,redirect_url:n,callback_urls:[`${o}/api/auth/callback/github`],request_oauth_on_install:!1,public:!1,hook_attributes:{active:!1}}}var Td=(e,t)=>`https://github.com${e?`/organizations/${encodeURIComponent(e)}`:""}/settings/apps/new?state=${encodeURIComponent(t)}`,Od=(e,t)=>t?`https://github.com/organizations/${encodeURIComponent(t)}/settings/apps/${e}`:`https://github.com/settings/apps/${e}`,Nr=e=>e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]),Rd=(e,t)=>`<!doctype html>
|
|
190
|
+
<meta charset="utf-8"><title>moa setup \u2014 GitHub App</title>
|
|
191
|
+
<form id="f" method="post" action="${Nr(e)}">
|
|
192
|
+
<input type="hidden" name="manifest" value="${Nr(JSON.stringify(t))}">
|
|
193
|
+
<p>Taking you to GitHub to create the app\u2026 <button type="submit">Go</button></p>
|
|
194
|
+
</form>
|
|
195
|
+
<script>document.getElementById("f").submit()</script>
|
|
196
|
+
`,To=e=>`<!doctype html><meta charset="utf-8"><title>moa setup</title><p>${Nr(e)}</p>`;async function Id(e){let t=ri(16).toString("hex"),n,o=new Promise((c,l)=>{n={ok:c,no:l}}),r=Cd((c,l)=>{let d=new URL(c.url??"/","http://127.0.0.1"),u=(p,m,w)=>{l.writeHead(p,{"content-type":"text/html; charset=utf-8"}),l.end(m,w)};if(d.pathname==="/"){let p=r.address().port,m=_d(e.consoleUrl,e.name,`http://127.0.0.1:${p}/created`);return u(200,Rd(Td(e.org,t),m))}if(d.pathname!=="/created")return u(404,To("Nothing here."));let f=d.searchParams.get("code");if(!f||d.searchParams.get("state")!==t)return u(400,To("That is not the app moa setup asked for. Try again from setup."));Ld(e.fetch,f).then(p=>u(200,To(`Created ${p.slug}. Back to the terminal.`),()=>n?.ok(p)),p=>u(502,To(`GitHub would not hand the app over: ${p.message}`),()=>n?.no(p)))});await new Promise((c,l)=>{r.once("error",l),r.listen(0,"127.0.0.1",()=>c())});let s=`http://127.0.0.1:${r.address().port}/`,a=setTimeout(()=>n?.no(new Error("no app came back from GitHub \u2014 press g to try again")),e.timeoutMs??15*6e4),i=()=>n?.no(new Error("stopped"));e.signal?.addEventListener("abort",i,{once:!0});try{return e.onPage?.(s),await e.open(s),await o}finally{clearTimeout(a),e.signal?.removeEventListener("abort",i),r.closeAllConnections(),r.close()}}async function Ld(e,t){let n=await e(`https://api.github.com/app-manifests/${encodeURIComponent(t)}/conversions`,{method:"POST",headers:{accept:"application/vnd.github+json","x-github-api-version":"2022-11-28"}}),o=await n.json().catch(()=>({}));if(!n.ok)throw new Error(String(o.message??`GitHub said ${n.status}`));let{id:r,slug:s,client_id:a,client_secret:i,html_url:c,owner:l}=o;if(typeof r!="number"||!s||!a||!i)throw new Error("GitHub's answer had no app in it");return{id:r,slug:s,clientId:a,clientSecret:i,htmlUrl:c??`https://github.com/apps/${s}`,...l?{owner:l}:{}}}async function Ud(e,t){let[n,o]=process.platform==="darwin"?["open",[t]]:process.platform==="win32"?["cmd",["/c","start","",t]]:["xdg-open",[t]];await e.exec(n,o)}async function jr(e,t,n){let o=Eo(e,t);await xd(Pd(o),{recursive:!0}),await Ed(o,`${JSON.stringify(n,null,2)}
|
|
197
|
+
`,{mode:384}),await Sd(o,384)}async function Gr(e,t){await Ad(Eo(e,t),{force:!0})}async function si(e,t,n,o=r=>Ud(e.sys,r)){let{state:r,sys:s}=e,a=r.answers.appOrg,i=r.answers.tenant??xe(r.answers.owner);if(r.target==="vercel"&&!r.answers.sandbox?.consoleUrl)throw new Error("no sandbox yet \u2014 Esc back to Agent logins, where it is made");if(r.target==="cloudflare"&&!r.answers.domain)throw new Error("no domain yet \u2014 Esc back to Checks & account and press d");let c=Ao(r),l=await Id({consoleUrl:c,name:`fnmoa-${i}`.slice(0,34),...a?{org:a}:{},fetch:s.fetch,open:o,onPage:p=>t([{label:"GitHub App",mark:"wait",detail:`click Create on GitHub \u2014 if no browser opened: ${p}`}]),...n?{signal:n}:{}}),d=r.answers.githubApp,u=(d?Me(s,d)?.authSecret:void 0)??ri(32).toString("base64url");await jr(s,l,{clientSecret:l.clientSecret,authSecret:u}),d&&d.id!==l.id&&await Gr(s,d);let f=l.owner?.type==="Organization"?l.owner.login:a;return{githubApp:{id:l.id,slug:l.slug,clientId:l.clientId,settingsUrl:Od(l.slug,f),consoleUrl:c}}}St();Wt();qe();jn();ht();Ze();zt();var Dd={local:"This machine",vercel:"Vercel Sandbox",cloudflare:"Cloudflare"};function ai(e){let{state:t,sys:n}=e,{answers:o}=t,r=t.target??"local",s=[{label:"Target",value:Dd[r]}];s.push({label:"fnMoa",value:De(e)?be():`built from this checkout (${n.root})`}),o.repo&&s.push({label:"Repository",value:o.repo}),o.owner&&s.push({label:"GitHub",value:o.owner}),s.push({label:"Database",value:r==="cloudflare"?"D1":r==="vercel"?o.store==="sqlite"?"SQLite on the sandbox's kept disk":"Postgres on the sandbox's kept disk":o.runner==="compose"?"Postgres, with docker compose":"SQLite"}),s.push({label:"Agent",value:o.agentCli??"claude"}),s.push({label:"Memory",value:ke(e)?"LocusGraph":"in the workspace process"}),r==="cloudflare"&&o.domain?s.push({label:"Domain",value:o.domain}):r==="vercel"&&o.vercelTeam?s.push({label:"Vercel team",value:o.vercelTeam}):r==="local"&&s.push({label:"Ports",value:`workspace ${jt(t)} \xB7 console ${Et(t)}`});let a=nt.filter(c=>c!=="deploy"),i=a.filter(c=>t.passed.includes(c)).length;return s.push({label:"Checks",value:`${i} of ${a.length} screens passed`}),s.push({label:"Cost",value:r==="cloudflare"?`Workers Paid, plus the container (standard-4) while it runs \u2014 up to ${$t}h after the last request`:r==="vercel"?bo(ft(o.vercelPlan??"hobby")):"nothing billed \u2014 it runs on this machine"}),s}Ze();Ie();Ye();qe();Qt();St();hn();Ze();zt();qe();ht();Wt();Ze();zt();qe();ht();St();zt();import{randomUUID as Md}from"node:crypto";import{mkdtemp as ii,readFile as Nd,rm as ci,writeFile as jd}from"node:fs/promises";import{tmpdir as li}from"node:os";import{join as Cn}from"node:path";async function Gd(e,t){if(!t.writeFiles)throw new Error("This sandbox SDK cannot upload the release archive.");let n=await ii(Cn(li(),"fnmoa-image-")),o=`fnmoa-export-${Md()}`,r=!1;try{if(!(await e.sys.exec("docker",["create","--platform","linux/amd64","--name",o,be()])).ok)throw new Error("Could not open the release image.");if(r=!0,!(await e.sys.exec("docker",["cp",`${o}:/repo`,Cn(n,"repo")],{timeoutMs:6e5})).ok)throw new Error("Could not export the built release.");if(!(await e.sys.exec("tar",["-czf",Cn(n,"release.tgz"),"-C",n,"repo"],{timeoutMs:6e5})).ok)throw new Error("Could not archive the built release.");if(await t.writeFiles([{path:"/tmp/fnmoa-release.tgz",content:await Nd(Cn(n,"release.tgz"))}]),await $o(t),!(await _e(t,`set -eu
|
|
198
|
+
mkdir -p "$HOME/moa/release-next"
|
|
199
|
+
tar -xzf /tmp/fnmoa-release.tgz -C "$HOME/moa/release-next"
|
|
200
|
+
rm -f "$HOME/moa/fnmoa"
|
|
201
|
+
ln -s "$HOME/moa/release-next/repo" "$HOME/moa/fnmoa"
|
|
202
|
+
rm -f /tmp/fnmoa-release.tgz`)).ok)throw new Error("Could not install the release; the existing data is kept.")}finally{r&&await e.sys.exec("docker",["rm",o]),await ci(n,{recursive:!0,force:!0})}}async function di(e,t,n){let o=e.sys.vercel;if(!o)return C("sandbox","Vercel SDK unavailable","Update the CLI");try{for(let c of await ut(e))if(c.mark==="fail")return c;let r=zn(e.state.answers.tenant??e.state.answers.owner??"workspace"),s=await o.get(t,r,!0),a=Ut();if(s&&await vo(s)===a)return{box:s,built:!1,info:{name:r,workspaceUrl:s.domain(8787),consoleUrl:s.domain(3100),revision:a}};for(let c of await An(e))if(c.mark==="fail")return c;if(s)n("Updating the application files; keeping the sandbox's data disk"),await Gd(e,s);else{let c=await e.sys.fetch(`https://api.vercel.com/v9/projects/${encodeURIComponent(t.projectId)}?teamId=${encodeURIComponent(t.teamId)}`,{headers:{authorization:`Bearer ${t.token}`},signal:AbortSignal.timeout(15e3)});if(!c.ok)throw new Error("Could not resolve the Vercel project's registry name.");let{name:l}=await c.json();if(!/^[a-z0-9-]+$/i.test(l)||!/^[a-z0-9-]+$/i.test(t.team))throw new Error("Invalid Vercel registry namespace.");let d=`vcr.vercel.com/${t.team}/${l}/fnmoa:${a}`,u=await ii(Cn(li(),"fnmoa-vcr-"));try{if(await jd(Cn(u,"config.json"),JSON.stringify({auths:{"vcr.vercel.com":{auth:Buffer.from(`${t.teamId}:${t.token}`).toString("base64")}}}),{mode:384}),n(`Copying ${be()} to Vercel`),!(await e.sys.exec("docker",["tag",be(),d])).ok)throw new Error("Could not tag the release for Vercel.");if(!(await e.sys.exec("docker",["--config",u,"push",d],{timeoutMs:9e5})).ok)throw new Error("Could not push the release to Vercel Container Registry.")}finally{await ci(u,{recursive:!0,force:!0})}if(n("Starting the sandbox from the release image"),s=await o.create(t,{name:r,size:ft(t.plan),image:`fnmoa:${a}`}),!(await _e(s,`set -eu
|
|
203
|
+
mkdir -p "$HOME/moa"
|
|
204
|
+
ln -s /repo "$HOME/moa/fnmoa"`)).ok)throw new Error("Could not prepare the sandbox's application directory.")}if(!(await _e(s,'printf %s "$VERSION" > "$HOME/moa/image"',{VERSION:a})).ok)throw new Error("Could not record the installed version.");return{box:s,built:!0,info:{name:r,workspaceUrl:s.domain(8787),consoleUrl:s.domain(3100),revision:a}}}catch(r){return C("sandbox",r instanceof Error?r.message:String(r),"Check your account and retry; data is kept.")}}var Hr=e=>{if(!e.vercel)throw new Error("this build of moa cannot reach Vercel");return e.vercel},Fr=e=>e.answers.tenant??xe(e.answers.owner),ui=e=>{let t=/^git@([^:]+):(.+)$/.exec(e.trim());return t?`https://${t[1]}/${t[2]}`:e.trim()};async function fi(e){let t=e.root;if(!t)return{why:"not inside a fnMoa checkout",fix:"run moa setup from a clone of https://github.com/fnLog0/fnmoa"};let n=(...c)=>e.exec("git",["-C",t,...c]),[o,r,s]=await Promise.all([n("rev-parse","HEAD"),n("remote","get-url","origin"),n("branch","-r","--contains","HEAD")]),a=ye(o.stdout),i=ye(r.stdout);return!o.ok||!a||!r.ok||!i?{why:"this checkout has no commit, or no origin",fix:"clone fnMoa from GitHub"}:!s.ok||!s.stdout.trim()?{why:`commit ${a.slice(0,7)} is not on ${ui(i)} \u2014 the sandbox clones it from there`,fix:"push it, or check out a commit that is pushed, then press r"}:{url:ui(i),revision:a}}async function Br(e){try{let{vercelTeam:t,vercelScope:n}=e.state.answers;return await Hr(e.sys).account(t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return C("account",n,/not installed/.test(n)?"install the Vercel CLI: npm i -g vercel":/signed in|token/i.test(n)?"run `vercel login`, then press r":"check the team (e), then press r")}}var Oo=e=>typeof e=="object"&&e!==null&&"mark"in e;async function Hd(e){let{sys:t}=e,o=[Number(process.versions.node.split(".")[0])>=22?v("node",process.versions.node):C("node",process.versions.node,"install node 22 or newer"),...await Promise.all([en(t,"git","install git"),en(t,"gh","install the GitHub CLI: https://cli.github.com"),en(t,"vercel","install the Vercel CLI: npm i -g vercel")])],r=De(e)?{url:"GHCR",revision:be()}:await fi(t);o.push("why"in r?C("fnMoa",r.why,r.fix):v("fnMoa",`${r.revision.slice(0,7)} from ${r.url} \u2014 built in the sandbox`));let s=await Br(e);if(Oo(s))return o.push(s),{lines:o};let a=ft(s.plan);return o.push(v("account",`${s.user} \xB7 team ${s.team} \xB7 ${Ha(s.plan,a)}`),v("estimate",bo(a)),v("storage",Fa(a))),s.plan==="hobby"&&o.push(J("note","Hobby: a session stops after 45 minutes \u2014 `moa setup status` starts it again, and pipelines carry on")),{lines:o,answers:{vercelTeam:s.team,vercelPlan:s.plan,vercelScope:{teamId:s.teamId,projectId:s.projectId}}}}async function Ro(e){if(e.secrets.github)return{token:e.secrets.github,from:"pasted"};if(e.state.answers.githubToken==="pasted"&&e.state.deployed?.runner==="vercel")return{kept:!0};let t=await e.sys.exec("gh",["auth","token"]),n=ye(t.stdout);return t.ok&&n?{token:n,from:"own"}:void 0}async function pi(e,t,n){try{let o=await e.fetch(`https://api.github.com${n}`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json"},signal:AbortSignal.timeout(15e3)});return o.ok?await o.json():o.status}catch{return 0}}async function Fd(e){let{sys:t,state:n}=e,o=await t.exec("gh",["api","user","--jq",".login"]),r=ye(o.stdout);if(!o.ok||!r)return{lines:[C("gh login",o.missing?"gh is not installed":"gh has no login on this machine","run `gh auth login`, then press r to check again")]};let s=[v("owner",`${r} \u2014 you, the first owner`)],a=await Ro(e),i,c;if(!a)s.push(C("GitHub token","none for the sandbox","press k to paste a fine-grained token \u2014 a bot account's is best"));else if("kept"in a)c="pasted",s.push(J("GitHub token","the one pasted before, kept in the sandbox (k to replace it)"));else{let d=await pi(t,a.token,"/user");typeof d=="number"?s.push(C("GitHub token",`GitHub refused it${d?` (${d})`:""}`,"press k to paste another \u2014 contents, pull requests, issues read/write; checks, actions read")):(i=a.token,c=a.from,s.push(v("GitHub token",a.from==="pasted"?`${K} \u2014 ${d.login??"?"}${d.login?.toLowerCase()===r.toLowerCase()?" (your own)":" (bot account)"}`:`${K} \u2014 your own gh login; a bot account's is better (k to paste one)`)))}let l=n.answers.repo;if(!l)s.push(J("repo","none \u2014 a pipeline can name any repository later (e to set one)"));else if(i){let d=await pi(t,i,`/repos/${l}`);s.push(typeof d!="number"&&d.permissions?.push?v("repo",`${l} \u2014 the token can push`):C("repo",typeof d=="number"?`${l} is not a repository the token can see`:`the token cannot push to ${l}`,"give the token this repository, or press e to change it"))}return{lines:s,answers:{owner:r,...c?{githubToken:c}:{}}}}async function Kr(e,t,n=()=>{}){if(De(e))return di(e,t,n);let o=Hr(e.sys),r=await fi(e.sys);if("why"in r)return C("sandbox",r.why,r.fix);let s=zn(Fr(e.state)),a=e.state.answers.sandbox?.name===s?e.state.answers.sandbox:void 0,i=a?.teamId===t.teamId&&a.projectId===t.projectId?a.snapshotId:void 0,c=ft(t.plan),l,d=i,u=!1;try{n(`looking for ${s}`),l=await o.get(t,s,!0),!l&&d&&(n(`making ${s} from its snapshot \u2014 ${c.vcpus} vCPU, persistent`),l=await o.create(t,{name:s,size:c,snapshotId:d}).catch(()=>{}),l||(d=void 0)),l||(n(`making ${s} \u2014 ${c.vcpus} vCPU, persistent`),l=await o.create(t,{name:s,size:c}),u=!0)}catch(p){return C("sandbox",p instanceof Error?p.message.split(`
|
|
205
|
+
`)[0]??"":String(p),"check the account on Checks & account, then press r")}let f=!1;if(await vo(l)!==r.revision){n("stopping the servers before the build");try{await $o(l)}catch(w){return C("sandbox",`the servers did not stop: ${w instanceof Error?w.message:String(w)}`,"press r to try again")}n(`building the image at ${r.revision.slice(0,7)} \u2014 several minutes the first time`);let p=await Ro(e),m=await Ja(l,r.url,r.revision,p&&"token"in p?p.token:void 0);if(!m.ok)return C("sandbox",`the image did not build: ${mt(m.stderr||m.stdout)}`,"fix that, then press r");if(f=!0,u){n("snapshotting the image");try{d=await l.snapshot(),l=await o.get(t,s,!0)??l}catch{}}}return{box:l,built:f,info:{name:s,workspaceUrl:l.domain(st),consoleUrl:l.domain(qt),...d?{snapshotId:d,teamId:t.teamId,projectId:t.projectId}:{},revision:r.revision}}}var Bd=async(e,t)=>(await _e(e,`test -s "$HOME/moa/env/workspace/${t}"`)).ok;async function Kd(e,t){return _e(e,t?'claude -p "$PROMPT"':'CLAUDE_CODE_OAUTH_TOKEN=$(cat "$HOME/moa/env/workspace/CLAUDE_CODE_OAUTH_TOKEN") exec claude -p "$PROMPT"',{PROMPT:mn,...t?{CLAUDE_CODE_OAUTH_TOKEN:t}:{}},18e4)}async function Jd(e){let{state:t}=e,n=await Br(e);if(Oo(n))return{lines:[n]};let o=await Kr(e,n);if(Oo(o))return{lines:[o]};let{box:r,info:s}=o,a=ft(n.plan),i=[v("sandbox",`${s.name} \u2014 ${r.vcpus??a.vcpus} vCPU, persistent, fnMoa ${s.revision?.slice(0,7)??""}`)],c={claude:!1,codex:!1},l=ze(e),d=!l&&await Bd(r,"CLAUDE_CODE_OAUTH_TOKEN");if(l||d){let p=await Kd(r,l?.token);c.claude=p.ok&&/\bok\b/i.test(p.stdout),i.push(c.claude?v("claude",`token ${K} (${l?l.from:"kept in the sandbox"}) \u2014 "reply ok" in the sandbox`):C("claude",`the token did not answer: ${mt(p.stdout||p.stderr)||"no answer"}`,"press t to make a new one with `claude setup-token`, or e to paste it again"))}else i.push(J("claude","no token carried in \u2014 t to make one with `claude setup-token`"));let u=await r.exec("codex",["login","status"],{timeoutMs:6e4});c.codex=u.ok,i.push(u.ok?v("codex",`${ye(u.stdout)||"logged in"} \u2014 its own session in the sandbox (l to log in again)`):J("codex","not logged in inside the sandbox \u2014 l to log in with a code"));let f=t.answers.agentCli??(c.claude?"claude":c.codex?"codex":void 0);return f?i.push(c[f]?v("proof run",`uses ${f} (\u2191 \u2193 to change)`):C("proof run",`${f} is not ready in the sandbox`,f==="claude"?"press t to carry a token in \u2014 or \u2191 \u2193 to choose codex":"press l to log codex in \u2014 or \u2191 \u2193 to choose claude")):i.push(C("agent","neither claude nor codex is logged in inside the sandbox","press t to carry a claude token in, or l to log codex in")),i.push(J("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)")),{lines:i,answers:{sandbox:s,...f?{agentCli:f}:{},loggedIn:["claude","codex"].filter(p=>c[p])}}}function mi(e,t){let n=!1,o=()=>{n=!0};return{done:(async()=>{let s=await Br(e);if(Oo(s))return{ok:!1,stdout:"",stderr:s.detail??"no account"};let a=e.state.answers.sandbox?.name??zn(Fr(e.state)),i=await Hr(e.sys).get(s,a,!0);if(!i)return{ok:!1,stdout:"",stderr:`${a} is not there`};if(n)return{ok:!1,stdout:"",stderr:"stopped"};let c="",l=i.stream("codex",["login","--device-auth"],d=>{c+=d,t(qn(c))});return o=l.cancel,l.done})(),cancel:()=>o()}}async function Wd(e){let{sys:t,state:n,secrets:o}=e,r=ke(e),s=[];if(r){let i=await gn(t,r,Fr(n)),c=`secret ${K} (${r.from}), graph ${r.graph}`;s.push(i.read.ok&&i.write.ok?v("LocusGraph",`${c} \u2014 one graph read, one event written`):C("LocusGraph",`${c} \u2014 ${i.read.ok?`the write failed: ${i.write.why}`:`the read failed: ${i.read.why}`}`,"e to paste the secret again, or check LOCUSGRAPH_GRAPH_ID \u2014 then r"))}else n.deployed?.runner==="vercel"&&n.deployed.memoryKept&&!o.noLocusgraph?s.push(J("LocusGraph","the secret the last deploy gave the sandbox, kept there (e to replace it)")):s.push(J("LocusGraph","no secret \u2014 memory stays in the workspace process (e to paste one)"));let a=n.answers.store??"postgres";return s.push(a==="postgres"?v("database","Postgres, in the sandbox on its kept disk \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):v("database","SQLite, on the sandbox's kept disk \u2014 migrated at deploy (\u2191 \u2193 for Postgres)")),{lines:s,answers:{store:a}}}function qd(e){let t=Ur(e),{githubApp:n,sandbox:o}=e.state.answers,r=[...t.lines];return o?n&&n.consoleUrl!==o.consoleUrl?r.unshift(C("callback",`${n.slug} signs people in at ${n.consoleUrl??"localhost"}, not ${o.consoleUrl}`,"press g to create an app for the sandbox's console")):r.unshift(v("console",`${o.consoleUrl} \u2014 GitHub sends people back here`)):r.unshift(C("console","no sandbox yet","Esc back to Agent logins: the sandbox is made there")),{lines:r,answers:t.answers??{}}}var gi={...Co,checks:Hd,github:Fd,logins:Jd,memory:Wd,access:qd};var tn="vercel";async function Io(e,t,n){let o=Date.now()+n*1e3;for(;Date.now()<o;){if(await e.fetch(t,{signal:AbortSignal.timeout(5e3)}).then(s=>s.ok).catch(()=>!1))return!0;await e.sleep(1e3)}return!1}var zd=e=>typeof e=="object"&&e!==null&&"mark"in e;async function Lo(e){if(!e.sys.vercel)return{label:"account",detail:"this build of moa cannot reach Vercel",fix:"update moa"};try{let{vercelTeam:t,vercelScope:n}=e.state.answers;return await e.sys.vercel.account(t,n)}catch(t){return{label:"account",detail:t instanceof Error?t.message:String(t),fix:"run `vercel login`, then Enter"}}}var nn=e=>typeof e=="object"&&e!==null&&"fix"in e&&"label"in e;async function Uo(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=V=>{let Be=s.findIndex(Ke=>Ke.label===V.label);Be===-1?s.push(V):s[Be]=V,t([...s])},i,c=(V,Be,Ke)=>(a({label:V,mark:"fail",detail:Be,fix:Ke}),i?{ok:!1,deployed:i}:{ok:!1}),l=r.answers.githubApp,d=l?Me(o,l):void 0;if(!l||!d)return c("GitHub App",l?`the client secret for ${l.slug} is not on this machine`:"none yet","Esc back to Console access and press g");a({label:"sandbox",mark:"wait",detail:"looking for it"});let u=await Lo(e);if(nn(u))return c(u.label,u.detail,u.fix);let f=await Kr(e,u,V=>a({label:"sandbox",mark:"wait",detail:V}));if(zd(f))return c("sandbox",f.detail??"",f.fix??"Enter to try again");let{box:p,info:m,built:w}=f,b=ft(u.plan);a({label:"sandbox",mark:"ok",detail:`${m.name} \u2014 ${p.vcpus??b.vcpus} vCPU, persistent${w?", image built":""}`});let g=m.workspaceUrl,k=m.consoleUrl;if(l.consoleUrl!==k)return c("GitHub App",`${l.slug} signs people in at ${l.consoleUrl??"localhost"}, not ${k}`,"Esc back to Console access and press g");let{token:O,pending:P,before:R,had:Y}=await Mt(o,tn,g);a({label:"owner token",mark:"ok",detail:`${K} \u2014 profile ${tn} in ~/.moa/config.json${R===tn||!Y?"":`; \`moa\` now talks to the sandbox, not profile ${R} (\`moa use ${R}\` to go back)`}`});let we=r.answers.tenant??xe(r.answers.owner),I=r.answers.owner,X=ke(e),U=ze(e)?.token,H=await Ro(e),W={...Object.fromEntries(Object.entries(await Dt(e)).map(([V,Be])=>[`workspace/${V}`,Be])),store:r.answers.store??"postgres","workspace/MOA_TOKENS":`${O}:${we}`,"workspace/PORT":String(st),"workspace/MOA_PORT":String(st),"workspace/MOA_SELF_URL":`http://127.0.0.1:${st}`,"workspace/MOA_MAX_CONCURRENT_RUNS":String(b.maxRuns),"workspace/MOA_REPO":r.answers.repo,"workspace/MOA_OWNER_LOGIN":I,"workspace/MOA_GITHUB_CLIENT_ID":l.clientId,"workspace/LOCUSGRAPH_AGENT_SECRET":X?.secret,"workspace/LOCUSGRAPH_GRAPH_ID":X?.graph,"workspace/LOCUSGRAPH_SERVER_URL":X?.server,"workspace/CLAUDE_CODE_OAUTH_TOKEN":U,"workspace/GH_TOKEN":H&&"token"in H?H.token:void 0,"console/MOA_URL":`http://127.0.0.1:${st}`,"console/MOA_TOKEN":O,"console/AUTH_GITHUB_ID":l.clientId,"console/AUTH_GITHUB_SECRET":d.clientSecret,"console/AUTH_SECRET":d.authSecret,"console/AUTH_URL":k,"console/AUTH_TRUST_HOST":"true"};if(!H)return c("secrets","no GitHub token for the sandbox","Esc back to GitHub and press k to paste one");let de=["workspace/MOA_CLIENT_ADDRESS",...r.answers.repo?[]:["workspace/MOA_REPO"],...I?[]:["workspace/MOA_OWNER_LOGIN"],...e.secrets.noLocusgraph?["workspace/LOCUSGRAPH_AGENT_SECRET","workspace/LOCUSGRAPH_GRAPH_ID","workspace/LOCUSGRAPH_SERVER_URL"]:[]];a({label:"secrets",mark:"wait",detail:"writing them into the sandbox"});let _=await Ba(p,W,de);if(!_.ok)return c("secrets",`not written: ${_.why??"no answer"}`,"Enter to try again");let Te=["workspace/MOA_TOKENS","workspace/LOCUSGRAPH_AGENT_SECRET","workspace/CLAUDE_CODE_OAUTH_TOKEN","workspace/GH_TOKEN","console/MOA_TOKEN","console/AUTH_GITHUB_SECRET","console/AUTH_SECRET"].filter(V=>W[V]!==void 0);a({label:"secrets",mark:"ok",detail:`${Te.length} written \u2014 none shown; no Vercel token in the sandbox`});let pe=_.changed||w;a({label:"workspace",mark:"wait",detail:pe?`starting on :${st}`:"checking it runs"});try{await br(p,pe)}catch(V){return c("workspace",`the start did not run: ${V instanceof Error?V.message:String(V)}`,"Enter to try again")}let T=r.deployed?.workspaceUrl===g?r.deployed.people:void 0,Ae=V=>{i={runner:"vercel",workspaceUrl:g,consoleUrl:k,...T?{people:T}:{},...V?{memoryKept:V}:{},at:new Date().toISOString()},n(i)},M=r.deployed?.runner==="vercel"?r.deployed.memoryKept:void 0,N=X?!0:e.secrets.noLocusgraph?!1:M;if(Ae(N),!await Io(o,`${g}/health`,180))return c("workspace",`did not answer at ${g} \u2014 ${await wn(p,"workspace")||"no log yet"}`,"fix that, then Enter");let se=D({url:g,token:O},o.fetch);if(!await se.get("/v1/whoami").then(()=>!0,()=>!1))return c("workspace","it started but refuses the owner token","Enter to try again");P&&await Nt(o,tn,g,O),a({label:"workspace",mark:"ok",detail:`${g} \u2014 :${st} in the sandbox`});let te=await xr(o,g,a,r.answers.store??"postgres",`look at the sandbox's ~/moa/logs \u2014 ${await wn(p,"postgres")}`);if(te)return c(te.label,te.detail,te.fix);let me=await Vt(se,r,a,T);if(me)return c(me.label,me.detail,me.fix);let Fe=T;if(T=r.answers.people??[],JSON.stringify(T)!==JSON.stringify(Fe??[])&&Ae(N),a({label:"console",mark:"wait",detail:`waiting for :${qt}`}),!await Io(o,k,180))return c("console",`did not answer at ${k} \u2014 ${await wn(p,"console")||"no log yet"}`,"fix that, then Enter");a({label:"console",mark:"ok",detail:`${k} \u2014 Sign in with GitHub`}),pe&&await Ka(p);let ve=r.answers.agentCli??"claude",Oe=[...new Set([ve,...r.answers.loggedIn??[]])],Ee=await Yt(se,Oe,"in the sandbox",()=>"Esc back to Agent logins and log it in again, then Enter",a);if("label"in Ee)return c(Ee.label,Ee.detail,Ee.fix);let ge=await Xt(se,a);if("label"in ge)return c(ge.label,ge.detail,ge.fix);ge.live!==void 0&&(N=ge.live),a({label:"Console",mark:"ok",detail:k}),a({label:"Workspace",mark:"ok",detail:g}),a({label:"Next",mark:"skip",detail:`moa setup status \xB7 moa setup stop \xB7 moa run ${vn(ve)} "\u2026"`});let E=Ee.runs[ve];return{ok:!0,deployed:{runner:"vercel",workspaceUrl:g,consoleUrl:k,...E?{proofRunId:E}:{},memory:ge.memory,people:T,...N?{memoryKept:N}:{},at:new Date().toISOString()}}}async function Do(e,t){let n=e.state.answers.sandbox;if(e.state.target!=="vercel"||!n)return{label:"sandbox",detail:"moa setup has not made one",fix:"run `moa setup` and choose Vercel Sandbox"};let o=await Lo(e);if(nn(o))return o;try{let r=await e.sys.vercel?.get(o,n.name,t);return r?{box:r}:{label:"sandbox",detail:`${n.name} is not there`,fix:"run `moa setup` to make it again"}}catch(r){return{label:"sandbox",detail:r instanceof Error?r.message:String(r),fix:"check `vercel whoami`, then try again"}}}async function hi(e,t){let{sys:n,state:o}=e,r=await Do(e,!0);if(nn(r))return t({label:r.label,mark:"fail",detail:r.detail,fix:r.fix}),!1;let{box:s}=r;t({label:"sandbox",mark:"ok",detail:`${s.name} \u2014 ${s.status}${s.vcpus?`, ${s.vcpus} vCPU`:""}`});let a=o.answers.sandbox;try{await br(s,!1)}catch(w){return t({label:"workspace",mark:"fail",detail:`the start did not run: ${mt(w instanceof Error?w.message:String(w))}`,fix:"try again, or run `moa setup` to deploy it again"}),!1}let i={workspace:s.domain(st),console:s.domain(qt)};if(i.workspace!==a.workspaceUrl||i.console!==a.consoleUrl){let w=[...i.workspace!==a.workspaceUrl?[`the workspace is at ${i.workspace}, not ${a.workspaceUrl}`]:[],...i.console!==a.consoleUrl?[`the console is at ${i.console}, not ${a.consoleUrl}`]:[]];return t({label:"URLs",mark:"fail",detail:`the sandbox's domain has changed \u2014 ${w.join("; ")}`,fix:"run `moa setup` to deploy it again, and press g on Console access for the new callback"}),!1}if(!await Io(n,`${a.workspaceUrl}/health`,180))return t({label:"workspace",mark:"fail",detail:`no answer at ${a.workspaceUrl} \u2014 ${await wn(s,"workspace")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!1;let l=(await Pe(n.env)).profiles[tn];if(!l||l.url!==a.workspaceUrl)return t({label:"workspace",mark:"fail",detail:`${a.workspaceUrl} answers, but this machine has no token for it`,fix:"run `moa setup` to deploy it again"}),!1;let d=D(l,n.fetch),u=await n.fetch(`${a.workspaceUrl}/health`,{signal:AbortSignal.timeout(5e3)}).then(w=>w.json()).catch(()=>({})),f=await d.get("/v1/whoami").then(()=>{},w=>w instanceof re?`${w.status}`:String(w));t(f?{label:"workspace",mark:"fail",detail:`refuses the owner token (${f})`,fix:"run `moa setup` to deploy it again"}:{label:"workspace",mark:"ok",detail:`${a.workspaceUrl}${u.database?` \u2014 ${u.database}`:""}`});let p=await d.get("/v1/jobs").then(w=>w.jobs).catch(()=>{});if(p){let w=p.filter(b=>b.state==="running"||b.state==="queued");t({label:"pipelines",mark:"ok",detail:w.length===0?"nothing running":`${w.length} running or waiting \u2014 carried on from before the stop (moa jobs)`})}let m=await Io(n,a.consoleUrl,180);return t(m?{label:"console",mark:"ok",detail:`${a.consoleUrl} \u2014 Sign in with GitHub`}:{label:"console",mark:"fail",detail:`no answer at ${a.consoleUrl} \u2014 ${await wn(s,"console")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!f&&m}async function wi(e,t){let n=await Do(e,!1);if(nn(n))return t({label:n.label,mark:"fail",detail:n.detail,fix:n.fix}),!1;try{await n.box.stop()}catch(o){return t({label:"sandbox",mark:"fail",detail:`did not stop: ${mt(o instanceof Error?o.message:String(o))}`,fix:"try again"}),!1}return t({label:"sandbox",mark:"ok",detail:`${n.box.name} stopped \u2014 its disk is kept; \`moa setup status\` starts it again`}),!0}import{jsx as Z,jsxs as $e}from"react/jsx-runtime";var Ne=(e,t)=>({...e,answers:{...e.answers,...t}}),Qd=[{value:"vercel",label:"Vercel Sandbox",note:"one VM, 24h sessions, resumes by name"},{value:"cloudflare",label:"Cloudflare",note:"Worker + container, needs your domain"},{value:"local",label:"This machine",note:"Docker / local, nothing in the cloud"}],Zd={where:{options:e=>Qd.map(t=>{let n=mo(t.value,Jn(e,t.value));return n?{...t,note:n}:t}),value:e=>e.target,set:(e,t)=>rt(e,t)},checks:{options:()=>[{value:"image",label:"Published image",note:"pull the release from GHCR \u2014 nothing is built here"},{value:"checkout",label:"This checkout",note:"build from source \u2014 for working on fnMoa"}],value:e=>fn(e),set:(e,t)=>Ne(e,{source:t})},logins:{options:()=>[{value:"claude",label:"claude",note:"for the proof run"},{value:"codex",label:"codex",note:"for the proof run"}],value:e=>e.answers.agentCli,set:(e,t)=>Ne(e,{agentCli:t})},memory:{options:e=>e.target==="vercel"?[{value:"postgres",label:"Postgres",note:"in the sandbox, on its kept disk"},{value:"sqlite",label:"SQLite",note:"a file on the sandbox's kept disk"}]:[{value:"node",label:"SQLite",note:"node apps/workspace/dist/node.js"},{value:"compose",label:"Postgres",note:"docker compose up (apps/workspace/compose.yaml)"}],value:e=>e.target==="vercel"?e.answers.store??"postgres":e.answers.runner??"node",set:(e,t)=>e.target==="vercel"?Ne(e,{store:t}):Ne(e,{runner:t})}},Jr=e=>e.target==="vercel",bi=/^[a-z\d](?:[a-z\d-]{0,38})$/i;function eu(e,t){let n=e.answers.people??[],o=f=>p=>p.login.toLowerCase()===f.toLowerCase(),r="a GitHub login, then member or viewer, then a cap in $/day \u2014 or -login to remove",[s,...a]=t.split(/\s+/).filter(Boolean);if(!s)return r;if(s.startsWith("-")){let f=s.slice(1);if(!n.some(o(f)))return`${f} is not on the list`;let p=n.filter(w=>!o(f)(w)),m=[...new Set([...e.answers.removed??[],f.toLowerCase()])];return Ne(e,{people:p.length>0?p:void 0,removed:m})}if(!bi.test(s))return r;if(e.answers.owner&&s.toLowerCase()===e.answers.owner.toLowerCase())return`${s} is you \u2014 the owner already`;let i=n.find(o(s)),c=i?.role??"member",l=i?.dailyCapUsd??dr;for(let f of a){let p=f.toLowerCase(),m=Number(p.replace(/^\$|\/day$/g,""));if(p==="member"||p==="viewer")c=p;else if(Number.isFinite(m)&&m>=0&&m<=1e4)l=m;else return r}let d={login:s,role:c,dailyCapUsd:l},u=(e.answers.removed??[]).filter(f=>f!==s.toLowerCase());return Ne(e,{people:i?n.map(f=>o(s)(f)?d:f):[...n,d],removed:u.length>0?u:void 0})}var Mo=e=>e.target==="cloudflare",tu={checks:[{key:"l",label:"Licence key",secret:!0,shown:(e,t)=>t.licence?K:"saved key, or none",commit:(e,t,n)=>(t.licence=n||void 0,e)},{key:"d",label:"Domain",when:Mo,shown:e=>e.answers.domain??"none",commit:(e,t,n)=>{let o=n.toLowerCase().replace(/^https?:\/\//,"").replace(/\/+$/,"");return o===""?Ne(e,{domain:void 0}):er.test(o)?Ne(e,{domain:o}):"a domain on your Cloudflare account, like example.com"}},{key:"k",label:"D1 token",secret:!0,when:Mo,shown:(e,t)=>t.cloudflare?K:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces":(t.cloudflare=n||void 0,e)},{key:"e",label:"Vercel team",when:Jr,shown:e=>e.answers.vercelTeam??"the one Vercel picks",initial:e=>e.answers.vercelTeam??"",commit:(e,t,n)=>n===""||/^[\w-]{1,100}$/.test(n)?Ne(e,{vercelTeam:n||void 0,vercelScope:void 0}):"a team's slug or id, as `vercel teams ls` shows it"}],github:[{key:"e",label:"Repo",shown:e=>e.answers.repo??"none",commit:(e,t,n)=>n===""?{...e,answers:{...e.answers,repo:void 0}}:/^[\w.-]+\/[\w.-]+$/.test(n)?Ne(e,{repo:n}):"owner/name, like fnLog0/fnmoa"},{key:"k",label:"GitHub token",secret:!0,when:e=>Jr(e)||Mo(e),shown:(e,t)=>t.github?K:Jr(e)?"none \u2014 your own gh login":"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces":(t.github=n||void 0,e)}],logins:[{key:"e",label:"claude token",secret:!0,shown:(e,t)=>t.claude?K:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces \u2014 what `claude setup-token` printed":(t.claude=n||void 0,e)}],memory:[{key:"e",label:"LocusGraph secret",secret:!0,shown:(e,t)=>t.locusgraph?K:"none",commit:(e,t,n)=>(t.locusgraph=n||void 0,t.noLocusgraph=!n,e)}],access:[{key:"e",label:"Tenant",shown:e=>e.answers.tenant??"",commit:(e,t,n)=>/^[a-z0-9][a-z0-9-]{0,62}$/.test(n)?Ne(e,{tenant:n}):"lower-case letters, digits and dashes"},{key:"o",label:"App owner",shown:e=>e.answers.appOrg??"your account (o for an organization)",initial:e=>e.answers.appOrg??"",commit:(e,t,n)=>n===""||bi.test(n)?Ne(e,{appOrg:n||void 0}):"a GitHub organization's login, or nothing for your own account"},{key:"a",label:"Add people",shown:()=>"login member|viewer $/day \u2014 -login takes them off",initial:()=>"",commit:(e,t,n)=>eu(e,n)}]},Wr=(e,t)=>{let n=[...e];for(let o of t){let r=n.findIndex(s=>s.label===o.label);r===-1?n.unshift(o):n[r]=o}return n},yi={ok:{char:"\u2713",color:"green"},fail:{char:"\u2717",color:"red"},wait:{char:"\u2026",color:"yellow"},skip:{char:"\u2013",color:"gray"}},qr={where:"Where",checks:"Checks",github:"GitHub",memory:"Memory",logins:"Logins",access:"Access",deploy:"Deploy"},nu=Object.fromEntries(nt.filter(e=>e!=="where"&&e!=="deploy").map((e,t)=>[String(t+1),e])),ou={local:"this machine",vercel:"the Vercel Sandbox",cloudflare:"Cloudflare"},ru={ok:"ok",fail:"failing",wait:"checking"};function su({screen:e,passed:t}){return Z(He,{columnGap:2,flexWrap:"wrap",children:nt.map(n=>n===e?$e(oe,{bold:!0,color:"cyan",children:["\u25B8 ",qr[n]]},n):t.includes(n)?$e(oe,{color:"green",children:["\u2713 ",qr[n]]},n):$e(oe,{dimColor:!0,children:[" ",qr[n]]},n))})}function No({title:e,aside:t,children:n}){return $e(He,{flexDirection:"column",marginTop:1,children:[$e(He,{justifyContent:"space-between",children:[Z(oe,{bold:!0,dimColor:!0,children:e.toUpperCase()}),t?Z(oe,{dimColor:!0,children:t}):null]}),n]})}function au({rows:e}){let t=Math.max(...e.map(n=>n.label.length));return Z(No,{title:"review",children:e.map(n=>$e(He,{children:[Z(He,{flexShrink:0,width:t+2,children:Z(oe,{dimColor:!0,children:n.label})}),Z(oe,{children:n.value})]},n.label))})}function iu({hints:e}){return Z(He,{columnGap:2,flexWrap:"wrap",paddingX:1,children:e.map(t=>{let n=t.indexOf(" ");return $e(oe,{children:[Z(oe,{bold:!0,children:t.slice(0,n)}),Z(oe,{dimColor:!0,children:t.slice(n)})]},t)})})}var cu=e=>[e.deployed?{label:"last deploy",mark:"skip",detail:`console ${e.deployed.consoleUrl} \xB7 workspace ${e.deployed.workspaceUrl} \u2014 Enter to check it again`}:{label:"deploy",mark:"wait",detail:e.target==="vercel"?"Enter to write the secrets into the sandbox, start the workspace and console, and prove it with one agent run and one memory read":e.target==="cloudflare"?"Enter to deploy the Worker, D1 and the container to your domain, and prove it with one agent run and one memory read":"Enter to build, start the workspace and console, and prove it with one agent run and one memory read"}];function $i({initial:e,sys:t,save:n,checks:o,deploy:r,createApp:s=si,onDone:a,secrets:i,onHandOver:c,pasteFirst:l=!1,deviceLogin:d=Ra,after:u={}}){let{exit:f}=Vd(),[p,m]=at(()=>e.screen==="where"&&!e.target?{...e,target:"local"}:e),[w,b]=at([]),[g,k]=at(!1),[O,P]=at(0),R=l&&e.screen==="logins",[Y,we]=at(R?"":void 0),[I,X]=at(R?"e":void 0),U=wt(void 0),H=wt([]),W=h=>Wr(h,H.current),[de,_]=at(void 0),[Te,pe]=at(!1),[T,Ae]=at("no"),M=wt(i??{}).current,N=wt(void 0),[se,fe]=at(0),te=wt(Promise.resolve()),me=wt(0),[Fe,ve]=at(!1),Oe=wt(!1),Ee=h=>{Oe.current=h,ve(h)},ge=wt(e.screen!=="where").current,E=p.screen,V=p.target==="vercel",Be=o??(V?gi:Co),Ke=r??(V?Uo:p.target==="cloudflare"?_o:So),et=E==="checks"&&De({sys:t,state:p}),kt=V||p.answers.runner==="compose",ce=wt(p);ce.current=p;let Ht=wt(p),ne=h=>{Ht.current!==h&&(Ht.current=h,te.current=te.current.then(()=>n(h)).catch(()=>{}))};ki(()=>ne(p),[p,n]);let Ce=()=>(ne(ce.current),te.current),{answers:le}=p,Je=JSON.stringify([E,p.target,le,O,se]);ki(()=>{if(E==="deploy"){g||b(cu(p));return}let h=++me.current;b(A=>A.map(B=>H.current.some(L=>L.label===B.label)?B:{...B,mark:"wait"})),k(!0),Promise.resolve(Be[E]?.({state:p,sys:t,secrets:M})??{lines:[]}).then(async A=>({...A,lines:[...A.lines,...!o&&E==="checks"?await ut({state:p,sys:t,secrets:M}):[]]})).then(A=>{if(h!==me.current)return;let B=W(A.lines);b(B),U.current||k(!1);let L=A.answers??{};Object.entries(L).some(([In,oo])=>p.answers[In]!==oo)&&m(In=>Ne(In,L));let yt=Oe.current;Ee(!1),yt&&Qn(B)&&Bo(E)},A=>{h===me.current&&(b(W([{label:"check",mark:"fail",detail:String(A),fix:"press r to check again"}])),U.current||k(!1),Ee(!1))})},[Je]);let tt=h=>{U.current?.abort(),Ce().then(()=>{a?.(ce.current,h),f()})},Rn=()=>{k(!0),_(void 0);let h=L=>b([{label:"licence",mark:"wait",detail:L},{label:"note",mark:"skip",detail:"sign in there and approve it \u2014 Esc to stop"}]);h("asking the licence server for a code\u2026");let A=new AbortController,B={done:go({state:p,sys:t,secrets:M},L=>h(`open ${L.url} and check it shows ${L.code}`),A.signal).then(()=>({ok:!0,stdout:"",stderr:""}),L=>({ok:!1,stdout:"",stderr:L instanceof Error?L.message:String(L)})),cancel:()=>A.abort()};N.current=B,B.done.then(L=>{N.current===B&&(N.current=void 0,k(!1),L.ok||_(`not connected: ${L.stderr}`),P(j=>j+1))})},Qr=()=>{let h=De({sys:t,state:p})?void 0:t.root;if(!h&&!V)return;k(!0);let A=L=>b([{label:"codex",mark:"wait",detail:L.url&&L.code?`open ${L.url} and enter ${L.code} \u2014 waiting for you to approve it`:`asking codex for a code inside the ${V?"sandbox":"container"}\u2026`},{label:"note",mark:"skip",detail:"its own session, not a copy of this machine's \u2014 Esc to stop"}]);A({});let B=V||!h?mi({state:p,sys:t,secrets:M},A):d(t,h,A);N.current=B,B.done.then(L=>{N.current===B&&(N.current=void 0,k(!1),L.ok||_(`codex login did not finish: ${(L.stderr||L.stdout).trim().split(`
|
|
206
|
+
`).slice(-1)[0]??""}`),P(j=>j+1))})},Pt=h=>{Ae("no"),_(void 0),Ee(!1),H.current=[],b([]),m(A=>({...A,screen:h}))},Bo=h=>{ce.current.screen===h&&(m(A=>({...A,passed:[...new Set([...A.passed,h])]})),Pt(u[h]??ya(h)))},Zr=()=>{k(!0),b([]),Ke({state:p,sys:t,secrets:M},b,B=>{let L={...ce.current,deployed:B};ce.current=L,ne(L),m(j=>({...j,deployed:B}))},B=>{let L={...ce.current,cloudflare:B};ce.current=L,ne(L),m(j=>({...j,cloudflare:B}))}).then(B=>{let L=B.deployed;B.ok&&L?(pe(!0),m(j=>({...j,answers:{...j.answers,removed:void 0},deployed:L,passed:[...new Set([...j.passed,"deploy"])]}))):L&&m(j=>({...j,deployed:L}))}).catch(B=>b(L=>[...L,{label:"deploy",mark:"fail",detail:String(B),fix:"Enter to try again"}])).finally(()=>k(!1))},_t=h=>{H.current=Wr(H.current,h),b(A=>Wr(A,h))},Ko=()=>{let h=new AbortController;U.current=h,k(!0),_(void 0),_t([{label:"GitHub App",mark:"wait",detail:"starting a page for GitHub\u2026"}]),s({state:p,sys:t,secrets:M},_t,h.signal).then(A=>{U.current=void 0,k(!1),H.current=[],m(B=>Ne(B,A)),P(B=>B+1)},A=>{U.current=void 0,k(!1),!h.signal.aborted&&_t([{label:"GitHub App",mark:"fail",detail:A instanceof Error?A.message:String(A),fix:"press g to try again"}])})},Tt=(tu[E]??[]).filter(h=>!h.when||h.when(p)),Ot=Tt.find(h=>h.key===I),Re=E==="memory"&&Mo(p)||E==="checks"&&!t.root?void 0:Zd[E],sn=E==="access"?p.answers.githubApp:void 0;Yd((h,A)=>{if(Y!==void 0&&Ot){if(A.escape){we(void 0),X(void 0);return}if(A.return){let j=Ot.commit(p,M,Y.trim());if(typeof j=="string"){_(j);return}_(void 0),we(void 0),X(void 0),fe(yt=>yt+1),m(j);return}if(A.backspace||A.delete){we(j=>(j??"").slice(0,-1));return}h&&!A.ctrl&&!A.meta&&we(j=>`${j??""}${h}`);return}if(N.current){(A.escape||h==="q")&&(N.current.cancel(),N.current=void 0,k(!1),P(j=>j+1));return}if(h==="q")return tt(Te);if(A.escape){if(T==="asking")return Ae("no");E!=="where"&&!(g&&(E==="deploy"||U.current))&&Pt(ba(E));return}if(g){A.return&&E!=="deploy"&&Ee(!0);return}if(H.current=[],h==="r")return P(j=>j+1);let B=E==="deploy"&&!Te?nu[h]:void 0;if(B)return Pt(B);if(E==="logins"&&h==="t"&&c){Ce().then(()=>{c(ce.current),f()});return}if(E==="logins"&&h==="l"&&kt)return Qr();if(E==="checks"&&h==="c"&&et)return Rn();let L=Tt.find(j=>j.key===h);if(L){_(void 0),X(L.key),we(L.secret?"":L.initial?L.initial(p):L.shown(p,M)==="none"?"":L.shown(p,M));return}if(E==="access"&&h==="g")return Ko();if(sn&&h==="d"){m(j=>{let yt=j.answers.githubApp;return yt?Ne(j,{githubApp:{...yt,deviceFlow:!yt.deviceFlow}}):j});return}if((A.upArrow||A.downArrow)&&Re){let j=Re.options(p).map(Wo=>Wo.value),yt=j.indexOf(Re.value(p)??j[0]??""),In=A.upArrow?-1:1,oo=j[(yt+In+j.length)%j.length];oo!==void 0&&m(Wo=>Re.set(Wo,oo));return}if(A.return){if(E==="deploy")return Te?tt(!0):T==="no"?Ae("asking"):(Ae("yes"),Zr());if(!Qn(w))return;Bo(E)}});let Jo=Math.max(...w.map(h=>h.label.length),8),no=[E==="deploy"?Te?"Enter done":"Enter deploy":"Enter continue",E==="where"?void 0:"Esc back","q quit",Re?"\u2191 \u2193 choose":void 0,...Tt.length===1?["e edit"]:Tt.map(h=>`${h.key} ${h.label.toLowerCase()}`),E==="access"?sn?"g new GitHub App":"g create GitHub App":void 0,sn?sn.deviceFlow?"d undo device flow":"d device flow is ticked":void 0,E==="logins"&&c?"t claude setup-token":void 0,E==="logins"&&kt?"l codex login":void 0,et?"c connect account":void 0,E==="deploy"?void 0:"r check again",E==="deploy"&&!Te?"1\u20135 change a step":void 0].filter(Boolean),q=Xd(()=>E==="deploy"?ai({state:p,sys:t,secrets:M}):[],[E,p,t,M,se]),Q=["ok","fail","wait"].map(h=>[h,w.filter(A=>A.mark===h).length]).filter(([,h])=>h>0).map(([h,A])=>`${A} ${ru[h]}`),je=Math.max(...Tt.map(h=>h.label.length),0);return $e(He,{flexDirection:"column",children:[$e(He,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",paddingX:1,children:[Z(oe,{bold:!0,color:"cyan",children:E==="where"?"moa setup":`${$a(E)} ${Bn[E]}`}),E==="where"?null:Z(su,{screen:E,passed:p.passed}),E==="where"?Z(oe,{children:Bn.where}):null,ge&&E===e.screen&&E!=="where"?Z(oe,{color:"yellow",children:"resumed where the last setup stopped"}):null,Re?Z(No,{title:"choose",children:Re.options(p).map(h=>{let A=Re.value(p)===h.value;return $e(oe,{color:A?"cyan":void 0,children:[A?" \u25CF ":" \u25CB ",Z(oe,{bold:A,children:h.label.padEnd(16)}),Z(oe,{dimColor:!0,children:h.note})]},h.value)})}):null,Tt.length>0?Z(No,{title:"settings",children:Tt.map(h=>$e(oe,{children:[h.label.padEnd(je)," ",Y!==void 0&&h.key===I?$e(oe,{color:"cyan",children:[h.secret?Y?K:"":Y,"\u258F"]}):Z(oe,{dimColor:!h.shown(p,M),children:h.shown(p,M)||"\u2014"})]},h.key))}):null,de?Z(He,{marginTop:1,children:$e(oe,{color:"red",children:["\u2717 ",de]})}):null,E==="deploy"?Z(au,{rows:q}):null,Z(No,{title:"status",aside:E==="deploy"?void 0:Q.join(" \xB7 "),children:w.map(h=>$e(He,{flexDirection:"column",children:[$e(He,{children:[Z(He,{flexShrink:0,width:Jo+4,children:$e(oe,{children:[Z(oe,{color:yi[h.mark].color,children:yi[h.mark].char})," ",h.label]})}),Z(oe,{children:h.detail??""})]}),h.mark==="fail"&&h.fix?Z(He,{paddingLeft:Jo+4,children:$e(oe,{color:"red",children:["\u2192 ",h.fix]})}):null]},h.label))}),T==="asking"?Z(He,{marginTop:1,children:$e(oe,{color:"yellow",bold:!0,children:["Enter again to deploy to ",ou[p.target??"local"],p.answers.domain&&p.target==="cloudflare"?` (${p.answers.domain})`:""," ","\u2014 Esc to cancel"]})}):null,Fe&&g?Z(oe,{color:"yellow",children:"still checking \u2014 Enter goes on once every line is \u2713"}):null]}),Z(iu,{hints:Y!==void 0?["Enter keep","Esc cancel"]:N.current?["Esc stop the login"]:T==="asking"?["Enter deploy now","Esc cancel"]:no})]})}Ie();Ye();qe();jn();Qt();import{randomBytes as lu}from"node:crypto";import{rm as vi}from"node:fs/promises";import{join as jo}from"node:path";ht();St();Wt();hn();Ze();Vn();zt();var du="codex/auth.json",Ve=(e,t=2)=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
207
|
+
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),_n=e=>e instanceof Error?e.message:String(e);function Gt(e){let t=pr(e);return t.length>0?t:e.target?[e.target]:[]}var Tn=(e,t)=>({...e,state:rt(e.state,t??e.state.target)}),Ai=(e,t)=>e.state?{...e,state:rt(e.state,t)}:e,eo=e=>e==="vercel"?tn:e==="cloudflare"?En:bn;function Ct(e){let t=e.deployed;if(!(!t||!e.target))return e.target==="local"?t.runner==="node"||t.runner==="compose"||t.runner==="image"?t:void 0:t.runner===e.target?t:void 0}var On=e=>(e({label:"fnMoa",mark:"fail",detail:"moa setup has not deployed it here yet",fix:"run `moa setup`"}),{ok:!1});async function Si(e,t,n){for(let o=0;o<n*1e3;o+=500){if(!e.alive(t))return!0;await e.sleep(500)}return!e.alive(t)}var uu=15,pu=5,Vr=(e,t,n,o={},r=3e4)=>e.fetch(t,{...o,headers:{authorization:`Bearer ${n}`},signal:AbortSignal.timeout(r)});async function Go(e,t,n){let o=(await Pe(e.env)).profiles[eo(t)];return o?.url===n?o.token:void 0}async function Ei(e,t,n){let{sys:o}=e,r=Date.now()+t.seconds*1e3,s;for(;s=await o.fetch(`${t.workspaceUrl}/health`,{signal:AbortSignal.timeout(t.seconds>0?13e4:5e3)}).then(l=>l.json()).catch(()=>{}),!(s?.ok===!0||Date.now()>=r);)await o.sleep(2e3);let a=!0,i=await Go(o,t.target,t.workspaceUrl);if(!s)a=!1,n({label:"workspace",mark:"fail",detail:`no answer at ${t.workspaceUrl}`,fix:t.fix});else if(!i)a=!1,n({label:"workspace",mark:"fail",detail:`${t.workspaceUrl} answers, but this machine has no token for it`,fix:"run `moa setup` to deploy it again"});else{let l=D({url:t.workspaceUrl,token:i},o.fetch),d=await l.get("/v1/whoami").then(()=>{},u=>u instanceof re?`${u.status}`:_n(u));if(d)a=!1,n({label:"workspace",mark:"fail",detail:`refuses the owner token (${d})`,fix:"run `moa setup` to deploy it again"});else{n({label:"workspace",mark:"ok",detail:`${t.workspaceUrl}${t.where}`}),s.ok===!0?n({label:"database",mark:"ok",detail:`${s.database??"the store"} \u2014 it answers`}):(a=!1,n({label:"database",mark:"fail",detail:`${s.database??"the store"} does not answer: ${Ve(String(s.error??"no answer"),1)}`,fix:t.fix}));let u=await l.get("/v1/jobs").then(f=>f.jobs).catch(()=>{});if(u){let f=u.filter(p=>p.state==="running"||p.state==="queued");n({label:"pipelines",mark:"ok",detail:f.length===0?"nothing running":`${f.length} running or waiting (moa jobs)`})}}}let c=await o.fetch(t.consoleUrl,{redirect:"manual",signal:AbortSignal.timeout(t.seconds>0?13e4:5e3)}).then(l=>l.status<500).catch(()=>!1);return n(c?{label:"console",mark:"ok",detail:t.consoleUrl}:{label:"console",mark:"fail",detail:`no answer at ${t.consoleUrl}`,fix:t.fix}),a&&c}async function fu(e,t){let n=Ct(e.state);if(!n)return On(t);let o=Sn(e.sys,n),r=o.workspacePid,s=o.runner==="compose"?` \u2014 docker compose, project ${ue}`:r!==void 0&&At(e.sys,r,o.workspaceStarted,gt(o.workspaceUrl))?` (pid ${r})`:"";return{ok:await Ei(e,{target:"local",workspaceUrl:o.workspaceUrl,consoleUrl:o.consoleUrl,where:s,fix:"run `moa setup` to start it again",seconds:0},t)}}async function mu(e,t){let n=Ct(e.state);if(!n)return On(t);let{sys:o}=e,r=await Go(o,"cloudflare",n.workspaceUrl);if(!r)return t({label:"workspace",mark:"fail",detail:`this machine has no token for ${n.workspaceUrl}`,fix:"run `moa setup` to deploy it again"}),{ok:!1};let s=await Vr(o,`${n.workspaceUrl}/_moa/box`,r).then(async i=>i.ok?await i.json():`${i.status} ${Ve(await i.text().catch(()=>""),1)}`.trim()).catch(i=>_n(i));return typeof s=="string"?(t({label:"Worker",mark:"fail",detail:`${n.workspaceUrl} did not answer: ${s}`,fix:"run `moa setup` to deploy it again"}),{ok:!1}):(t({label:"container",mark:"ok",detail:s.running?`running \u2014 kept up ${$t}h after the last request`:"was stopped \u2014 this check starts it again, on its data in D1"}),{ok:await Ei(e,{target:"cloudflare",workspaceUrl:n.workspaceUrl,consoleUrl:n.consoleUrl,where:" \u2014 in the Cloudflare container",fix:`look at \`pnpm --dir apps/workspace exec wrangler tail ${Ge}\`, or run \`moa setup\` to deploy it again`,seconds:600},t)})}var Pn={local:"This machine",vercel:"Vercel Sandbox",cloudflare:"Cloudflare"};function gu(e,t){let n=pr(e);for(let o of n){let r=Jn(e,o);t({label:o,mark:"skip",detail:`${Pn[o]} \u2014 ${mo(o,r)}${ka(o,r)?"; billed while it stands":""}`})}return n}async function hu(e,t){return e.state.target==="vercel"?{ok:await hi(e,t)}:e.state.target==="cloudflare"?mu(e,t):fu(e,t)}async function Ci(e,t,n){let o=gu(e.state,t),r=n==="all"?Gt(e.state):n?[n]:o.length===1&&o[0]?[o[0]]:[],s=r.length>0?r:[e.state.target??"local"],a=!0;for(let i of s){s.length>1&&t({label:i,mark:"skip",detail:`\u2500\u2500 ${Pn[i]}`});let c=await hu(Tn(e,i),t);a=a&&c.ok}return{ok:a}}async function zr(e,t,n,o=!1){let{sys:r}=e,s=r.root;if(!s)return t({label:"workspace",mark:"fail",detail:"not inside a fnMoa checkout: docker compose needs its compose.yaml",fix:"run it from your fnMoa checkout"}),!1;let a=await r.exec("docker",n==="down"?xt(s,"down","--volumes","--remove-orphans","--rmi","local"):xt(s,"stop"),{cwd:s,env:Jt(r),timeoutMs:3e5});return a.ok?(t({label:"workspace",mark:"ok",detail:o?`compose project ${ue} taken down, with any volumes an earlier compose run left \u2014 codex's login among them`:n==="down"?`compose project ${ue} removed, with its volumes \u2014 Postgres and codex's login`:`stopped \u2014 Postgres kept in the ${ue} volumes`}),!0):(t({label:"workspace",mark:"fail",detail:`docker compose: ${Ve(a.stderr||a.stdout)}`,fix:"start Docker, then run this again"}),!1)}async function Ho(e,t,n,o){let{sys:r}=e;if(t.runner==="image"){let c=await r.exec("docker",Yn(r,...o==="down"?["down","--volumes"]:["stop"]),{timeoutMs:12e4});return n({label:"image",mark:c.ok?"ok":"fail",detail:o==="down"?"Container and data volumes removed":"Container stopped; data kept"}),{ok:c.ok,deployed:t}}let s=!0;t=Sn(r,t);let a={...t};if(t.runner==="compose"&&!await zr(e,n,o)&&(s=!1),t.runner==="compose"&&o==="down"){let{people:c,...l}=a;a=l}let i=[["workspace",t.workspacePid,t.workspaceStarted,gt(t.workspaceUrl)],["console",t.consolePid,t.consoleStarted,gt(t.consoleUrl)]];for(let[c,l,d,u]of i){if(l===void 0){if(c==="workspace"&&t.runner==="compose")continue;n({label:c,mark:"ok",detail:"not running"});continue}let f=()=>{let{[c==="workspace"?"workspacePid":"consolePid"]:w,[c==="workspace"?"workspaceStarted":"consoleStarted"]:b,...g}=a;a=g};if(r.alive(l)&&!At(r,l,d,u)){n({label:c,mark:"ok",detail:`not running \u2014 pid ${l} is another process now, left alone`}),f();continue}let p=r.processOf(l)?.started;if(r.alive(l)&&r.stop(l),await Si(r,l,uu)){n({label:c,mark:"ok",detail:`stopped (pid ${l})`}),f();continue}let m=r.processOf(l);if(p===void 0||m?.started!==p){let w=p!==void 0&&(m!==void 0||!r.alive(l));w?f():s=!1,n(w?{label:c,mark:"ok",detail:`stopped (pid ${l})`}:{label:c,mark:"fail",detail:`pid ${l} is still running`,fix:`stop it (\`kill -9 ${l}\`), then run this again`});continue}r.stop(l,!0),await Si(r,l,pu)?(n({label:c,mark:"ok",detail:`stopped (pid ${l}) \u2014 killed, it did not stop when asked to`}),f()):(s=!1,n({label:c,mark:"fail",detail:`pid ${l} is still running, after SIGTERM and SIGKILL`,fix:`stop it (\`kill -9 ${l}\`), then run this again`}))}return{ok:s,deployed:a}}async function wu(e,t){let n=Ct(e.state);if(!n)return On(t);let o=await Ho(e,n,t,"stop");return o.ok&&t({label:"data",mark:"skip",detail:"kept \u2014 `moa setup` starts it all again"}),{ok:o.ok,state:{...e.state,deployed:o.deployed}}}async function ku(e,t){let n=Ct(e.state);if(!n)return On(t);let{sys:o}=e,r=await Go(o,"cloudflare",n.workspaceUrl);if(!r)return t({label:"container",mark:"fail",detail:`this machine has no token for ${n.workspaceUrl}`,fix:"run `moa setup` to deploy it again"}),{ok:!1};let s=await Vr(o,`${n.workspaceUrl}/_moa/restart`,r,{method:"POST"}).then(async a=>a.ok?await a.json():`${a.status} ${Ve(await a.text().catch(()=>""),1)}`.trim()).catch(a=>_n(a));return typeof s=="string"?(t({label:"container",mark:"fail",detail:`did not stop: ${s}`,fix:"try again"}),{ok:!1}):(t({label:"container",mark:"ok",detail:`${s.restarted?"stopped":"was not running"} \u2014 D1 and R2 kept; the next request to it, or \`moa setup status\`, starts it again`}),{ok:!0})}async function Pi(e,t,n){let o=Tn(e,n),r=o.state.target==="vercel"?{ok:await wi(o,t)}:o.state.target==="cloudflare"?await ku(o,t):await wu(o,t);return Ai(r,e.state.target)}var yu=(e,t)=>e.replace(/\n$/,"").split(`
|
|
208
|
+
`).slice(-t).join(`
|
|
209
|
+
`);async function bu(e,t){let{sys:n}=e;if(e.state.deployed?.runner==="image"){let i=await n.exec("docker",Yn(n,"logs","--no-color","--tail",String(t)),{timeoutMs:3e4});return i.ok?{ok:!0,sections:[{name:"image",from:"docker compose",text:i.stdout}]}:{ok:!1,line:{label:"logs",mark:"fail",detail:"Docker Compose logs failed."}}}let o=Ct(e.state);if(!o)return{ok:!1,line:{label:"fnMoa",mark:"fail",detail:"not deployed here yet",fix:"run `moa setup`"}};let r=i=>{let c=ie(n,`${i}.log`),l=n.read(c);return{name:i,from:c,text:l===void 0?"":yu(l,t)}};if(o.runner!=="compose")return{ok:!0,sections:[r("workspace"),r("console")]};let s=n.root;if(!s)return{ok:!1,line:{label:"workspace",mark:"fail",detail:"not inside a fnMoa checkout: docker compose needs its compose.yaml",fix:"run it from your fnMoa checkout"}};let a=await n.exec("docker",xt(s,"logs","--no-color","--tail",String(t),"workspace"),{cwd:s,env:Jt(n),timeoutMs:6e4});return a.ok?{ok:!0,sections:[{name:"workspace",from:`docker compose -p ${ue} logs workspace`,text:a.stdout},r("console")]}:{ok:!1,line:{label:"workspace",mark:"fail",detail:`docker compose: ${Ve(a.stderr||a.stdout)}`,fix:"start Docker, then run this again"}}}async function $u(e,t){let n=await Do(e,!1);if(nn(n))return{ok:!1,line:{label:n.label,mark:"fail",detail:n.detail,fix:n.fix}};let o=[];for(let r of["workspace","console"]){let s=`$HOME/moa/logs/${r}.log`,a=await _e(n.box,`if [ -f "${s}" ]; then tail -n ${t} "${s}"; fi`);if(!a.ok)return{ok:!1,line:{label:"sandbox",mark:"fail",detail:`its logs could not be read: ${Ve(a.stderr||a.stdout)||"no answer"}`,fix:"a stopped sandbox is started by `moa setup status` \u2014 then run this again"}};o.push({name:r,from:`${n.box.name}:~/moa/logs/${r}.log`,text:a.stdout})}return{ok:!0,sections:o}}async function vu(e,t){let{sys:n}=e,o=Ct(e.state),r=(c,l,d)=>({ok:!1,line:{label:c,mark:"fail",detail:l,fix:d}});if(!o)return r("fnMoa","not deployed here yet","run `moa setup`");let s=await Go(n,"cloudflare",o.workspaceUrl);if(!s)return r("container",`this machine has no token for ${o.workspaceUrl}`,"run `moa setup` to deploy it again");let a=await Vr(n,`${o.workspaceUrl}/_moa/logs?lines=${t}`,s).catch(c=>_n(c));if(typeof a=="string")return r("container",a,"try again");if(a.status===404)return r("container","this deploy keeps no logs to read","`moa setup update` deploys a fnMoa that does");if(!a.ok)return r("container",`${a.status} ${Ve(await a.text())}`.trim(),"try again");let i=await a.json();return i.running?{ok:!0,sections:[{name:"workspace",from:"the container's /home/moa/logs",text:i.workspace??""},{name:"console",from:"the container's /home/moa/logs",text:i.console??""}]}:r("container","not running \u2014 a container's disk, and its logs, go when it stops",`\`moa setup status\` starts it; Cloudflare keeps older output: dashboard \u2192 Workers \u2192 ${Ge} \u2192 Logs`)}function _i(e,t,n){let o=Tn(e,n);return o.state.target==="vercel"?$u(o,t):o.state.target==="cloudflare"?vu(o,t):bu(o,t)}var Su=e=>[...e==="local"?["workspace.db","workspace.db-wal","workspace.db-shm","worktrees","workspace.log","console.log"]:[],...e==="cloudflare"?["cloudflare"]:[],jo("rotate",`${eo(e)}.json`)],Ti="~/.moa/local \u2014 the GitHub Apps' secrets, logs, and what setup kept there";function Oi(e,t){if(t==="all")return[...Gt(e).map(s=>xi(rt(e,s),s,!1)).flat(),Ti];let n=t??e.target??"local",o=Gt(e).filter(r=>r!==n);return xi(rt(e,n),n,o.length===0)}function xi(e,t,n){let o=[n?Ti:`its files in ~/.moa/local${t==="local"?" \u2014 the SQLite database, worktrees and logs":t==="cloudflare"?" \u2014 the Worker's config and its secrets":""}; the rest is the other targets'`,`profile ${eo(t)} in ~/.moa/config.json`];if(t==="vercel")return[`sandbox ${e.answers.sandbox?.name??"the sandbox"} and its disk \u2014 the database is on it`,"its snapshot",...o];if(t==="cloudflare"){let r=Ri(e);if(!r)return["nothing on Cloudflare unless you say yes \u2014 setup recorded nothing it made there; what it finds by its names is asked about one at a time",...o];let s=e.answers.domain;return[...r.worker?[`Worker ${r.worker}${s?`, with ${Ft(s).workspace} and ${Ft(s).console}`:""}`]:[],...r.containerId?[`container ${r.containerId}`]:[],...(r.images??[]).length>0?[`${(r.images??[]).length} container image tag(s) setup pushed`]:[],...(r.unsureImages??[]).length>0?[`${(r.unsureImages??[]).length} container image tag(s) \u2014 asked about: an older moa pushed them, so whether setup did cannot be told`]:[],...r.imagesUnlisted?["any container image tags an older moa pushed \u2014 asked about: the deploy could not list Cloudflare's registry, so it is listed again"]:[],...r.d1?.made?[`D1 database ${r.d1.id} \u2014 the database`]:r.d1?.unsure?[`D1 database ${r.d1.id} \u2014 asked about: an older moa deployed it, so whether setup made it cannot be told`]:[],...r.r2?.made?[`R2 bucket ${r.r2.name} \u2014 codex's login`]:r.r2?.unsure?[`R2 bucket ${r.r2.name} \u2014 asked about: an older moa deployed it, so whether setup made it cannot be told`]:[],...o]}return[(e.deployed?.runner??e.answers.runner)==="compose"?`the compose project ${ue} and its volumes \u2014 the database`:"the workspace and console setup started; the database is in ~/.moa/local",...o]}async function xu(e,t){let{sys:n,state:o}=e,r=o.answers.sandbox,s=[];if(!r)return t({label:"sandbox",mark:"ok",detail:"setup made none"}),{ok:!0,removed:s};let a=await Lo(e);if(nn(a))return t({label:a.label,mark:"fail",detail:a.detail,fix:a.fix}),{ok:!1,removed:s};let i=n.vercel;if(!i)return t({label:"sandbox",mark:"fail",detail:"this build of moa cannot reach Vercel",fix:"update moa"}),{ok:!1,removed:s};try{let c=await i.get(a,r.name,!1);c?(await c.delete(),s.push(`sandbox ${r.name}`),t({label:"sandbox",mark:"ok",detail:`${r.name} deleted, its disk with it`})):t({label:"sandbox",mark:"ok",detail:`${r.name} was already gone`})}catch(c){return t({label:"sandbox",mark:"fail",detail:`${r.name} was not deleted: ${Ve(_n(c))}`,fix:"check `vercel whoami`, then run this again"}),{ok:!1,removed:s}}if(r.snapshotId)try{await i.deleteSnapshot(a,r.snapshotId),s.push(`snapshot ${r.snapshotId}`),t({label:"snapshot",mark:"ok",detail:`${r.snapshotId} deleted`})}catch(c){return t({label:"snapshot",mark:"fail",detail:`${r.snapshotId} was not deleted: ${Ve(_n(c))}`,fix:"run this again"}),{ok:!1,removed:s}}return{ok:!0,removed:s}}var on=/not found|does not exist|couldn't find|could not find|no such|10007\b/i,Ri=e=>e.cloudflare??(e.deployed?.runner==="cloudflare"?e.deployed.cloudflare:void 0);async function Au(e,t,n){let{sys:o,state:r}=e,s=r.answers.cloudflareAccount,a=o.root;if(!s)return{declined:[],why:"setup recorded no Cloudflare account"};if(!a)return{declined:[],why:"not inside a fnMoa checkout: wrangler is run from it"};let i=I=>Ue(o,a,I,{account:s,timeoutMs:6e4}),c=I=>`${I.stdout}
|
|
210
|
+
${I.stderr}`,l=[],d=[],u=(I,X)=>{d.push(`${I}: ${Ve(c(X))||"wrangler did not answer"}`)},f=await i(["deployments","list","--name",Ge,"--json"]);f.ok?l.push({what:`Worker ${Ge}, and its two names`,more:{worker:Ge}}):on.test(c(f))||u("wrangler deployments list",f);let p=await tr(o,a,s);p.ok||d.push(`wrangler containers list: ${p.why}`);let m=p.ok?nr(p.apps):void 0;m&&l.push({what:`container ${m.id} (${Le})`,more:{containerId:m.id}});let w=await Mn(o,a,s);w.ok||d.push(`wrangler containers images list: ${w.why}`);let b=[...new Set([...w.ok?w.images:[],...m?.image?[m.image]:[]])];for(let I=0;I<b.length;I+=Kt){let X=b.slice(I,I+Kt),U=X.map(W=>W.slice(Le.length+1)),H=b.length>Kt?` \u2014 ${I+1} to ${I+X.length} of ${b.length}`:"";l.push({what:`${X.length} image tag${X.length===1?"":"s"} of ${Le} in Cloudflare's registry${H} (${U.join(", ")})`,more:{images:X}})}let g=await i(["d1","list","--json"]),k=Bt(g.stdout);(!g.ok||!Array.isArray(k))&&u("wrangler d1 list",g);let O=(Array.isArray(k)?k:[]).filter(I=>I.name===ct),P=O.length===1?O[0]?.uuid:void 0;P&&l.push({what:`D1 database ${ct} (${P}) \u2014 the workspace's data`,more:{d1:{id:P,made:!0}}});let R=await i(["r2","bucket","info",lt,"--json"]);if(R.ok?l.push({what:`R2 bucket ${lt} \u2014 codex's login`,more:{r2:{name:lt,made:!0}}}):on.test(c(R))||u("wrangler r2 bucket info",R),l.length===0)return{declined:[],why:d.length>0?`wrangler could not look for what setup may have made \u2014 ${d.join("; ")}`:`nothing is found by setup's names on account ${s}`};t({label:"Cloudflare",mark:"skip",detail:`setup recorded nothing it made on account ${s} \u2014 an older moa deployed it. ${l.length} found by setup's names: each is deleted only if you say yes`});let Y={account:s},we=d.map(I=>`not looked for \u2014 ${I}; anything there is left as it was`);d.length>0&&t({label:"Cloudflare",mark:"skip",detail:`wrangler could not look for all of it \u2014 ${d.join("; ")}`});for(let I of l)await n(`Delete ${I.what}? Only if setup made it [y/N] `)?Y={...Y,...I.more,...I.more.images?{images:[...Y.images??[],...I.more.images]}:{}}:we.push(`${I.what}: you said no \u2014 left as it was`);return{made:Y,declined:we}}async function Eu(e,t){let n=[],o=e,r=c=>t(`Delete ${c}? An older moa deployed it, so setup cannot tell \u2014 only if setup made it [y/N] `);if(e.d1?.unsure&&!e.d1.made){let c=`D1 database ${ct} (${e.d1.id}) \u2014 the workspace's data`;await r(c)?o={...o,d1:{id:e.d1.id,made:!0}}:n.push(`${c}: you said no \u2014 left as it was`)}if(e.r2?.unsure&&!e.r2.made){let c=`R2 bucket ${e.r2.name} \u2014 codex's login`;await r(c)?o={...o,r2:{name:e.r2.name,made:!0}}:n.push(`${c}: you said no \u2014 left as it was`)}let s=e.unsureImages??[],a=[],i=[];for(let c=0;c<s.length;c+=Kt){let l=s.slice(c,c+Kt),d=l.map(p=>p.slice(Le.length+1)),u=s.length>Kt?` \u2014 ${c+1} to ${c+l.length} of ${s.length}`:"",f=`${l.length} image tag${l.length===1?"":"s"} of ${Le} in Cloudflare's registry${u} (${d.join(", ")})`;await r(f)?a=[...a,...l]:(i=[...i,...l],n.push(`${f}: you said no \u2014 left as they were`))}if(a.length>0&&(o={...o,images:[...new Set([...o.images??[],...a])],...i.length>0?{unsureImages:i}:{}},i.length===0)){let{unsureImages:c,...l}=o;o=l}return{made:o,declined:n}}async function Cu(e,t,n){let{sys:o,state:r}=e,s=[],a=[],i=Ri(r),c;if(!i&&n){let g=await Au(e,t,n);i=g.made,c=g.why,a.push(...g.declined)}if(!i)return t({label:"Cloudflare",mark:"skip",detail:`setup recorded no deploy there${c?`, and ${c}`:""} \u2014 nothing on Cloudflare was deleted; anything left is in the dashboard`}),r.answers.cloudflareAccount&&a.push(`Cloudflare account ${r.answers.cloudflareAccount}: nothing recorded, nothing deleted`),{ok:!0,removed:s,left:a,unsure:!0};let l=o.root;if(!l)return t({label:"Cloudflare",mark:"fail",detail:"not inside a fnMoa checkout: wrangler is run from it",fix:"run it from your fnMoa checkout"}),{ok:!1,removed:s,left:a};let d=i.account,u=g=>Ue(o,l,g,{account:d,timeoutMs:18e4}),f=g=>`${g.stdout}
|
|
211
|
+
${g.stderr}`,p=(g,k,O)=>(t({label:g,mark:"fail",detail:Ve(f(k))||"no answer",fix:O}),{ok:!1,removed:s,left:a}),m;if(i.imagesUnlisted){let g=await Mn(o,l,d);if(g.ok){let{imagesUnlisted:k,images:O=[],...P}=i,R=[...new Set([...i.unsureImages??[],...g.images.filter(Y=>!O.includes(Y))])];i={...P,...O.length>0?{images:O}:{},...R.length>0?{unsureImages:R}:{}}}else m=g.why}if(n){let g=await Eu(i,n);i=g.made,a.push(...g.declined)}let w=a.length>0||i.d1?.unsure===!0||i.r2?.unsure===!0||(i.unsureImages??[]).length>0||m!==void 0;if(i.worker){let g=await u(["delete",i.worker,"--force"]);if(!g.ok&&!on.test(f(g)))return p("Worker",g,"check `wrangler whoami`, then run this again");g.ok&&s.push(`Worker ${i.worker}`),t({label:"Worker",mark:"ok",detail:g.ok?`${i.worker} deleted, and its two names with it`:`${i.worker} was already gone`})}else t({label:"Worker",mark:"ok",detail:"setup recorded none"});if(i.containerId){let g=await u(["containers","delete",i.containerId]);if(!g.ok&&!on.test(f(g)))return p("container",g,"run this again \u2014 or delete it in the dashboard: Workers \u2192 Containers");g.ok&&s.push(`container ${i.containerId}`),t({label:"container",mark:"ok",detail:g.ok?`${i.containerId} deleted`:`${i.containerId} was already gone`})}else t({label:"container",mark:"ok",detail:"setup recorded none"});let b=0;for(let g of i.images??[]){let k=await u(["containers","images","delete",g,"-y"]);if(!k.ok&&!on.test(f(k)))return p("images",k,"run this again");k.ok&&(b+=1)}if(b>0&&s.push(`${b} container image${b===1?"":"s"}`),t({label:"images",mark:"ok",detail:(i.images??[]).length===0?"setup recorded none":b>0?`${b} setup pushed deleted from Cloudflare's registry`:"the ones setup pushed were already gone"}),(i.unsureImages??[]).length>0){let g=`${(i.unsureImages??[]).length} image tag(s) of ${Le} \u2014 setup cannot tell whether it pushed them; left as they were`;n||a.push(g),t({label:"images",mark:"skip",detail:g})}if(m!==void 0){let g=`any image tags of ${Le} an older moa pushed \u2014 Cloudflare's registry could not be listed; left as they were`;a.push(g),t({label:"images",mark:"skip",detail:`${g}: ${m} \u2014 check the dashboard: Workers \u2192 Containers \u2192 Images`})}if(i.d1?.made){let g=i.d1.id,k=await u(["d1","list","--json"]),O=Bt(k.stdout);if(!k.ok||!Array.isArray(O))return p("D1",k,"run this again");let P=O.find(R=>R.uuid===g);if(!P?.name)t({label:"D1",mark:"ok",detail:`${g} was already gone`});else{let R=await u(["d1","delete",P.name,"-y"]);if(!R.ok&&!on.test(f(R)))return p("D1",R,"run this again");R.ok&&s.push(`D1 ${P.name} (${g})`),t({label:"D1",mark:"ok",detail:R.ok?`${P.name} (${g}) deleted`:`${g} was already gone`})}}else i.d1?.unsure?(n||a.push(`D1 ${i.d1.id} \u2014 setup cannot tell whether it made it; left as it was`),t({label:"D1",mark:"skip",detail:`${i.d1.id} \u2014 setup cannot tell whether it made it; left as it was`})):i.d1&&(a.push(`D1 ${i.d1.id} \u2014 it was there before setup; left as it was`),t({label:"D1",mark:"skip",detail:`${i.d1.id} was there before setup used it \u2014 left as it was`}));if(i.r2&&await u(["r2","object","delete",`${i.r2.name}/${du}`,"--remote"]),i.r2?.made){let g=await u(["r2","bucket","delete",i.r2.name]);if(!g.ok&&!on.test(f(g)))return p("R2",g,`empty ${i.r2.name} in the dashboard (R2 \u2192 ${i.r2.name}), then run this again`);g.ok&&s.push(`R2 ${i.r2.name}`),t({label:"R2",mark:"ok",detail:g.ok?`${i.r2.name} deleted`:`${i.r2.name} was already gone`})}else i.r2?.unsure?(n||a.push(`R2 ${i.r2.name} \u2014 setup cannot tell whether it made it; left as it was`),t({label:"R2",mark:"skip",detail:`${i.r2.name} \u2014 setup cannot tell whether it made it; codex's login taken out, the bucket left`})):i.r2&&(a.push(`R2 ${i.r2.name} \u2014 it was there before setup; left as it was`),t({label:"R2",mark:"skip",detail:`${i.r2.name} was there before setup used it \u2014 codex's login taken out, the bucket left`}));return{ok:!0,removed:s,left:a,unsure:w}}async function Ii(e,t,n=new Date,o,r){let s=r??e.state.target;if(!s)return t({label:"fnMoa",mark:"ok",detail:"moa setup has made nothing to take down"}),{ok:!0};let a=e.state.target,i=U=>Ai(U,a),{sys:c,state:l}=Tn(e,s),d=Gt(l).filter(U=>U!==s),u=d.length===0,f=[],p=[],m=!1;if(s==="vercel"||s==="cloudflare"){let U=s==="vercel"?{...await xu({...e,state:l},t),left:[],unsure:!1}:await Cu({...e,state:l},t,o);if(f.push(...U.removed),p.push(...U.left),m=U.unsure===!0,!U.ok)return{ok:!1}}else{let U=Ct(l),H=!1,W={ok:!1};if(U){let de=await Ho({...e,state:l},U,t,"down");if(W=i({ok:!1,state:{...l,deployed:de.deployed}}),!de.ok)return W;H=U.runner==="compose",f.push(H?`compose project ${ue} and its volumes`:"the workspace and console setup started")}else if(l.answers.runner==="compose"){if(!await zr({...e,state:l},t,"down"))return{ok:!1};H=!0,f.push(`compose project ${ue} and its volumes`)}if(!H){let de=c.root?await c.exec("docker",["info","--format","{{.ServerVersion}}"]):void 0;if(de?.ok){if(!await zr({...e,state:l},t,"down",!0))return W;f.push(`compose project ${ue}, if an earlier run left one`)}else de&&!de.missing&&(p.push(`any ${ue} volumes an earlier compose run left \u2014 Docker was not running`),t({label:"compose",mark:"skip",detail:`Docker is not running \u2014 any ${ue} volumes an earlier compose run left are still there: start it, then \`docker compose -p ${ue} down --volumes\``}))}}let w=await Pe(c.env),b=eo(s);if(w.profiles[b]){let{[b]:U,...H}=w.profiles,W=w.current===b?Object.keys(H)[0]??"default":w.current;await it({current:W,profiles:H},c.env),f.push(`profile ${b}`),t({label:"owner token",mark:"ok",detail:`profile ${b} removed from ~/.moa/config.json${w.current===b&&H[W]?`; \`moa\` now uses profile ${W}`:""}`})}if(u)await vi(jo(ot(c.env),"..","local"),{recursive:!0,force:!0}),f.push("~/.moa/local"),t({label:"this machine",mark:"ok",detail:"~/.moa/local removed"});else{for(let U of Su(s))await vi(ie(c,U),{recursive:!0,force:!0});f.push("this target's files in ~/.moa/local"),t({label:"this machine",mark:"ok",detail:`${Pn[s]}'s files in ~/.moa/local removed; the rest kept \u2014 ${d.map(U=>Pn[U]).join(" and ")} still ${d.length===1?"uses":"use"} it`})}let g=l.answers.githubApp;g&&(u||await Gr(c,g),p.push(`GitHub App ${g.slug} \u2014 ${g.settingsUrl}/advanced`),t({label:"GitHub App",mark:"skip",detail:`${g.slug} is yours to delete: ${g.settingsUrl}/advanced \u2192 Delete GitHub App (it costs nothing)${u?"":"; its client secret is gone from this machine"}`}));let k=d.map(U=>`${Pn[U]} \u2014 \`moa setup down --target ${U}\``).join("; ");t(m?{label:"billing",mark:"skip",detail:`setup cannot tell all it made on Cloudflare is gone \u2014 check the dashboard for anything billed${u?"":`; still deployed: ${k}`}`}:u?{label:"billing",mark:"ok",detail:"nothing setup made is left running or stored"}:{label:"billing",mark:"ok",detail:`nothing ${Pn[s]} had is left running or stored; still deployed: ${k}`});let O={target:s,at:n.toISOString(),removed:f,...p.length>0?{left:p}:{}};if(u)return{ok:!0,state:{...po(),down:O}};let{deployed:P,cloudflare:R,...Y}=l,{sandbox:we,githubApp:I,...X}=l.answers;return i({ok:!0,state:{...Y,answers:X,...a===s?{screen:"where",passed:Y.passed.filter(U=>U!=="deploy")}:{},down:O}})}async function Pu(e,t,n){let o=(...u)=>e.exec("git",["-C",t,...u],{timeoutMs:12e4}),r=async()=>(await o("rev-parse","HEAD")).stdout.trim().slice(0,7),s=await r(),a=await o("status","--porcelain","--untracked-files=no"),i=await o("rev-parse","--abbrev-ref","--symbolic-full-name","@{u}");if(!a.ok||a.stdout.trim()!=="")return n({label:"fnMoa",mark:"skip",detail:`${t} has changes of its own \u2014 deploying it as it is, at ${s||"its commit"}`}),!0;if(!i.ok||!i.stdout.trim())return n({label:"fnMoa",mark:"skip",detail:`no upstream branch to pull \u2014 deploying the checkout as it is, at ${s}`}),!0;let c=i.stdout.trim(),l=await o("pull","--ff-only");if(!l.ok)return n({label:"fnMoa",mark:"fail",detail:`git pull: ${Ve(l.stderr||l.stdout)}`,fix:`bring ${t} up to date yourself, then run \`moa setup update\` again`}),!1;let d=await r();return n({label:"fnMoa",mark:"ok",detail:s===d?`${d} \u2014 the newest on ${c}`:`${s} \u2192 ${d}, from ${c}`}),!0}async function _u(e,t,n,o){let r=jo(t,"apps","console",".next");if(e.exists(r)&&!e.exists(jo(r,"BUILD_ID")))return o({label:"build",mark:"fail",detail:"apps/console/.next holds no build \u2014 `next dev` may be using it",fix:"stop the console's dev server, then run `moa setup update` again"}),!1;let s=[...n?[]:[["--filter","@fnmoa/workspace...","run","build"]],["--filter","@fnmoa/console","run","build"]];o({label:"build",mark:"wait",detail:"building the new fnMoa \u2014 a few minutes"});for(let a of[["install"],...s]){let i=await e.exec("pnpm",a,{cwd:t,timeoutMs:9e5});if(!i.ok)return o({label:"build",mark:"fail",detail:`pnpm ${a.join(" ")}: ${Ve(i.stderr||i.stdout)}`,fix:`run \`pnpm ${a.join(" ")}\` in ${t}, then \`moa setup update\` again`}),!1}return o({label:"build",mark:"ok",detail:n?"console built":"workspace and console built"}),!0}async function Fo(e,t,n){e=Tn(e,n);let{sys:o,state:r}=e,s=Ct(r);if(!s)return On(t);if(s.runner==="image"||!o.root){let c=await ut(e);return c.forEach(t),c.some(l=>l.mark==="fail")?{ok:!1}:{ok:!0,state:{...r,screen:"deploy"}}}let a=o.root;if(!a)return t({label:"checkout",mark:"fail",detail:"not inside a fnMoa checkout: the new fnMoa is built from one",fix:"run it from your fnMoa checkout"}),{ok:!1};if(!await Pu(o,a,t))return{ok:!1};let i=s;if(r.target==="local"){let c=await Ho(e,s,t,"stop");if(i=c.deployed,!c.ok)return{ok:!1,state:{...r,deployed:i}};if(!await _u(o,a,s.runner==="compose",t))return{ok:!1,state:{...r,deployed:i}};t({label:"data",mark:"skip",detail:s.runner==="compose"?`kept in the ${ue} volumes \u2014 Deploy builds the image and starts it all again`:"kept in ~/.moa/local \u2014 Deploy starts it all again"})}else t({label:"data",mark:"skip",detail:r.target==="vercel"?"kept on the sandbox's disk \u2014 Deploy builds its image at this commit and restarts it":"kept in D1 and R2 \u2014 Deploy builds the Worker and the container image at this commit"});return{ok:!0,state:{...r,screen:"deploy",deployed:i}}}async function Li(e,t,n,o){e=Tn(e,o);let{sys:r,state:s}=e,a=Ct(s),i=s.target;if(!a||!i)return On(t);let c={},l=s.answers.githubApp,d=l?Me(r,l):void 0,u=l&&d?(await n(`GitHub App ${l.slug}: on ${l.settingsUrl} press "Generate a new client secret" and paste it (Enter keeps the current one): `)).trim():"",f;if(i!=="local"&&(f=(await n(`New GitHub token for the ${i==="vercel"?"sandbox":"container"} \u2014 fine-grained: contents, pull requests, issues read/write; checks, actions read (Enter keeps the current one): `)).trim(),f)){let g=await r.fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${f}`,Accept:"application/vnd.github+json"},signal:AbortSignal.timeout(15e3)}).then(async k=>k.ok?(await k.json()).login:void 0).catch(()=>{});if(!g)return t({label:"GitHub token",mark:"fail",detail:"GitHub refuses it",fix:"make another, then run `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};c.github=f,t({label:"GitHub token",mark:"ok",detail:`${K} \u2014 ${g}'s, new`})}let p=a.memoryKept===!0||ke(e)!==void 0,m=i==="vercel"||ke(e)!==void 0;if(p){let g=(await n(m?"New LocusGraph agent secret (Enter keeps the current one): ":"LocusGraph agent secret \u2014 this machine has no copy, and Deploy needs one: paste the current one or a new one: ")).trim();if(!g&&!m)return t({label:"LocusGraph",mark:"fail",detail:"the workspace keeps its memory there, and this machine has no copy of the secret \u2014 Deploy would turn memory off",fix:"run `moa setup rotate` again and paste the secret, the current one or a new one \u2014 nothing was changed"}),{ok:!1};if(g){let k=ke({...e,secrets:{locusgraph:g}}),O=s.answers.tenant??xe(s.answers.owner),P=k?await gn(r,k,O):void 0;if(!P?.read.ok||!P.write.ok)return t({label:"LocusGraph",mark:"fail",detail:`the new secret does not work: ${P?.read.why??P?.write.why??"no answer"}`,fix:"check it, then run `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};c.locusgraph=g,t({label:"LocusGraph",mark:"ok",detail:`${K} \u2014 new; it reads and writes ${k?.graph}`})}}let w=eo(i);await Ua(r,w,a.workspaceUrl,kr()),t({label:"owner token",mark:"ok",detail:`${K} \u2014 new; Deploy gives it to the workspace, and profile ${w} takes it once the workspace answers to it`}),l&&d?(await jr(r,l,{clientSecret:u||d.clientSecret,authSecret:lu(32).toString("base64url")}),t({label:"GitHub App",mark:"ok",detail:u?`new client secret and AUTH_SECRET \u2014 once Deploy is done, delete the old secret on ${l.settingsUrl}`:"new AUTH_SECRET; client secret kept \u2014 everyone signs in again"})):t({label:"GitHub App",mark:"skip",detail:l?"its secret is not on this machine \u2014 Console access, g, makes a new app":"none \u2014 the console stays on this machine"}),i==="local"?t({label:"GitHub token",mark:"skip",detail:"this machine's own gh login \u2014 `gh auth refresh` renews it"}):f||t({label:"GitHub token",mark:"skip",detail:"kept"}),p?c.locusgraph||t({label:"LocusGraph",mark:"skip",detail:"kept"}):t({label:"LocusGraph",mark:"skip",detail:"not used \u2014 memory stays in the workspace"});let b=a;if(i==="local"){let g=await Ho(e,a,t,"stop");if(b=g.deployed,!g.ok)return{ok:!1,state:{...s,screen:"deploy",deployed:b}}}return{ok:!0,secrets:c,state:{...s,screen:"deploy",answers:{...s.answers,...c.github?{githubToken:"pasted"}:{}},deployed:b}}}St();Ze();Vn();qe();Qt();function Tu(e){let{state:t,sys:n}=e;if(!t.target||!t.deployed||!t.passed.includes("deploy"))return"finish the initial deployment with `moa setup`";if(!t.answers.owner)return"the GitHub owner (run `moa setup`)";if(Zt(e))return"the LocusGraph secret (set LOCUSGRAPH_AGENT_SECRET)";if(t.answers.githubApp&&!Me(n,t.answers.githubApp))return"the GitHub App client secret (run `moa setup`)";if(t.target==="cloudflare"&&!t.answers.domain)return"the Cloudflare domain (run `moa setup`)"}async function Ui(e,t,n){let o=Tu(e);if(o)return t({label:"update",mark:"fail",detail:`Update needs ${o}`}),!1;let r=await Fo(e,t);if(r.state&&await n(r.state),!r.ok||!r.state)return!1;let s=r.state,a=Promise.resolve(),i=u=>{s=u,a=a.then(()=>n(u))},c=new Map,l=u=>{for(let f of u){let p=JSON.stringify(f);c.get(f.label)!==p&&t(f),c.set(f.label,p)}},d=s.target==="vercel"?Uo:s.target==="cloudflare"?_o:So;try{let u=await d({...e,state:s},l,f=>i({...s,deployed:f}),f=>i({...s,cloudflare:f}));return u.deployed&&i({...s,deployed:u.deployed,...u.ok?{answers:{...s.answers,removed:void 0}}:{}}),u.ok}finally{await a}}var Uu=["status","login","licence","rotate","update","logs","stop","down"],Du={ok:"\u2713",fail:"\u2717",wait:"\u2026",skip:"\u2013"},rn=e=>{process.stdout.write(`${Du[e.mark]} ${e.label.padEnd(12)} ${e.detail??""}
|
|
212
|
+
${e.mark==="fail"&&e.fix?` ${"".padEnd(12)} \u2192 ${e.fix}
|
|
213
|
+
`:""}`)};async function Yr(e){let t=Xr({input:process.stdin,output:process.stdout});try{return await t.question(e)}finally{t.close()}}async function Di(e){process.stdout.write(e);let t=new Ru({write:(o,r,s)=>s()}),n=Xr({input:process.stdin,output:t,terminal:!0});try{return await n.question("")}finally{n.close(),process.stdout.write(`
|
|
214
|
+
`)}}var Mu=e=>{let t=x(e,"lines");if(t===void 0)return 100;let n=Number(t);if(!Number.isInteger(n)||n<=0||n>2e3)throw new $("--lines must be a whole number, 1\u20132000");return n},Mi=e=>vt.includes(e);async function to(e,t,n){let o=e==="status"||e==="down",r=x(n,"target");if(r!==void 0){if(Mi(r)||o&&r==="all")return r;throw new $(`--target must be one of ${vt.join(", ")}${o?" or all":""}, not ${r}`)}let s=Gt(t);if(s.length<=1)return s[0];let a=`${s.join(", ")}${o?" or all":""}`;if(S(n,"yes")||!process.stdin.isTTY)throw new $(`moa setup has ${s.length} deployments on record, ${s.join(" and ")}: say which with --target ${s.join("|")}${o?"|all":""}`);for(process.stdout.write(`moa setup has ${s.length} deployments on record: ${s.map(i=>`--target ${i}`).join(", ")}
|
|
215
|
+
`);;){let i=(await Yr(`Which one? ${a}: `)).trim().toLowerCase();if(Mi(i)&&s.includes(i)||o&&i==="all")return i;if(i==="")throw new $(`moa setup ${e} needs a target: ${a}`);process.stdout.write(`One of ${a}.
|
|
216
|
+
`)}}async function Nu(e,t,n){let o=await Kn(n),r={state:o,sys:kn(n),secrets:{}},s=await to(e,o,t);if(e==="logs"){let c=await _i(r,Mu(t),s==="all"?void 0:s);if(!c.ok)return rn(c.line),y.failed;for(let l of c.sections)process.stdout.write(`\u2500\u2500 ${l.name} \xB7 ${l.from}
|
|
217
|
+
`),process.stdout.write(l.text.trim()?`${l.text.replace(/\n?$/,`
|
|
218
|
+
`)}`:`(nothing yet)
|
|
219
|
+
`);return y.ok}let a;if(e==="down"&&s&&!S(t,"yes")){if(!process.stdin.isTTY)throw new $("moa setup down asks first, and needs a terminal to ask in \u2014 or --yes");let c=s==="all"?[]:Gt(o).filter(l=>l!==s);if(process.stdout.write(`moa setup down deletes, for good:
|
|
220
|
+
${Oi(o,s).map(l=>` \xB7 ${l}
|
|
221
|
+
`).join("")}${c.length>0?`and leaves ${c.join(" and ")} deployed, with ${c.length===1?"its":"their"} profile and record.
|
|
222
|
+
`:""}`),(await Yr("Type down to delete it all: ")).trim()!=="down")return process.stdout.write(`Nothing was deleted.
|
|
223
|
+
`),y.ok;a=async l=>/^y(es)?$/i.test((await Yr(l)).trim())}if(e==="status")return(await Ci(r,rn,s)).ok?y.ok:y.failed;if(e==="stop"){let c=await Pi(r,rn,s==="all"?void 0:s);return c.state&&await Lt(c.state,n),c.ok?y.ok:y.failed}let i=o;for(let c of s==="all"?Gt(o):[s]){s==="all"&&process.stdout.write(`\u2500\u2500 ${c}
|
|
224
|
+
`);let l=await Ii({...r,state:i},rn,new Date,a,c);if(l.state&&(i=l.state,await Lt(i,n)),!l.ok)return y.failed}return y.ok}var Ni=(e,t)=>{let n=x(e,t);if(n===void 0)return;let o=Number(n);if(!Number.isInteger(o)||o<=0||o>=65536)throw new $(`--${t} must be a port, 1\u201365535`);return o};async function ju(){process.stdout.write(`
|
|
225
|
+
moa setup: handing the terminal to \`claude setup-token\`.
|
|
226
|
+
|
|
227
|
+
`);let e=Ou("claude",["setup-token"],{stdio:"inherit"}),t=Xr({input:process.stdin,output:process.stdout});await t.question(e.error?`
|
|
228
|
+
claude setup-token did not start: ${e.error.message}. Enter to go back. `:`
|
|
229
|
+
Copy the token it printed, then press Enter: the screen is cleared and setup asks for it. `),t.close(),process.stdout.write("\x1B[2J\x1B[3J\x1B[H")}async function ji(e,t=process.env){let n=e.positional[1];if(n!==void 0&&!Uu.includes(n))throw new $(`moa setup has no ${n}`);if(n==="licence"){let m=await Kn(t),w=await to(n,m,e),g={state:rt(m,w==="all"?void 0:w),sys:kn(t),secrets:{}};if(t.FNMOA_LICENCE_KEY||S(e,"key")){let k=t.FNMOA_LICENCE_KEY??(process.stdin.isTTY?await Di("Licence key: "):"");if(!k.trim())throw new $("Licence activation needs a key: set FNMOA_LICENCE_KEY or run in a terminal.");await gr(g,k.trim())}else await go(g,k=>process.stdout.write(`Open ${k.url}
|
|
230
|
+
Sign in, check the page shows ${k.code}, and approve it. Waiting\u2026
|
|
231
|
+
`),new AbortController().signal);return process.stdout.write("Licence activated. Run `moa setup update` to apply it to the workspace.\n"),y.ok}if(n==="status"||n==="stop"||n==="logs"||n==="down")return Nu(n,e,t);if(!process.stdin.isTTY){if(n==="update"){let m=await Kn(t),w=await to(n,m,e),b=rt(m,w==="all"?void 0:w);return await Ui({state:b,sys:kn(t),secrets:{}},rn,k=>Lt(k,t))?y.ok:y.failed}throw new $("moa setup asks questions, and needs a terminal to ask them in")}let o={},r=await Kn(t);if(n==="login"){let m=await to(n,r,e);m&&m!=="all"&&m!==r.target&&(r=rt(r,m),await Lt(r,t))}if(n==="update"||n==="rotate"){let m={state:r,sys:kn(t),secrets:o},w=await to(n,r,e),b=w==="all"?void 0:w,g=n==="update"?await Fo(m,rn,b):await Li(m,rn,Di,b);if(g.state&&await Lt(g.state,t),!g.ok||!g.state)return y.failed;r=g.state,o=g.secrets??{},process.stdout.write(`
|
|
232
|
+
Deploy runs next: Enter there hands it all to fnMoa.
|
|
233
|
+
`)}let s=Ni(e,"workspace-port"),a=Ni(e,"console-port"),{down:i,...c}=r,l={...c,answers:{...r.answers,...s?{workspacePort:s}:{},...a?{consolePort:a}:{}}};(s||a)&&await Lt(l,t);let d={};n==="login"&&(l.screen="logins",l.passed.includes("deploy")&&(d={logins:"deploy"}));let u=kn(t),f,p=!1;for(;;){let m;if(await Iu(Lu($i,{initial:l,sys:u,secrets:o,pasteFirst:p,after:d,save:b=>Lt(b,t),onDone:(b,g)=>{f={state:b,finished:g}},onHandOver:b=>{m=b}})).waitUntilExit(),!m)break;await ju(),l=m,p=!0}return f&&!f.finished&&process.stdout.write(`Saved. \`moa setup\` picks up at ${Bn[f.state.screen]}.
|
|
234
|
+
`),y.ok}var Gi=`moa \u2014 talk to a workspace
|
|
235
|
+
|
|
236
|
+
moa setup set fnMoa up \u2014 this machine, Vercel or Cloudflare \u2014
|
|
237
|
+
step by step; resumes where it stopped
|
|
238
|
+
[--workspace-port n] [--console-port n]
|
|
239
|
+
moa setup status each piece \u2713/\u2717, live; resumes a stopped sandbox
|
|
240
|
+
moa setup login add or replace the agent logins (owner only)
|
|
241
|
+
moa setup rotate new workspace, GitHub, LocusGraph and GitHub App secrets
|
|
242
|
+
moa setup update deploy a newer fnMoa, data kept
|
|
243
|
+
moa setup licence connect your fnMoa account in the browser for a licence
|
|
244
|
+
[--key: paste a licence key; or FNMOA_LICENCE_KEY]
|
|
245
|
+
moa setup logs workspace and console logs [--lines n]
|
|
246
|
+
moa setup stop stop it (on Vercel the disk is kept)
|
|
247
|
+
moa setup down delete everything setup made \u2014 asks first [--yes]
|
|
248
|
+
moa login <url> --token <token> save a workspace and check the token
|
|
249
|
+
moa login <url> --github sign in with GitHub, for a token of your own
|
|
250
|
+
moa whoami which workspace, which tenant
|
|
251
|
+
moa health [url] is it up
|
|
252
|
+
moa doctor can it do what you are about to ask
|
|
253
|
+
moa profiles the workspaces you have saved
|
|
254
|
+
moa use <profile> work against a different one from now on
|
|
255
|
+
moa logout [profile] forget a workspace, and its token
|
|
256
|
+
moa seats what this tenant can run on, and what it spent
|
|
257
|
+
moa spend [--since 24h] what it cost, and what asked for it
|
|
258
|
+
|
|
259
|
+
moa agent create <name> --cli <cli> [--role "\u2026"] [--peers a,b]
|
|
260
|
+
moa agent list who exists
|
|
261
|
+
moa agent show <name> role, peers and what an owner did
|
|
262
|
+
moa agent role <name> "\u2026" change what it is for
|
|
263
|
+
moa agent grant <from> <to> let one open a thread with another
|
|
264
|
+
moa agent revoke <from> <to> take that back
|
|
265
|
+
moa agent rm <name> release its workspace
|
|
266
|
+
moa peers [--all] who may open a thread with whom
|
|
267
|
+
|
|
268
|
+
moa run <name> "<prompt>" give it work, and wait
|
|
269
|
+
moa runs [name] [--state w] what it \u2014 or everyone \u2014 has run, and why
|
|
270
|
+
running \xB7 done \xB7 failed \xB7 interrupted
|
|
271
|
+
--before <run> the page before that one
|
|
272
|
+
moa watch every job, as it changes
|
|
273
|
+
moa watch <name> what one agent is doing right now
|
|
274
|
+
moa mcp <name> [--file <path>] what it can reach mid-run, and a config
|
|
275
|
+
moa mcp <name> --revoke take back every token it has out
|
|
276
|
+
moa logs <runId> [--tail n] what a run did
|
|
277
|
+
[--watch] follow one that is still going
|
|
278
|
+
[--since seq] only what came after that line
|
|
279
|
+
moa ask <from> <to> "<q>" one agent asks another, and waits
|
|
280
|
+
|
|
281
|
+
moa send <from> <a,b,c> "<body>" a message, along edges the owner granted
|
|
282
|
+
moa inbox who is waiting on an answer
|
|
283
|
+
moa inbox <name> what an agent has not read yet
|
|
284
|
+
moa mail <name> [--limit n] everything it said or was sent
|
|
285
|
+
moa delegate <from> <to> "<task>" hand work over, and own what comes back
|
|
286
|
+
moa delegations [--blocked] what is open, and what is stuck
|
|
287
|
+
moa thread <thread> one conversation, in order
|
|
288
|
+
moa reply <thread> <from> --kind <question|answer|finding|done> "<body>"
|
|
289
|
+
|
|
290
|
+
moa team work <a,b,c> run whoever has mail, until nobody does
|
|
291
|
+
[--nudge "\u2026"] [--route-replies]
|
|
292
|
+
moa ship <issue> [--watch] issue \u2192 pull request \u2192 merge
|
|
293
|
+
moa jobs what this workspace is doing
|
|
294
|
+
moa stop <jobId> stop it after its current step
|
|
295
|
+
|
|
296
|
+
moa manager say "<text>" tell the Manager something, and wait for its answer
|
|
297
|
+
moa manager proposals what it proposes, and is waiting on a yes for
|
|
298
|
+
moa manager approve <id> say yes, and run it now
|
|
299
|
+
moa manager dismiss <id> say no
|
|
300
|
+
moa manager watch what is running, and how it is going
|
|
301
|
+
moa completion <bash|zsh> a completion script, for eval
|
|
302
|
+
|
|
303
|
+
Flags
|
|
304
|
+
--watch print what the agent does, as it does it; on ask, what
|
|
305
|
+
the agent being asked is doing; on ship, the step the
|
|
306
|
+
pipeline is on and what it has spent
|
|
307
|
+
--detach start the work and do not wait
|
|
308
|
+
--timeout <s> give up waiting after this long (default 3600)
|
|
309
|
+
--nudge "\u2026" what each agent is told at the top of its turn
|
|
310
|
+
--route-replies post a reply that starts with @name onto the bus, even
|
|
311
|
+
where the agents can send their own mail
|
|
312
|
+
--key <word> name this ask: running it again with the same key is the
|
|
313
|
+
same run, not a second one
|
|
314
|
+
--profile <name> use a saved workspace other than the current one
|
|
315
|
+
--json machine-readable output, and nothing else
|
|
316
|
+
--version which moa this is, and its licence
|
|
317
|
+
--help this
|
|
318
|
+
|
|
319
|
+
Environment
|
|
320
|
+
MOA_URL, MOA_TOKEN override the saved profile entirely
|
|
321
|
+
`,Bu="Proprietary. Copyright (c) 2026 fnLog0. See LICENSE.";function Ku(){let e=JSON.parse(Gu(new URL("../package.json",import.meta.url),"utf8"));return`moa ${typeof e.version=="string"?e.version:"unknown"}
|
|
322
|
+
${Bu}
|
|
323
|
+
`}var Ju=["profile","json","help"],Wu={login:["token","github"],whoami:[],health:["url"],doctor:[],profiles:[],use:[],logout:[],seats:[],spend:["since"],agent:["cli","role","peers"],peers:["all"],run:["watch","detach","timeout","key"],runs:["limit","state","failed","before"],watch:["every"],logs:["tail","since","watch","every"],ask:["watch","detach","timeout","key"],mcp:["file","revoke"],send:["thread"],inbox:[],mail:["limit"],delegate:["parent"],delegations:["blocked"],thread:[],reply:["kind"],team:["watch","timeout","nudge","route-replies","max-cost","max-turns","key"],ship:["watch","timeout","repo","max-cost","max-steps","key"],jobs:[],stop:[],manager:["timeout"],completion:[],setup:["workspace-port","console-port","lines","yes"]};function qu(e,t){let n=(r,s)=>{if(r.length!==s.length)return!1;let a=[...r].map((l,d)=>d).filter(l=>r[l]!==s[l]),[i,c]=a;return a.length===2&&i!==void 0&&c!==void 0&&r[i]===s[c]&&r[c]===s[i]},o=(r,s)=>{if(r===s||n(r,s))return!0;if(r.startsWith(s)||s.startsWith(r))return Math.abs(r.length-s.length)<=2;if(Math.abs(r.length-s.length)>1)return!1;let a=0,i=0,c=0;for(;a<r.length&&i<s.length;){if(r[a]===s[i]){a+=1,i+=1;continue}if(++c>1)return!1;r.length>s.length?a+=1:(s.length>r.length||(a+=1),i+=1)}return c+(r.length-a)+(s.length-i)<=1};return t.find(r=>o(e,r))}function zu(e,t){let n=Wu[e];if(n===void 0)return;let o=[...n,...Ju],r=[...t.flags.keys()].filter(i=>!o.includes(i));if(r.length===0)return;let s=r[0],a=qu(s,o);throw new $(`moa ${e} has no --${s}${a===void 0?"":` \u2014 did you mean --${a}?`}`)}var Hi={login:cs,whoami:ls,health:ds,doctor:us,profiles:fs,use:gs,logout:ms,seats:ps,spend:hs,agent:ks,peers:ys,run:Ds,runs:Hs,watch:Fs,logs:js,ask:Gs,mcp:Ts,send:$s,inbox:vs,mail:Ss,delegate:xs,delegations:As,thread:Es,reply:Cs,team:Os,ship:Rs,jobs:Is,stop:Ls,manager:_s,setup:ji};async function Vu(e,t=process.env){let n=es(e),o=n.positional[0];if(!o&&S(n,"version"))return process.stdout.write(Ku()),y.ok;if(!o||S(n,"help")||o==="help")return process.stdout.write(Gi),o?y.ok:y.misuse;if(o==="completion")return process.stdout.write(Bs(n.positional[1]??"",Object.keys(Hi))),y.ok;let r=Hi[o];if(!r)throw new $(`unknown command ${o}
|
|
324
|
+
|
|
325
|
+
${Gi}`);return zu(o,n),r(n,t)}var Yu=()=>{let e=process.argv[1];if(!e)return!1;try{return Hu(e)===Fu(import.meta.url)}catch{return!1}};if(Yu()){for(let e of[process.stdout,process.stderr])e.on("error",t=>{throw t.code==="EPIPE"&&process.exit(0),t});try{process.exitCode=await Vu(process.argv.slice(2))}catch(e){let{message:t,code:n}=ss(e);process.stderr.write(`moa: ${t}
|
|
326
|
+
`),process.exitCode=n}}export{Hi as COMMANDS,Wu as FLAGS,Bu as NOTICE,zu as checkFlags,qu as nearest,Vu as run,Ku as versionText};
|