@fnmoa/cli 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +78 -78
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
3
|
-
var
|
|
4
|
-
`,{mode:384})}async function
|
|
5
|
-
${o.stderr}`;return/already exists|already own/i.test(r)?{ok:!0,made:!1}:o.ok?{ok:!0,made:!0}:{ok:!1,why:
|
|
6
|
-
`).filter(t=>t.trim()!=="").slice(-2).join(" \xB7 ").slice(0,240)});function
|
|
3
|
+
var _c=Object.defineProperty;var De=(e,t)=>()=>(e&&(t=e(e=0)),t);var Oc=(e,t)=>{for(var n in t)_c(e,n,{get:t[n],enumerable:!0})};function U(e,t=fetch){let n=e.url.replace(/\/+$/,""),o=async(s,a,c,i)=>{let l=async()=>t(`${n}${a}`,{method:s,headers:{authorization:`Bearer ${e.token}`,...c===void 0?{}:{"content-type":"application/json"},...i===void 0?{}:{"idempotency-key":i}},...c===void 0?{}:{body:JSON.stringify(c)}}),d;try{d=await l()}catch(u){if(i!==void 0)try{d=await l()}catch{throw Es(n,u)}else throw Es(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 ue(d.status,f||`${s} ${a} failed`,p)}return d},r=async(s,a,c,i)=>{let l=await o(s,a,c,i);return l.status===204?void 0:await l.json()};return{url:n,get:s=>r("GET",s),post:(s,a,c)=>r("POST",s,a,c),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,c,i){let l=await t(s,{method:"POST",headers:{authorization:`Bearer ${a}`,"content-type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:c,...i?{params:i}:{}})}),d=await l.json().catch(()=>({}));if(!l.ok||d.error)throw new ue(l.status,d.error?.message??`the server said ${l.status}`);return d.result}}}var ue,Tt,Es,je=De(()=>{"use strict";ue=class extends Error{status;requestId;constructor(t,n,o){super(n),this.name="ApiError",this.status=t,o&&(this.requestId=o)}},Tt=class extends Error{constructor(t,n){super(`cannot reach ${t}: ${n instanceof Error?n.message:String(n)}`),this.name="Unreachable"}},Es=(e,t)=>new Tt(e,t)});import{mkdir as Tc,readFile as Ic,writeFile as Rc}from"node:fs/promises";import{homedir as Lc}from"node:os";import{dirname as Dc,join as Cs}from"node:path";async function Oe(e){try{let t=JSON.parse(await Ic(_s(e),"utf8"));return{current:t.current??"default",profiles:t.profiles??{}}}catch{return Uc()}}async function wt(e,t){let n=_s(t);await Tc(Dc(n),{recursive:!0}),await Rc(n,`${JSON.stringify(e,null,2)}
|
|
4
|
+
`,{mode:384})}async function ae(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 Oe(t)}catch(s){throw t.MOA_URL||t.MOA_TOKEN?new Error(Ps(t)):s}let o=e??n.current,r=n.profiles[o];if(!r)throw t.MOA_URL||t.MOA_TOKEN?new Error(Ps(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 Uc,_s,Ps,Ze=De(()=>{"use strict";Uc=()=>({current:"default",profiles:{}}),_s=(e=process.env)=>e.MOA_CONFIG??Cs(e.MOA_HOME??Cs(Lc(),".moa"),"config.json");Ps=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 Bt(e,t,n,o,r){let s=Date.now()+n*1e3,a=250,c="",i=t,l;for(;;){let d=await e.get(`/v1/jobs/${i}`).catch(async u=>{if(u instanceof Tt){if(l??=Date.now()+lr,Date.now()<l&&Date.now()<s)return;throw u}let p=u instanceof ue&&u.status===404&&r!==void 0?await r():void 0;if(!p)throw u;return i=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!==c&&(c=p,o(d.progress))}if(d.state!=="running"&&d.state!=="queued")return d;if(Date.now()>s)throw new to(t,n);await new Promise(u=>setTimeout(u,a)),a=Math.min(a*1.5,1e3)}}function xn(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 to,lr,An=De(()=>{"use strict";je();to=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}},lr=2e4});import{join as oo}from"node:path";function Je(e,t,n,o={}){let r={...e.env};o.account&&(r.CLOUDFLARE_ACCOUNT_ID=o.account),r.WRANGLER_SEND_METRICS="false",r.NO_COLOR="1";let[s,...a]=e.root?["pnpm","exec","wrangler"]:["npx","--yes",Co];return e.exec(s,[...a,...n],{cwd:e.root?oo(t,"apps","workspace"):t,env:r,timeoutMs:o.timeoutMs??6e4})}function ga(e,t,n,o,r){let s={...e.env,WRANGLER_SEND_METRICS:"false",NO_COLOR:"1"};o.account&&(s.CLOUDFLARE_ACCOUNT_ID=o.account);let[a,...c]=e.root?["pnpm","exec","wrangler"]:["npx","--yes",Co];return e.run(a,[...c,...n],{cwd:e.root?oo(t,"apps","workspace"):t,env:s,timeoutMs:o.timeoutMs??6e4},r)}function ha(e){let t=new Set,n=new Set;for(let o of e.split(/[\r\n]+/)){let r=/^([0-9a-f]{12}): (.+)$/.exec(o.trim());!r?.[1]||!r[2]||(t.add(r[1]),/^(Pushed|Layer already exists|Mounted from)/.test(r[2])&&n.add(r[1]))}return{done:n.size,total:t.size}}function Jt(e){let t=e.search(/[[{]/);if(t!==-1)try{return JSON.parse(e.slice(t))}catch{return}}async function wa(e,t){let n=await Je(e,t,["whoami","--json"],{timeoutMs:3e4}),o=Jt(n.stdout||n.stderr);if(!o)return{ok:!1,why:ao(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(c=>c.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 ka(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(`${El}?name=${encodeURIComponent(r)}&type=NS`,{headers:{accept:"application/dns-json"},signal:AbortSignal.timeout(1e4)})).json()}catch(c){return{onCloudflare:!1,why:`DNS did not answer: ${c instanceof Error?c.message:String(c)}`}}let a=(s.Answer??[]).filter(c=>c.type===2).map(c=>c.data);if(a.length!==0)return{zone:r,onCloudflare:a.every(c=>/\.ns\.cloudflare\.com\.?$/i.test(c))}}return{onCloudflare:!1,why:`no DNS zone holds ${t}`}}async function ya(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 d=(await s.json().catch(()=>({}))).errors?.[0]?.message??"";return{ok:!1,why:`${s.status} ${d}`.trim()}}let[a,c]=await Promise.all([r("/workers/account-settings"),r("/workers/scripts")]),i=c.ok?(await c.json().catch(()=>({}))).result?.length??0:0;return a.ok||i>0?{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 ba(e,t,n){let o=async()=>{let c=await Je(e,t,["d1","list","--json"],{account:n});return(Jt(c.stdout)??[]).find(l=>l.name===kt)?.uuid},r=await o();if(r)return{ok:!0,id:r,made:!1};let s=await Je(e,t,["d1","create",kt],{account:n});if(!s.ok)return{ok:!1,why:ao(s.stderr||s.stdout)||"d1 create failed"};let a=await o();return a?{ok:!0,id:a,made:!0}:{ok:!1,why:`${kt} was made but is not listed`}}async function $a(e,t,n){let o=await Je(e,t,["r2","bucket","create",yt],{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:ao(r)||"r2 bucket create failed"}}async function wr(e,t,n){let o=await Je(e,t,["containers","list","--json"],{account:n}),r=o.ok?Jt(o.stdout):void 0;return Array.isArray(r)?{ok:!0,apps:r}:{ok:!1,why:ao(o.stderr||o.stdout)||"wrangler did not answer"}}function kr(e){let t=e.filter(s=>s.name===We),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(`${We}:`)?o:void 0;return{id:n.id,...r?{image:r}:{}}}async function va(e,t,n){let o=await wr(e,t,n);return o.ok?kr(o.apps):void 0}async function so(e,t,n){let o=await Je(e,t,["containers","images","list","--json"],{account:n}),r=o.ok?Jt(o.stdout):void 0;if(!Array.isArray(r))return{ok:!1,why:ao(o.stderr||o.stdout)||"wrangler did not answer"};let s=r.filter(a=>typeof a.name=="string"&&a.name.split("/").at(-1)===We).flatMap(a=>Array.isArray(a.tags)?a.tags:[]).filter(a=>typeof a=="string"&&a!=="").map(a=>`${We}:${a}`);return{ok:!0,images:[...new Set(s)]}}async function Sa(e,t,n,o){let[r,s]=o.split(":"),a=await Je(e,t,["containers","images","list","--json"],{account:n}),c=a.ok?Jt(a.stdout):void 0;return Array.isArray(c)?c.some(i=>typeof i.name=="string"&&i.name.split("/").at(-1)===r&&Array.isArray(i.tags)&&i.tags.includes(s)):!1}function xa(e){let t=oo(e.root,"apps","workspace"),n=an(e.domain);return{name:Ye,account_id:e.account,main:e.worker??oo(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:kt,database_id:e.databaseId}],r2_buckets:[{binding:"STATE",bucket_name:yt}],durable_objects:{bindings:[{name:"WORKSPACE",class_name:"WorkspaceBox"}]},migrations:[{tag:"v1",new_sqlite_classes:["WorkspaceBox"]}],containers:[{class_name:"WorkspaceBox",name:We,image:e.image??oo(t,"Dockerfile.cloudflare"),...e.image?{}:{image_build_context:e.root},instance_type:"standard-4",max_instances:1}],vars:e.vars}}var Ye,kt,yt,We,It,hr,an,ro,Co,El,ao,io=De(()=>{"use strict";Ye="fnmoa-workspace",kt="fnmoa-workspace",yt="fnmoa-state",We=`${Ye}-workspacebox`,It=6,hr=/^(?=.{4,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/,an=e=>({workspace:`workspace.${e}`,console:`console.${e}`}),ro=e=>{let t=an(e);return{workspaceUrl:`https://${t.workspace}`,consoleUrl:`https://${t.console}`}},Co="wrangler@4";El="https://cloudflare-dns.com/dns-query";ao=e=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
6
|
+
`).filter(t=>t.trim()!=="").slice(-2).join(" \xB7 ").slice(0,240)});function _l(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Po(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 Ol(e){let t=e.replace(/-----BEGIN [^-]+-----/,"").replace(/-----END [^-]+-----/,"").replace(/\\n/g,"").replace(/\s+/g,"");return Po(t.replace(/\+/g,"-").replace(/\//g,"_"))}async function Tl(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},yr,!0,["verify"])}return crypto.subtle.importKey("spki",Ol(t),yr,!0,["verify"])}async function Il(e){let t=await crypto.subtle.exportKey("raw",e),n=await crypto.subtle.digest("SHA-256",t);return _l(new Uint8Array(n).slice(0,8))}async function Aa(e,t,n={}){let o=e.trim().split(".");if(o.length!==3)return{ok:!1,reason:"malformed"};let[r,s,a]=o,c,i,l;try{c=JSON.parse(new TextDecoder().decode(Po(r))),i=JSON.parse(new TextDecoder().decode(Po(s))),l=Po(a)}catch{return{ok:!1,reason:"malformed"}}let d=c;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(y=>typeof y=="string"?Tl(y):y)),f;if(typeof d.kid=="string")for(let y of u)await Il(y)===d.kid&&(f=y);else u.length===1&&(f=u[0]);if(!f)return{ok:!1,reason:"unknown key"};if(!await crypto.subtle.verify(yr,f,l,new TextEncoder().encode(`${r}.${s}`)))return{ok:!1,reason:"bad signature"};if(!Rl(i))return{ok:!1,reason:"malformed"};if(i.iss!==Cl)return{ok:!1,reason:"wrong issuer"};if(n.install!==void 0&&i.install!==n.install)return{ok:!1,reason:"wrong install"};let m=Math.floor((n.now??new Date).getTime()/1e3);return i.iat>m+Pl?{ok:!1,reason:"issued in the future"}:!n.allowExpired&&m>i.exp+(n.leewaySeconds??60)?{ok:!1,reason:"expired"}:{ok:!0,claims:i}}var Cl,yr,Pl,Rl,br=De(()=>{"use strict";Cl="fnmoa-licence",yr={name:"Ed25519"},Pl=300;Rl=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 Ll(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 $r(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 c=typeof a.error=="string"?a.error:"request_failed";throw new _o(c,s.status,Ll(c,typeof a.seats=="number"?a.seats:void 0,typeof a.message=="string"?a.message:void 0))}return a}async function Ca(e,t,n,o=fetch){return Ea(await $r(e,t,n,o))}async function Pa(e,t,n=fetch){let o=await $r(e,"device",t,n),{device_code:r,user_code:s,verification_uri_complete:a,expires_in:c,interval:i}=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 c=="number"?c:600,interval:typeof i=="number"?i:5}}async function _a(e,t,n=fetch){try{return Ea(await $r(e,"device/token",{device_code:t},n))}catch(o){if(o instanceof _o&&o.code==="authorization_pending")return"pending";throw o}}async function co(e,t,n,o){let r=await Aa(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 _o,Ea,Oa=De(()=>{"use strict";br();_o=class extends Error{constructor(n,o,r){super(r);this.code=n;this.status=o}};Ea=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 Dl,Pn,lo,Ta=De(()=>{"use strict";Dl=`-----BEGIN PUBLIC KEY-----
|
|
7
7
|
MCowBQYDK2VwAyEARMFG3IjOWjtjPhlxBemrNtNnN59FZNc3sDDipX1WKQA=
|
|
8
|
-
-----END PUBLIC KEY-----`,Pn=[
|
|
9
|
-
`,s=`${o}.${process.pid}.tmp`;await
|
|
10
|
-
`,{mode:384}),await
|
|
11
|
-
${n.stderr}`;return n.ok?{loggedIn:!0,says:
|
|
12
|
-
`).slice(-2).join(" \xB7 ").slice(0,200);
|
|
13
|
-
`)[0]?.trim()??"",
|
|
14
|
-
`).pop()?.trim()||"you",team:u.teamSlug??u.teamId,teamId:u.teamId,projectId:u.projectId,plan:await
|
|
8
|
+
-----END PUBLIC KEY-----`,Pn=[Dl,""].filter(Boolean),lo="https://fnmoa.locusgraph.com"});var Ia=De(()=>{Oa();Ta();br()});import{access as Ul,mkdir as Nl,readFile as Ml,rename as Na,writeFile as Gl}from"node:fs/promises";import{homedir as jl}from"node:os";import{dirname as Ma,join as _n}from"node:path";function Ba(e){if(typeof e!="object"||e===null)return To();let t=e,n=typeof t.answers=="object"&&t.answers!==null?t.answers:{},o={version:1,screen:Ra(t.screen)?t.screen:"where",answers:bt({source:qe(n.source,["image","checkout"]),runner:qe(n.runner,Sr),agentCli:qe(n.agentCli,we),tenant:B(n.tenant,64),owner:B(n.owner,64),repo:B(n.repo,140),workspacePort:La(n.workspacePort),consolePort:La(n.consolePort),appOrg:B(n.appOrg,39),githubApp:ja(n.githubApp),people:Fa(n.people),removed:Bl(n.removed),loginsFor:qe(n.loginsFor,Sr),loggedIn:Array.isArray(n.loggedIn)?[...new Set(n.loggedIn.map(w=>qe(w,we)).filter(w=>w!==void 0))]:void 0,domain:B(n.domain,253),cloudflareAccount:B(n.cloudflareAccount,64),locusgraphGraph:B(n.locusgraphGraph,100),consoleSignIn:qe(n.consoleSignIn,["password","github"]),vercelTeam:B(n.vercelTeam,100),vercelPlan:qe(n.vercelPlan,["hobby","pro","enterprise"]),vercelScope:Kl(n.vercelScope),store:qe(n.store,["postgres","sqlite"]),sandbox:Ha(n.sandbox),githubToken:qe(n.githubToken,["own","pasted"])}),passed:Array.isArray(t.passed)?[...new Set(t.passed.filter(Ra))]:[]},r=qe(t.target,$t);r&&(o.target=r);let s=Fl(t.lastDeploy);s&&(o.lastDeploy=s);let a=bt({deployed:Ka(t.deployed),sandbox:o.answers.sandbox,cloudflare:Cr(t.cloudflare),githubApp:o.answers.githubApp}),c=typeof t.deployments=="object"&&t.deployments!==null?t.deployments:{},i={};for(let w of $t){let v=zl(c[w]);v&&(w===r?Io(a)||(a=v):i[w]=v)}let l=(w,v)=>{let C=a[w];if(C===void 0||r===void 0||v===void 0||v===r)return;let N=i[v]??{};if(N[w]!==void 0)return;i[v]={...N,[w]:C};let{[w]:W,...ie}=a;a=ie},d=a.deployed?.runner,u=d==="vercel"?"vercel":d==="cloudflare"?"cloudflare":d&&"local",f=a.githubApp?.consoleUrl,p=f===void 0||f.startsWith("http://localhost:")?"local":f===a.sandbox?.consoleUrl?"vercel":f===a.deployed?.consoleUrl?u:void 0;l("githubApp",p),l("deployed",u),l("sandbox","vercel"),l("cloudflare","cloudflare"),a.deployed&&(o.deployed=a.deployed),a.cloudflare&&(o.cloudflare=a.cloudflare),o.answers=bt({...o.answers,sandbox:a.sandbox,githubApp:a.githubApp}),Object.keys(i).length>0&&(o.deployments=i);let m=typeof t.down=="object"&&t.down!==null?t.down:void 0,y=qe(m?.target,$t),b=w=>{if(!Array.isArray(w))return;let v=w.map(C=>B(C,300)).filter(C=>C!==void 0);return v.length>0?v:void 0};m&&y&&(o.down=bt({target:y,at:B(m.at)??new Date(0).toISOString(),removed:b(m.removed)??[],left:b(m.left)}));let g=B(t.updatedAt);return g&&(o.updatedAt=g),o}async function Vl(e,t){let n=_n(Ma(pt(t)),"local"),o=i=>Ul(_n(n,i)).then(()=>!0,()=>!1);if(!await o(Ar))return;let r=new Map;for(let i of[e.answers.githubApp,...$t.map(l=>e.deployments?.[l]?.githubApp)])i&&r.set(i.id,i);let s=[];for(let i of r.values())await o(Oo(i))||s.push(i);let[a,...c]=s;!a||c.length>0||await Na(_n(n,Ar),_n(n,Oo(a)))}async function Tn(e){let t;try{t=Ba(JSON.parse(await Ml(pt(e),"utf8")))}catch{return To()}return await Vl(t,e).catch(()=>{}),t}async function Rt(e,t,n=new Date){let o=pt(t);await Nl(Ma(o),{recursive:!0});let r=`${JSON.stringify({...Ba(e),updatedAt:n.toISOString()},null,2)}
|
|
9
|
+
`,s=`${o}.${process.pid}.tmp`;await Gl(s,r,{mode:384}),await Na(s,o)}function it(e,t){if(t===e.target)return e;let{deployed:n,cloudflare:o,deployments:r,...s}=e,{sandbox:a,githubApp:c,...i}=e.answers,l=Wa(e),d=t?e.deployments?.[t]:void 0,u={};for(let y of $t){let b=y===e.target?l:y===t?void 0:e.deployments?.[y];b&&(u[y]=b)}let f=e.answers.runner!=="cloudflare"?e.answers.runner:void 0,p=d?.deployed?.runner;return{...s,...t?{target:t}:{},answers:bt({...i,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 Ro(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 ut,po,$t,Sr,Hl,we,Te,Er,cn,Fl,On,To,pt,Ra,B,La,xr,qe,Ga,ja,Ha,Kl,Fa,Bl,vr,Wl,Jl,ql,uo,Da,Ua,Cr,Ka,Io,zl,bt,Oo,Ar,Wa,fo,Pr,Ja,_r,qa,za,Va,D,Xe=De(()=>{"use strict";ut=["where","checks","github","memory","logins","access","deploy"],po={where:"Where should fnMoa run?",checks:"Checks & account",github:"GitHub",logins:"Agent logins (owner only)",memory:"Memory & database",access:"Console access",deploy:"Deploy & done"},$t=["vercel","cloudflare","local"],Sr=["node","compose","cloudflare"],Hl=[...Sr,"vercel","image"],we=["claude","codex","cursor"],Te={claude:"ANTHROPIC_API_KEY",codex:"OPENAI_API_KEY",cursor:"CURSOR_API_KEY"},Er=20,cn=50,Fl=e=>{if(typeof e!="object"||e===null)return;let t=e,n=qe(t.target,$t),o=B(t.at,40);if(!n||!o||!Array.isArray(t.lines))return;let r=t.lines.slice(0,40).map(s=>{let a=typeof s=="object"&&s!==null?s:{},c=B(a.label,40),i=qe(a.mark,["ok","fail","wait","skip"]);if(!c||!i)return;let l=typeof a.detail=="string"?a.detail.slice(0,300):void 0;return{label:c,mark:i,...l?{detail:l}:{}}}).filter(s=>s!==void 0);return{target:n,at:o,ok:t.ok===!0,lines:r}},On=e=>e.answers.source??(e.deployed&&e.deployed.runner!=="image"?"checkout":"image"),To=()=>({version:1,screen:"where",answers:{},passed:[]}),pt=(e=process.env)=>_n(e.MOA_HOME??_n(jl(),".moa"),"setup.json"),Ra=e=>typeof e=="string"&&ut.includes(e),B=(e,t=200)=>typeof e=="string"&&e.length>0&&e.length<=t?e:void 0,La=e=>typeof e=="number"&&Number.isInteger(e)&&e>0&&e<65536?e:void 0,xr=e=>typeof e=="number"&&Number.isInteger(e)&&e>0?e:void 0,qe=(e,t)=>typeof e=="string"&&t.includes(e)?e:void 0,Ga=/^[a-z\d](?:[a-z\d-]{0,38})$/i,ja=e=>{if(typeof e!="object"||e===null)return;let t=e,n=xr(t.id),o=B(t.slug,100),r=B(t.clientId,100),s=B(t.settingsUrl);if(!n||!o||!r||!s)return;let a=B(t.consoleUrl);return{id:n,slug:o,clientId:r,settingsUrl:s,...t.deviceFlow===!0?{deviceFlow:!0}:{},...a?{consoleUrl:a}:{}}},Ha=e=>{if(typeof e!="object"||e===null)return;let t=e,n=B(t.name,63),o=B(t.workspaceUrl),r=B(t.consoleUrl);if(!(!n||!o||!r))return bt({name:n,workspaceUrl:o,consoleUrl:r,snapshotId:B(t.snapshotId,100),teamId:B(t.teamId,100),projectId:B(t.projectId,100),revision:B(t.revision,64)})},Kl=e=>{if(typeof e!="object"||e===null)return;let t=e,n=B(t.teamId,100),o=B(t.projectId,100);return n&&o?{teamId:n,projectId:o}:void 0},Fa=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"&&Ga.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:Er})}return t.size>0?[...t.values()]:void 0},Bl=e=>{if(!Array.isArray(e))return;let t=[...new Set(e.filter(n=>typeof n=="string"&&Ga.test(n)).map(n=>n.toLowerCase()))];return t.length>0?t:void 0},vr=/^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/,Wl=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,Jl=/^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/,ql=/^[a-z0-9]+(?:[._-][a-z0-9]+)*:[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/,uo=(e,t)=>typeof e=="string"&&t.test(e)?e:void 0,Da=(e,t,n)=>{if(typeof e!="object"||e===null)return;let o=e,r=uo(o[t],n);if(!(!r||typeof o.made!="boolean"))return{[t]:r,made:o.made,...o.unsure===!0&&!o.made?{unsure:!0}:{}}},Ua=e=>Array.isArray(e)?[...new Set(e.map(t=>uo(t,ql)).filter(t=>t!==void 0))]:[],Cr=e=>{if(typeof e!="object"||e===null)return;let t=e,n=uo(t.account,vr);if(!n)return;let o=Ua(t.images),r=Ua(t.unsureImages).filter(s=>!o.includes(s));return bt({account:n,worker:uo(t.worker,Wl),containerId:uo(t.containerId,vr),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:Da(t.d1,"id",vr),r2:Da(t.r2,"name",Jl)})},Ka=e=>{if(typeof e!="object"||e===null)return;let t=e,n=qe(t.runner,Hl),o=B(t.workspaceUrl),r=B(t.consoleUrl);if(!(!n||!o||!r))return bt({runner:n,workspaceUrl:o,consoleUrl:r,workspacePid:xr(t.workspacePid),consolePid:xr(t.consolePid),workspaceStarted:B(t.workspaceStarted,64),consoleStarted:B(t.consoleStarted,64),consoleFor:B(t.consoleFor),workspaceFor:B(t.workspaceFor),image:B(t.image),proofRunId:B(t.proofRunId),memory:B(t.memory),people:Fa(t.people),memoryKept:t.memoryKept===!0?!0:void 0,cloudflare:n==="cloudflare"?Cr(t.cloudflare):void 0,at:B(t.at)??new Date(0).toISOString()})},Io=e=>e!==void 0&&(e.deployed!==void 0||e.sandbox!==void 0||e.cloudflare!==void 0||e.githubApp!==void 0),zl=e=>{if(typeof e!="object"||e===null)return;let t=e,n=bt({deployed:Ka(t.deployed),sandbox:Ha(t.sandbox),cloudflare:Cr(t.cloudflare),githubApp:ja(t.githubApp)});return Io(n)?n:void 0},bt=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0));Oo=e=>`github-app.${e.id}.json`,Ar="github-app.json";Wa=e=>{let t=bt({deployed:e.deployed,sandbox:e.answers.sandbox,cloudflare:e.cloudflare,githubApp:e.answers.githubApp});return Io(t)?t:void 0},fo=(e,t)=>t===e.target?Wa(e):e.deployments?.[t],Pr=e=>$t.filter(t=>Io(fo(e,t)));Ja=(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};_r=e=>ut.indexOf(e),qa=e=>ut[Math.min(_r(e)+1,ut.length-1)],za=e=>ut[Math.max(_r(e)-1,0)],Va=e=>e==="where"?"":`${_r(e)}/${ut.length-1}`,D="\u2022\u2022\u2022"});import{randomUUID as Ya}from"node:crypto";import{chmod as Yl,mkdir as Xl,readFile as Ql,rename as Zl,writeFile as ed}from"node:fs/promises";import{hostname as td}from"node:os";import{dirname as Xa,join as nd}from"node:path";import{readFileSync as od}from"node:fs";async function mo(e,t){try{let n=JSON.parse(await Ql(Qa(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 Or(e,t,n){let o=Qa(e,t);await Xl(Xa(o),{recursive:!0,mode:448});let r=`${o}.${Ya()}.tmp`;await ed(r,`${JSON.stringify(n)}
|
|
10
|
+
`,{mode:384}),await Yl(r,384),await Zl(r,o)}async function Za(e){if(!lo||Pn.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 mo(e.sys.env,t),o=n??{install:Ya()};return n||await Or(e.sys.env,t,o),{target:t,saved:o}}async function Tr(e,t){let{target:n,saved:o}=await Za(e),r=await Ca(lo,"activate",{key:t,install:o.install,version:qt(),target:n},e.sys.fetch);if((await co(r.licence,Pn,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 Or(e.sys.env,n,a),a}async function Lo(e,t,n){let{target:o,saved:r}=await Za(e),s=await Pa(lo,{install:r.install,version:qt(),target:o,machine:td().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 c=await _a(lo,s.deviceCode,e.sys.fetch);if(c==="pending")continue;if((await co(c.licence,Pn,r.install,o)).lapsed)throw new Error("The subscription is unpaid. Renew it before installing or updating.");let l={install:r.install,licence:c.licence};return await Or(e.sys.env,o,l),l}throw new Error("The code ran out before it was approved. Press c to try again.")}function qt(){return rd}async function vt(e){if(ei(e)&&!e.secrets.licence)return[];try{let t=e.state.target;if(!t)throw new Error("Choose a target first.");let n=await mo(e.sys.env,t);if(!e.secrets.licence&&n?.licence){let a=await co(n.licence,Pn,n.install,t).then(c=>c.lapsed?void 0:c).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 Tr(e,o);return[{label:"licence",mark:"ok",detail:`${(await co(r.licence,Pn,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 zt(e){if(!e.state.target)return{};let t=await mo(e.sys.env,e.state.target);if(!t?.licence){if(ei(e))return{};throw new Error("Activate this install with `moa setup licence`.")}return{MOA_LICENCE:t.licence,MOA_INSTALL_ID:t.install}}var Qa,rd,ei,Lt=De(()=>{"use strict";Ia();Xe();Qa=(e,t)=>nd(Xa(pt(e)),`licence-${t}.json`);rd=JSON.parse(od(new URL(import.meta.url.endsWith("/main.js")?"../package.json":"../../package.json",import.meta.url),"utf8")).version,ei=e=>!!e.sys.root&&On(e.state)==="checkout"});import{join as sd}from"node:path";import{parseEnv as ad}from"node:util";async function oi(e,t){let n=await e.exec("docker",Dt(t,"build","workspace"),{cwd:t,env:ln(e),timeoutMs:12e5});return n.ok?{ok:!0}:{ok:!1,why:Do(n.stderr||n.stdout)||"no answer"}}function ln(e,t){let n={...e.env,MOA_TOKENS:"setup-check:setup"};for(let o of[...ri,"MOA_CLI_HOME","LOCUSGRAPH_AGENT_SECRET"])delete n[o];return t&&(n.CLAUDE_CODE_OAUTH_TOKEN=t),n}async function Ir(e,t,n){let o;if(n==="here"){let r={...e.env};for(let s of ri)delete r[s];o=await e.exec("claude",["-p",In],{env:{...r,CLAUDE_CODE_OAUTH_TOKEN:t},timeoutMs:12e4})}else o=await e.exec("docker",ni(n.container,"claude","-p",In),{cwd:n.container,env:ln(e,t),timeoutMs:12e5});return o.ok&&/\bok\b/i.test(o.stdout)?{ok:!0}:{ok:!1,why:Do(o.stdout||o.stderr)||"no answer"}}async function si(e,t){let n=await e.exec("docker",ni(t,"codex","login","status"),{cwd:t,env:ln(e),timeoutMs:12e5}),o=`${n.stdout}
|
|
11
|
+
${n.stderr}`;return n.ok?{loggedIn:!0,says:id(n.stdout)||"logged in"}:/not logged in/i.test(o)?{loggedIn:!1,says:"not logged in"}:{loggedIn:!1,says:"",broken:Do(o)||"the container did not start"}}function dn(e){let t=e.replace(cd,""),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 ai(e,t,n){let o="",r=!1,s,a=ln(e),c=()=>e.exec("docker",["rm","-f",ti],{cwd:t,env:a,timeoutMs:3e4});return{done:c().then(()=>r?{ok:!1,stdout:"",stderr:"stopped"}:(s=e.run("docker",Dt(t,"run","--rm","--no-deps","-T","--name",ti,"workspace","codex","login","--device-auth"),{cwd:t,env:a,timeoutMs:16*6e4},l=>{o+=l,n(dn(o))}),s.done)),cancel:()=>{r=!0,s?.cancel(),c()}}}async function Rn(e,t,n){let o={Authorization:`Bearer ${t.secret}`,"Content-Type":"application/json"},r=async(i,l,d=!1)=>{try{let u=await e.fetch(i,{...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} ${Do(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 c=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:c}}function ci(e){let t={};for(let[n,o]of Object.entries(ad(e??"")))o&&(t[n]=o);return t}var be,Dt,ni,In,ri,Do,id,cd,ti,ii,Vt=De(()=>{"use strict";be="fnmoa-setup",Dt=(e,...t)=>["compose","--env-file","/dev/null","-f",sd(e,"apps","workspace","compose.yaml"),"-p",be,...t],ni=(e,...t)=>Dt(e,"run","--rm","--no-deps","-T","workspace",...t);In="Reply with the single word ok and nothing else.",ri=["ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","CLAUDE_CODE_OAUTH_TOKEN"];Do=e=>e.trim().split(`
|
|
12
|
+
`).slice(-2).join(" \xB7 ").slice(0,200);id=e=>e.trim().split(`
|
|
13
|
+
`)[0]?.trim()??"",cd=new RegExp("\x1B\\[[0-9;?]*[A-Za-z]","g");ti=`${be}-codex-login`;ii="https://api.locusgraph.com"});import{randomBytes as ld}from"node:crypto";import{mkdir as dd,readFile as ud,rm as Rr,writeFile as pd}from"node:fs/promises";import{dirname as fd,join as md}from"node:path";async function li(e,t,n,o){let r=Uo(e,t);await dd(fd(r),{recursive:!0}),await Rr(r,{force:!0}),await pd(r,JSON.stringify({url:n,token:o}),{mode:384})}async function di(e,t,n){try{let o=JSON.parse(await ud(Uo(e,t),"utf8"));return o.url===n&&typeof o.token=="string"&&gd.test(o.token)?o.token:void 0}catch{return}}async function Yt(e,t,n){let o=await Oe(e.env),r=o.profiles[t],s=o.current,a=!!o.profiles[s],c=await di(e,t,n),i=r?.url===n?r.token:void 0;if(c&&i)return s!==t&&await wt({...o,current:t},e.env),{token:c,pending:!0,before:s,had:a};let l=c??i??Lr();return(r?.token!==l||r?.url!==n||s!==t)&&await wt({current:t,profiles:{...o.profiles,[t]:{url:n,token:l}}},e.env),c&&await Rr(Uo(e,t),{force:!0}),{token:l,pending:!1,before:s,had:a}}async function Xt(e,t,n,o){if(await di(e,t,n)!==o)return;let r=await Oe(e.env);await wt({...r,profiles:{...r.profiles,[t]:{url:n,token:o}}},e.env),await Rr(Uo(e,t),{force:!0})}var gd,Lr,Uo,Ln=De(()=>{"use strict";Ze();Qe();gd=/^moa_[A-Za-z0-9_-]{16,128}$/,Lr=()=>`moa_${ld(24).toString("base64url")}`,Uo=(e,t)=>pe(e,md("rotate",`${t}.json`))});import{Writable as hd}from"node:stream";function xt(e){return e==="hobby"?{vcpus:4,memoryGb:8,sessionMs:45*6e4,maxRuns:1}:{vcpus:8,memoryGb:16,sessionMs:24*36e5,maxRuns:3}}function mi(e){let t=e.memoryGb*24*No.memoryGbHour;return{idle:t,busy:t+e.vcpus*24*No.cpuHour,perGb:No.transferGb,storagePerGbMonth:No.storageGbMonth}}async function wi(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:kd};o.forEach(([,a],c)=>{r[`V_${c}`]=a});let s=await He(e,yd,r);return s.ok?{ok:!0,changed:s.stdout.trim().endsWith("1")}:{ok:!1,changed:!1,why:At(s.stderr||s.stdout)}}async function Ur(e,t){if(t){let n=await He(e,`exec sh "${St}/start.sh" stop`,{},12e4);if(!n.ok)throw new Error(At(n.stderr||n.stdout)||"the servers did not stop")}await e.spawn("sh",["-c",`exec sh "${St}/start.sh"${t?" restart":""}`])}async function ki(e){await He(e,`rm -f "${St}/restart"`)}async function jo(e){let t=await He(e,`if [ -f "${St}/start.sh" ]; then exec sh "${St}/start.sh" stop; fi`,{},12e4);if(!t.ok)throw new Error(At(t.stderr||t.stdout)||"the servers did not stop")}function pi(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:Dr(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:ui(i=>{r+=i,o(i)}),stderr:ui(i=>{s+=i,o(i)}),signal:a.signal}).then(i=>({ok:i.exitCode===0,stdout:r,stderr:s}),i=>({ok:!1,stdout:r,stderr:s||Dr(i)})),cancel:()=>a.abort()}},snapshot:async()=>(await e.snapshot()).snapshotId,stop:async()=>{await e.stop()},delete:async()=>{await e.delete({deleteOrphanSnapshots:!0})}}}async function bd(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 bi(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:c}=await import("@vercel/sandbox/dist/auth/index.js"),i=t.VERCEL_TEAM_ID?.trim(),l=t.VERCEL_PROJECT_ID?.trim(),d=o??i,u=r?{...r,teamSlug:o??r.teamId}:d&&l&&(!o||o===i)?{teamId:d,projectId:l,teamSlug:d}:await c({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 bd(e.fetch,a,u.teamId),token:a}},get:async(o,r,s=!1)=>{let{Sandbox:a}=await import("@vercel/sandbox");try{return pi(await a.get({name:r,resume:s,...n(o)}))}catch(c){if(fi(c))return;throw c}},create:async(o,r)=>{let{Sandbox:s}=await import("@vercel/sandbox"),a={name:r.name,persistent:!0,ports:[ft,un],resources:{vcpus:r.size.vcpus},timeout:r.size.sessionMs,...r.env?{env:r.env}:{},...n(o)};return pi(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(!fi(a))throw a}}}}var ft,un,go,No,Mo,gi,Go,hi,St,wd,kd,yd,He,At,Dn,Ho,yi,Dr,ui,fi,pn=De(()=>{"use strict";ft=8787,un=3100,go=e=>`fnmoa-${e}`.slice(0,63);No={cpuHour:.128,memoryGbHour:.0212,transferGb:.15,storageGbMonth:.08};Mo=e=>`$${e<10?e.toFixed(2):e.toFixed(0)}`,gi=(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`}`,Go=e=>{let t=mi(e);return`~${Mo(t.idle)} a day idle, ~${Mo(t.busy)} a day busy, + ${Mo(t.perGb)} per GB served from its URLs (downloads free) \u2014 list prices`},hi=e=>`+ ${Mo(mi(e).storagePerGbMonth)} per GB a month for its kept disk and its snapshot, stopped or not`,St="$HOME/moa",wd=`set -eu
|
|
15
15
|
M="${St}"
|
|
16
16
|
mkdir -p "$M"
|
|
17
17
|
missing() { ! command -v "$1" > /dev/null 2>&1; }
|
|
@@ -45,7 +45,7 @@ pnpm install --frozen-lockfile
|
|
|
45
45
|
pnpm --filter "@fnmoa/workspace..." run build
|
|
46
46
|
pnpm --filter @fnmoa/console run build
|
|
47
47
|
printf %s "$MOA_REVISION" > "$M/image"
|
|
48
|
-
`,
|
|
48
|
+
`,kd=`#!/bin/sh
|
|
49
49
|
M="${St}"
|
|
50
50
|
R="$M/run/$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || echo boot)"
|
|
51
51
|
mkdir -p "$M/logs" "$R" "$M/data/worktrees"
|
|
@@ -104,12 +104,12 @@ fi
|
|
|
104
104
|
if ! running console; then
|
|
105
105
|
(
|
|
106
106
|
load console
|
|
107
|
-
cd "$M/fnmoa/apps/console" && exec node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port ${
|
|
107
|
+
cd "$M/fnmoa/apps/console" && exec node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port ${un}
|
|
108
108
|
) >> "$M/logs/console.log" 2>&1 &
|
|
109
109
|
echo $! > "$R/console.pid"
|
|
110
110
|
fi
|
|
111
111
|
wait
|
|
112
|
-
`,
|
|
112
|
+
`,yd=`set -eu
|
|
113
113
|
M="${St}"
|
|
114
114
|
umask 077
|
|
115
115
|
changed=0
|
|
@@ -133,38 +133,38 @@ if [ ! -f "$M/start.sh" ] || [ "$(cat "$M/start.sh")" != "$(printf %s "$START")"
|
|
|
133
133
|
fi
|
|
134
134
|
if [ "$changed" = 1 ]; then : > "$M/restart"; fi
|
|
135
135
|
if [ -f "$M/restart" ]; then echo 1; else echo 0; fi
|
|
136
|
-
`,
|
|
137
|
-
`).slice(-t).join(" \xB7 ").slice(0,300);Dn=async(e,t)=>{let n=await
|
|
138
|
-
`)[0]?.slice(0,300)??"",
|
|
139
|
-
`)[0]??"");return r?.[1]&&r[2]!==void 0?{started:r[1],command:r[2]}:void 0}catch{return}},portsOf:n=>{try{let r
|
|
140
|
-
`).map(s=>Ad.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)=>{kd(Ur(r.log),{recursive:!0});let s=yd(r.log,"a"),a=ki(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=hi({env:e,exec:(n,o)=>t.exec(n,o),fetch:t.fetch,scratch:Nr(pt(e),"..","local")}),t}var xd,Ad,Ed,mo=Oe(()=>{"use strict";qe();un();xd=/^\s*(\w{3} \w{3} [ \d]\d \d\d:\d\d:\d\d \d{4})\s+(.*)$/,Ad=/^n.*:(\d+)$/,Ed=e=>Dr(Nr(e,"pnpm-workspace.yaml"))&&Dr(Nr(e,"apps","workspace","compose.yaml"))});import{createHash as Pd}from"node:crypto";import{join as Un}from"node:path";async function yi(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 _d(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 Od(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 Go(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=Q=>{let Le=s.findIndex(ue=>ue.label===Q.label);Le===-1?s.push(Q):s[Le]=Q,t([...s])},i,c=(Q,Le,ue)=>(a({label:Q,mark:"fail",detail:Le,fix:ue}),i?{ok:!1,deployed:i}:{ok:!1}),l=o.root;if(!l||Te(e)||r.deployed?.runner==="image")return Gr(e,t,n);if(gn(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,g=Ce(e),y=r.answers.tenant??Ie(r.answers.owner),b=Qt(r),m=Gt(r),w=`http://127.0.0.1:${b}`,v=`http://localhost:${m}`,_=r.answers.owner,H=await Zt(e);if("why"in H)return c("console sign-in",H.why,H.fix);let W=H.mode==="github"?r.answers.githubApp:void 0,le=H.mode==="none"?{}:{...H.vars,...H.secrets,AUTH_URL:v};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 L=Un(l,"apps","workspace","dist","node.js"),X=Un(l,"apps","console",".next"),D=o.exists(L),B=o.exists(Un(X,"BUILD_ID"));if(D&&B)a({label:"build",mark:"ok",detail:"workspace and console are built"});else{if(!B&&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 Q=[...D?[]:[{what:"workspace",args:["--filter","@fnmoa/workspace...","run","build"]}],...B?[]:[{what:"console",args:["--filter","@fnmoa/console","run","build"]}]],Le=Q.map(Be=>Be.what).join(" and ");a({label:"build",mark:"wait",detail:`building the ${Le} \u2014 a few minutes`});let ue=await o.exec("pnpm",["install"],{cwd:l,timeoutMs:6e5});if(!ue.ok)return c("build",`pnpm install: ${Mn(ue.stderr||ue.stdout)}`,`run \`pnpm install\` in ${l}, then Enter`);for(let Be of Q){let ht=await o.exec("pnpm",Be.args,{cwd:l,timeoutMs:9e5});if(!ht.ok)return c("build",`${Be.what}: ${Mn(ht.stderr||ht.stdout)}`,`run \`pnpm ${Be.args.join(" ")}\` in ${l}, then Enter`)}a({label:"build",mark:"ok",detail:`${Le} built`})}let{token:oe,pending:he,before:Z,had:ve}=await Vt(o,Nn,w);a({label:"owner token",mark:"ok",detail:`${U} \u2014 profile ${Nn} in ~/.moa/config.json${Z===Nn?"":ve?`; \`moa\` now talks to this workspace, not profile ${Z} (\`moa use ${Z}\` to go back)`:"; `moa` now talks to this workspace"}`});let $=N({url:w,token:oe},o.fetch),I=r.deployed?jn(o,r.deployed):void 0,re=Td({runner:d,workspaceUrl:w,tenant:y,token:oe,...r.answers.repo?{repo:r.answers.repo}:{},..._?{owner:_}:{},...W?{githubClientId:W.clientId}:{},...g?{locusgraph:g.secret,graph:g.graph,server:g.server}:{},...p?{claude:p}:{}}),O=I?.workspacePid!==void 0&&Nt(o,I.workspacePid,I.workspaceStarted,Et(I.workspaceUrl))?I.workspacePid:void 0,V=O?I?.workspaceStarted:void 0,fe=I?.workspaceFor,ee=I?.consolePid!==void 0&&Nt(o,I.consolePid,I.consoleStarted,Et(I.consoleUrl))?I.consolePid:void 0,Se=ee?I?.consoleStarted:void 0,xe=I?.consoleFor,Ge=I?.memoryKept,Re=I?.workspaceUrl===w?I?.people:void 0,Ke=()=>{i={runner:d,workspaceUrl:w,consoleUrl:v,...O?{workspacePid:O,...V?{workspaceStarted:V}:{}}:{},...fe?{workspaceFor:fe}:{},...ee?{consolePid:ee,...Se?{consoleStarted:Se}:{},...xe?{consoleFor:xe}:{}}:{},...Re?{people:Re}:{},...Ge?{memoryKept:Ge}:{},at:new Date().toISOString()},n(i)};if((O||ee)&&Ke(),O!==void 0&&fe!==re){if(a({label:"workspace",mark:"wait",detail:"restarting \u2014 it was started with other answers"}),o.stop(O),!await Od(o,O,15))return c("workspace",`the old workspace (pid ${O}) is still running`,`stop it (\`kill ${O}\`), then Enter`);O=void 0,V=void 0,fe=void 0,Ke()}let ke=await $.get("/v1/whoami").then(()=>"ours",Q=>Q instanceof ie?"other":"down");if(ke==="other")return c("workspace",`something else answers on :${b} and refuses this token`,"stop it, or run `moa setup --workspace-port <port>`");if(ke==="ours"&&fe!==re&&d==="node")return c("workspace",`the workspace on :${b} was not started with these answers`,I?.runner==="compose"?`run \`docker compose -p ${we} down\`, then Enter`:"stop it, then Enter");if(ke==="ours"&&fe===re)a({label:"workspace",mark:"ok",detail:`${w} \u2014 already running`});else{a({label:"workspace",mark:"wait",detail:`starting on :${b}`});let Q={...o.env,...await qt(e),MOA_TOKENS:`${oe}:${y}`,PORT:String(b),MOA_PORT:String(b),MOA_CONSOLE_PORT:String(m),...r.answers.repo?{MOA_REPO:r.answers.repo}:{},..._?{MOA_OWNER_LOGIN:_}:{},...W?{MOA_GITHUB_CLIENT_ID:W.clientId}:{},...g?{LOCUSGRAPH_AGENT_SECRET:g.secret,LOCUSGRAPH_GRAPH_ID:g.graph,LOCUSGRAPH_SERVER_URL:g.server}:{}};for(let ue of["MOA_DATABASE_URL","MOA_SQLITE_FILE","MOA_TOKEN_FILE","MOA_WORKSPACE_ROOT","MOA_SELF_URL",..._?[]:["MOA_OWNER_LOGIN"],...W?[]:["MOA_GITHUB_CLIENT_ID"]])delete Q[ue];if(d==="compose"){for(let Be of["MOA_CLI_HOME","CLAUDE_CODE_OAUTH_TOKEN","ANTHROPIC_API_KEY"])delete Q[Be];p&&(Q.CLAUDE_CODE_OAUTH_TOKEN=p);let ue=await o.exec("docker",Ut(l,"up","-d","--build"),{cwd:l,env:Q,timeoutMs:12e5});if(!ue.ok)return c("workspace",`docker compose: ${Mn(ue.stderr||ue.stdout)}`,"fix that, then Enter");O=void 0,V=void 0,fe=re,Ge=!!g,Ke()}else{let ue=ce(o,"workspace.db");O=o.start(process.execPath,[L],{cwd:Un(l,"apps","workspace"),env:{...Q,...p?{CLAUDE_CODE_OAUTH_TOKEN:p}:{},MOA_DATABASE_URL:`sqlite:${ue}`,MOA_D1_ACCOUNT_ID:"",MOA_D1_DATABASE_ID:"",MOA_D1_API_TOKEN:"",MOA_SQLITE_FILE:ue,MOA_WORKSPACE_ROOT:ce(o,"worktrees"),MOA_SELF_URL:w,MOA_PID_FILE:jr(o)},log:ce(o,"workspace.log")}),V=o.processOf(O)?.started,fe=re,Ge=!!g,Ke()}if(!await yi(o,`${w}/health`,d==="compose"?120:60))return c("workspace",`did not answer on :${b}`,d==="compose"?`look at \`docker compose -p ${we} logs workspace\`, then Enter`:`look at ${ce(o,"workspace.log")}, then Enter`);if(!await $.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:`${w}${O?` (pid ${O})`:""}`})}he&&await Yt(o,Nn,w,oe);let _e=await Mr(o,w,a,d==="compose"?"postgres":"sqlite",d==="compose"?`look at \`docker compose -p ${we} logs\`, then Enter`:`look at ${ce(o,"workspace.log")}, then Enter`);if(_e)return c(_e.label,_e.detail,_e.fix);let T=await fn($,r,a,Re);if(T)return c(T.label,T.detail,T.fix);let F=Re;Re=r.answers.people??[],JSON.stringify(Re)!==JSON.stringify(F??[])&&Ke();let je=Id(w,oe,le),nt=ee!==void 0&&await o.listening(m);if(nt&&ee!==void 0&&xe!==je){if(a({label:"console",mark:"wait",detail:"restarting \u2014 it points at another workspace"}),o.stop(ee),ee=void 0,Se=void 0,xe=void 0,Ke(),!await _d(o,m,15))return c("console",`the old console still holds :${m}`,"stop it, then Enter");nt=!1}if(nt)a({label:"console",mark:"ok",detail:`${v} \u2014 already running`});else{if(await o.listening(m))return c("console",`something else is on :${m}`,"stop it, or run `moa setup --console-port <port>`");if(a({label:"console",mark:"wait",detail:`starting on :${m}`}),ee=o.start(process.execPath,[Un(l,"apps","console","node_modules","next","dist","bin","next"),"start","--port",String(m),"--hostname","127.0.0.1"],{cwd:Un(l,"apps","console"),env:{...Rd(o.env),MOA_URL:w,MOA_TOKEN:oe,...le},log:ce(o,"console.log")}),Se=o.processOf(ee)?.started,xe=je,Ke(),!await yi(o,v,60))return c("console",`did not answer on :${m}`,`look at ${ce(o,"console.log")}, then Enter`);a({label:"console",mark:"ok",detail:`${v} (pid ${ee})${W?" \u2014 Sign in with GitHub":""}`})}let ot=await Mt($,f,d==="compose"?"in the container":"on this machine",Q=>d==="compose"?"Esc back to Agent logins and log it in again, then Enter":`is ${Q} logged in on this machine? Run \`${Q}\` once and log in, then Enter`,a);if("label"in ot)return c(ot.label,ot.detail,ot.fix);let gt=ot.runs[u],me=await mn($,a);if("label"in me)return c(me.label,me.detail,me.fix);let $n=me.memory;return me.live!==void 0&&(Ge=me.live),a({label:"Console",mark:"ok",detail:v}),a({label:"Workspace",mark:"ok",detail:w}),a({label:"Next",mark:"skip",detail:`moa whoami \xB7 moa agent list \xB7 moa run ${Gn(u)} "\u2026"`}),{ok:!0,deployed:{runner:d,workspaceUrl:w,consoleUrl:v,...O?{workspacePid:O,...V?{workspaceStarted:V}:{}}:{},...fe?{workspaceFor:fe}:{},...ee?{consolePid:ee,...Se?{consoleStarted:Se}:{},...xe?{consoleFor:xe}:{}}:{},...gt?{proofRunId:gt}:{},memory:$n,people:Re,...Ge?{memoryKept:Ge}:{},at:new Date().toISOString()}}}async function Mr(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: ${Mn(String(s.error??"no answer"),1)}`,fix:r};n({label:"database",mark:"ok",detail:`${a} \u2014 migrated, and it answers`})}async function fn(e,t,n,o=[]){let r=t.answers.people??[],s=r.filter(c=>!o.some(l=>Ld(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 ie&&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 ie&&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 Mt(e,t,n,o,r){let s={};for(let a of t){let i=`proof: ${a} run`,c=Gn(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 ie&&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:In}),u=await Bt(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:Mn(String(p),2),fix:o(a)}}s[a]=f.runId,r({label:i,mark:"ok",detail:`${a} answered "${Mn(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 mn(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 Nn,Gn,Mn,bi,Td,Id,Rd,Ld,Xt=Oe(()=>{"use strict";Ne();An();ze();at();Dt();zt();Ln();qe();mo();Nn="local",Gn=e=>`setup-proof-${e}`,Mn=(e,t=3)=>e.trim().split(`
|
|
141
|
-
`).slice(-t).join(" \xB7 ").slice(0,300);
|
|
142
|
-
`)[0]} \u2014 ${o}`},{label:"image",mark:n.ok?"ok":"fail",detail:
|
|
143
|
-
`).map(n=>n.trim()).reverse().find(n=>/^[A-Z]\w*(Error|Exception)\b.*:/.test(n))}async function
|
|
144
|
-
${$.stderr}`);return{...s(I?`The workspace did not become ready: ${I}. \`moa setup logs\` has the rest.`:"The workspace did not become ready. `moa setup logs` says why."),deployed:L}}await Yt(c,"local",u,p);let B=await fn(X,l,r,l.deployed?.people);if(B)return{...s(B.detail),deployed:L};let oe=l.answers.agentCli??"claude",he=[...new Set([oe,...l.answers.loggedIn??[]])];for(let $ of he){if(!go($)||be(e,$)||await tn(c,$))continue;r({label:$,mark:"wait",detail:"starting its login inside the container\u2026"});let re=await Kn(c,$,O=>{let V=Bn(O);V&&r({label:$,mark:"wait",detail:V})}).done;if(!re.ok||!await tn(c,$))return r({label:$,mark:"fail",detail:`the login did not finish: ${ae(re.stderr)||"no answer"}`,fix:"Enter to log in again \u2014 or Esc back to Agent logins and \u2191 \u2193 to choose another agent"}),{ok:!1,deployed:L};r({label:$,mark:"ok",detail:"logged in inside the container \u2014 kept on its home volume"})}let Z=await Mt(X,he,"in the image",$=>$==="claude"?"Esc back to Agent logins: t to connect your Claude subscription, or export ANTHROPIC_API_KEY and run setup again.":`\`moa setup login ${$}\` to log it in again, or export ${Ee[$]} and run setup again.`,r);if("label"in Z)return{...s(Z.detail),deployed:L};let ve=await mn(X,r);return"label"in ve?{...s(ve.detail),deployed:L}:(r({label:"console",mark:"ok",detail:f}),{ok:!0,deployed:{...L,people:l.answers.people,memory:ve.memory,memoryKept:ve.live}})}var Pe,Hn,Hr,en,et,Kr,go,Bn,at=Oe(()=>{"use strict";Ne();ze();Xt();Dt();zt();Ln();qe();Pe=()=>`ghcr.io/fnlog0/fnmoa:${Jt()}`,Hn=()=>Md(new URL(import.meta.url.endsWith("/main.js")?"../":"../../",import.meta.url)),Hr=()=>Si(Hn(),"dist/assets/worker.js"),en=e=>ce(e,"image/compose.json"),et=(e,...t)=>["compose","--project-name","fnmoa-image","--file",en(e),...t];Kr={codex:{status:["codex","login","status"],login:["codex","login","--device-auth"],env:[],loggedIn:e=>e.ok,prompt:ln},cursor:{status:["cursor-agent","status"],login:["cursor-agent","login"],env:["-e","NO_OPEN_BROWSER=1"],loggedIn:e=>e.ok&&!/not logged in/i.test(`${e.stdout}${e.stderr}`),prompt:jo}},go=e=>e in Kr;Bn=e=>e.url?e.code?`open ${e.url} and enter ${e.code} \u2014 waiting for you to approve it`:`open ${e.url} and sign in \u2014 waiting for you to approve it`:void 0});import{pbkdf2Sync as Gd,randomBytes as Pi}from"node:crypto";import{chmod as jd,mkdir as Hd,writeFile as Fd}from"node:fs/promises";import{dirname as Kd,join as Jr}from"node:path";async function hn(e,t,n,o=["--version"]){let r=await e.exec(t,o);return r.ok?S(t,wo(r.stdout)):C(t,"not found",n)}function Jd(e=process){return e.platform==="darwin"&&e.env.MOA_KEEP_AWAKE!=="0"?S("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"):S("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 qd(e,t=process){switch(e.target){case"local":return{lines:[S("This machine","Docker or local, nothing in the cloud"),Jd(t)]};case"cloudflare":return{lines:[S("Cloudflare","a Worker and its container on D1, on your own domain \u2014 Workers Paid plan")]};case"vercel":return{lines:[S("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 Ci(e,t,n,o,r,s){return await e.sys.listening(n)?o!==void 0&&Nt(e.sys,o,r,n)?S(`port ${n}`,`the ${t} the last setup started`):C(`port ${n}`,"something else is listening there",`stop it, or run \`moa setup ${s} <port>\``):S(`port ${n}`,`free for the ${t}`)}async function _i(e){return[Number(process.versions.node.split(".")[0])>=22?S("node",process.versions.node):C("node",process.versions.node,"install node 22 or newer"),...await Promise.all([hn(e,"pnpm","install pnpm: `corepack enable`"),hn(e,"git","install git"),hn(e,"gh","install the GitHub CLI: https://cli.github.com")])]}function zr(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 Vr(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 zd(e){let{sys:t,state:n}=e,o=await _i(t);if(Te(e)){let{imageChecks:u}=await Promise.resolve().then(()=>(at(),Br));o.push(...await u(e))}else{let u=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);o.push(u.ok?S("docker",`${ae(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??Hn();o.push(S(t.root?"checkout":"release",s));let a=await We(t,s,["--version"],{timeoutMs:3e4});if(!a.ok)return o.push(t.root?C("wrangler","not installed in this checkout",`run \`pnpm install\` in ${s}, then r`):C("wrangler",`\`npx ${xo}\` did not run: ${ae(a.stderr)||"no answer"}`,"check Node and npm can reach the npm registry, then r")),{lines:o};o.push(S("wrangler",wo(a.stdout)));let i=await ma(t,s),c;i.ok?(c=i.account.id,o.push(S("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 ${Jr(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 ga(t,l),f=no(l);o.push(u.zone&&u.onCloudflare?S("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=zr(e);if(!d)o.push(qr(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 ha(t,c,d.token);o.push(u.ok?S("D1 token",`${U} (${d.from}) \u2014 D1 answers to it`):C("D1 token",`${U} (${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 ${It}h after the last request`)),{lines:o,...c?{answers:{cloudflareAccount:c}}:{}}}async function Vd(e){let{sys:t,state:n}=e;if(Te(e)&&n.target==="local"){let{imageChecks:a}=await Promise.resolve().then(()=>(at(),Br));return{lines:await a(e)}}if(n.target==="cloudflare")return zd(e);let o=await _i(t),r=n.deployed?jn(t,n.deployed):void 0,s=await t.exec("docker",["--version"]);return o.push(s.ok?S("docker",wo(s.stdout)):j("docker","not found \u2014 only needed for Postgres with docker compose")),o.push(t.root?S("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 Ci(e,"workspace",Qt(n),r?.workspacePid,r?.workspaceStarted,"--workspace-port"),await Ci(e,"console",Gt(n),n.deployed?.consolePid,n.deployed?.consoleStarted,"--console-port"),j("account","this machine \u2014 no account needed")),{lines:o}}async function Yd(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=ae(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 Qd(e,s);let a=[S("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=ae(c.stdout);a.push(c.ok&&["ADMIN","MAINTAIN","WRITE"].includes(l)?S("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 Xd(e,t,n,o){let r=await e.exec("gh",["repo","view",t,"--json","viewerPermission","--jq",".viewerPermission"],o?{env:o}:{}),s=ae(r.stdout);return r.ok&&["ADMIN","MAINTAIN","WRITE"].includes(s)?S("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 Qd(e,t){let{sys:n,state:o}=e,r=[S("gh login",`${t} \u2014 you, the first owner`)],s=Vr(e),a;if(!s)r.push(qr(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=ae(c.stdout)||void 0,r.push(c.ok&&a?S("token",`${U} (${s.from}) \u2014 GitHub sees ${a}`):C("token",`${U} (${s.from}) \u2014 GitHub refused it`,"press k to paste another"));let l=o.answers.repo;l&&a&&r.push(await Xd(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 eu(e){let{sys:t,state:n}=e;if(Te(e)&&n.target==="local")return tu(e);if(n.target==="cloudflare")return ou(e);let o=Zd(n),[r,s]=await Promise.all([t.exec("claude",["--version"]),t.exec("codex",["--version"])]),a=Ze(e),i={claude:r.ok,codex:s.ok,cursor:!1},c=[o?S("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 Za(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 g=await ti(t,t.root);i.codex=g.loggedIn,l=g.loggedIn?S("codex",`${g.says} \u2014 its own session in the container (l to log in again)`):g.broken?C("codex",`the container did not run: ${g.broken}`,"start Docker, then press r"):j("codex","not logged in inside the container \u2014 l to log in with a code")}else{let g=s.ok?await t.exec("codex",["login","status"]):void 0;i.codex=s.ok&&g?.ok===!0,l=s.ok?g?.ok?S("codex",`${wo(s.stdout)} \u2014 ${ae(g.stdout)||"logged in"}`):j("codex","not logged in \u2014 run `codex login`, then press r"):j("codex","not found")}let u;if(a){let g=await Or(t,a.token,o&&t.root?{container:t.root}:"here");i.claude=g.ok,u=g.ok?S("claude",`token ${U} (${a.from}) \u2014 "reply ok" ${o?"in the container":"on it alone"}`):C("claude",`the token did not answer: ${g.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?S("claude",`${wo(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]?S("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(g=>i[g]);return{lines:c,answers:{...f?{agentCli:f}:{},loggedIn:p,...ko(c)?{loginsFor:o?"compose":"node"}:{}}}}async function tu(e){let{sys:t,state:n}=e,o=n.deployed?.runner==="image",r=Ze(e),s=new Set,a=[S("agents run","in the image's container \u2014 only what is carried in reaches it")];r?(s.add("claude"),a.push(S("claude",`token ${U} (${r.from}) \u2014 Deploy proves it in the image`))):be(e,"claude")?(s.add("claude"),a.push(S("claude",`ANTHROPIC_API_KEY ${U} (${ho(e,"claude")})`))):o&&n.answers.loggedIn?.includes("claude")?(s.add("claude"),a.push(j("claude","kept \u2014 the running container has its login (t to replace it)"))):a.push(j("claude","not connected \u2014 t for your Claude subscription (`claude setup-token`), or export ANTHROPIC_API_KEY"));let i={codex:"ChatGPT",cursor:"Cursor"};for(let l of["codex","cursor"])be(e,l)?(s.add(l),a.push(S(l,`${Ee[l]} ${U} (${ho(e,l)})`))):o&&await tn(t,l)?(s.add(l),a.push(S(l,"logged in inside the container \u2014 kept on its home volume"))):a.push(j(l,`logs in at Deploy when chosen \u2014 or later, \`moa setup login ${l}\` with your ${i[l]} account`));let c=n.answers.agentCli??(s.has("claude")?"claude":s.values().next().value??"codex");return a.push(c!=="claude"||s.has("claude")?S("proof run",`uses ${c} (\u2191 \u2193 to change)`):C("proof run","claude has no login for the image","press t to connect your Claude subscription \u2014 or \u2191 \u2193 to choose codex or cursor")),{lines:a,answers:{agentCli:c,loggedIn:pe.filter(l=>s.has(l))}}}async function nu(e){let t=e.state.deployed;if(t?.runner!=="cloudflare")return;let n=await Ae(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 ou(e){let{sys:t,state:n}=e,o=Ze(e),r=[S("agents run","in the Cloudflare container \u2014 only what is carried in reaches it")],s={claude:!1,codex:!0,cursor:!1};if(o){let d=await Or(t,o.token,"here");s.claude=d.ok,r.push(d.ok?S("claude",`token ${U} (${o.from}) \u2014 "reply ok" on it alone; a Worker secret`):C("claude",`the token did not answer: ${d.why}`,"press c to make a new one with `claude setup-token`, or k for an API key"))}else be(e,"claude")?(s.claude=!0,r.push(S("claude",`ANTHROPIC_API_KEY ${U} (${ho(e,"claude")}) \u2014 a Worker secret`))):qr(n)&&n.answers.loggedIn?.includes("claude")?(s.claude=!0,r.push(j("claude","kept \u2014 the deployed Worker has the token (c to replace it)"))):r.push(j("claude","not connected \u2014 c for your Claude subscription, or k for an API key"));let a=be(e,"codex"),i=a?void 0:await nu(e);r.push(a?S("codex",`OPENAI_API_KEY ${U} (${ho(e,"codex")}) \u2014 a Worker secret`):i===!0?S("codex","logged in inside the container \u2014 its login is kept in R2 across restarts"):j("codex",i===!1?"not logged in inside the container \u2014 Deploy shows a link and a code (or k for an API key)":"logs in inside the container at Deploy \u2014 a link and a code (or k for an API key)")),s.cursor=be(e,"cursor")!==void 0,r.push(s.cursor?S("cursor",`CURSOR_API_KEY ${U} (${ho(e,"cursor")}) \u2014 a Worker secret`):j("cursor","not connected \u2014 on Cloudflare cursor runs on an API key: k"));let c=n.answers.agentCli??(s.claude?"claude":"codex");r.push(s[c]?S("proof run",`uses ${c} (\u2191 \u2193 to change)`):C("proof run",`${c} is not connected`,"press c to connect it or k for its API key \u2014 or \u2191 \u2193 to another agent"),j("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)"));let l=pe.filter(d=>s[d]&&(d!=="codex"||a!==void 0||i===!0));return{lines:r,answers:{agentCli:c,loggedIn:l,...ko(r)?{loginsFor:"cloudflare"}:{}}}}function Oi(e){let{sys:t,state:n,secrets:o}=e,r=ru(e),s=t.env.LOCUSGRAPH_AGENT_SECRET,a=s?.trim(),i=o.locusgraph||(s!==void 0?a:r.LOCUSGRAPH_AGENT_SECRET?.trim()),c=o.locusgraph?"pasted in this run":a?"your environment":"apps/workspace/.env";return{...i?{secret:i,from:c}:{},graph:n.answers.locusgraphGraph||t.env.LOCUSGRAPH_GRAPH_ID?.trim()||r.LOCUSGRAPH_GRAPH_ID?.trim()||void 0,server:t.env.LOCUSGRAPH_SERVER_URL?.trim()||r.LOCUSGRAPH_SERVER_URL||oi}}function Ce(e){let{secret:t,from:n,graph:o,server:r}=Oi(e);return t&&n&&o?{secret:t,from:n,graph:o,server:r}:void 0}function Yr(e){let{secret:t,from:n,graph:o}=Oi(e);if(t&&!o){let r=e.state.answers.tenant??Ie(e.state.answers.owner);return C("LocusGraph",`secret ${U} (${n}), but no graph ID`,e.state.deployed?.memoryKept?`press g to type the graph ID this secret belongs to \u2014 the last deploy used fnmoa-${r}`:"press g to type the graph ID this secret belongs to")}if(!t&&e.state.answers.locusgraphGraph)return C("LocusGraph",`graph ${e.state.answers.locusgraphGraph}, but no secret`,"press e to paste its agent secret \u2014 or g, then Enter on nothing, to go on without LocusGraph")}async function su(e){let{sys:t,state:n}=e,o=Ce(e),r=[],s=Yr(e);if(s)r.push(s);else if(!o)r.push(gn(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","not connected \u2014 memory stays in the workspace process (g for the graph ID, e for its secret)"));else{let c=n.answers.tenant??Ie(n.answers.owner),l=await Rn(t,o,c),d=`secret ${U} (${o.from}), graph ${o.graph}`;r.push(l.read.ok&&l.write.ok?S("LocusGraph",`${d} \u2014 one graph read, one event written`):C("LocusGraph",`${d} \u2014 ${l.read.ok?`the write failed: ${l.write.why}`:`the read failed: ${l.read.why}`}`,"e to paste the secret again, or g to check the graph ID \u2014 then r"))}if(n.target==="cloudflare")return r.push(S("database",`D1 \u2014 ${yt} on your account, made at deploy; the container reaches it over HTTP`)),{lines:r,answers:{runner:"cloudflare"}};let a=n.answers.runner??"node";if(a==="node")return r.push(S("database",`SQLite, ${ce(t,"workspace.db")} \u2014 migrated at deploy (\u2191 \u2193 for Postgres)`)),{lines:r,answers:{runner:a}};let i=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);return r.push(i.ok?S("database","Postgres, with docker compose \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):C("database",i.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:a}}}function nn(e,t){try{let n=JSON.parse(e.read(Fo(e,t))??"");if(n.clientSecret&&n.authSecret)return{clientSecret:n.clientSecret,authSecret:n.authSecret}}catch{}}function Xr(e,t){try{let n=JSON.parse(e.read(Ti(e,t))??"");if(n.hash&&n.authSecret)return{hash:n.hash,authSecret:n.authSecret}}catch{}}function au(e,t=21e4){let n=Pi(16),o=Gd(e,n,t,32,"sha256");return`pbkdf2-sha256$${t}$${n.toString("base64url")}$${o.toString("base64url")}`}async function iu(e,t,n){let o={hash:au(n),authSecret:Xr(e,t)?.authSecret??Pi(32).toString("base64url")},r=Ti(e,t);return await Hd(Kd(r),{recursive:!0,mode:448}),await Fd(r,`${JSON.stringify(o)}
|
|
145
|
-
`,{mode:384}),await
|
|
146
|
-
`),s=Number(o.trim()),a=r.trim();if(!Number.isSafeInteger(s)||s<=1||a===""||e.processOf(s)?.started!==a)return t;let
|
|
147
|
-
`)[0]?.trim()??"",
|
|
136
|
+
`,He=(e,t,n={},o)=>e.exec("sh",["-c",t],{env:n,...o?{timeoutMs:o}:{}});At=(e,t=2)=>e.trim().split(`
|
|
137
|
+
`).slice(-t).join(" \xB7 ").slice(0,300);Dn=async(e,t)=>{let n=await He(e,`tail -n 5 "${St}/logs/${t}.log" 2> /dev/null`);return At(n.stdout,3)},Ho=async e=>{let t=await He(e,`cat "${St}/image" 2> /dev/null`);return t.ok&&t.stdout.trim()||void 0},yi=(e,t,n,o)=>He(e,wd,{MOA_SOURCE:t,MOA_REVISION:n,...o?{GH_TOKEN:o}:{}},18e5),Dr=e=>(e instanceof Error?e.message:String(e)).split(`
|
|
138
|
+
`)[0]?.slice(0,300)??"",ui=e=>new hd({write(t,n,o){e(String(t)),o()}});fi=e=>e.response?.status===404||/not.?found|404/i.test(Dr(e))});import{execFile as $d,execFileSync as $i,spawn as vi}from"node:child_process";import{existsSync as Nr,mkdirSync as vd,openSync as Sd,readFileSync as xd}from"node:fs";import{connect as Ad}from"node:net";import{dirname as Mr,join as Gr,resolve as Ed}from"node:path";import{fileURLToPath as Cd}from"node:url";function Ut(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 Et(e){try{let t=Number(new URL(e).port);return Number.isInteger(t)&&t>0?t:void 0}catch{return}}function Td(e=process.cwd()){let t=Mr(Cd(import.meta.url));for(let n of[e,t]){let o=Ed(n);for(;;){if(Od(o))return o;let r=Mr(o);if(r===o)break;o=r}}}function fn(e=process.env){let t={env:e,root:Td(),exec:(n,o,r={})=>new Promise(s=>{$d(n,o,{cwd:r.cwd,env:r.env??e,timeout:r.timeoutMs??15e3,maxBuffer:16777216},(a,c,i)=>{let l=a?.code;s({ok:a===null,stdout:String(c),stderr:String(i),...l==="ENOENT"?{missing:!0}:{}})})}),run:(n,o,r,s)=>{let a="",c="",i=vi(n,o,{cwd:r.cwd,env:r.env??e,stdio:["ignore","pipe","pipe"]}),l=setTimeout(()=>i.kill("SIGTERM"),r.timeoutMs??9e5);return i.stdout.on("data",u=>{a+=String(u),s(String(u))}),i.stderr.on("data",u=>{c+=String(u),s(String(u))}),{done:new Promise(u=>{i.once("error",f=>{clearTimeout(l),u({ok:!1,stdout:a,stderr:c||f.message,...f.code==="ENOENT"?{missing:!0}:{}})}),i.once("close",f=>{clearTimeout(l),u({ok:f===0,stdout:a,stderr:c})})}),cancel:()=>i.kill("SIGTERM")}},exists:Nr,read:n=>{try{return xd(n,"utf8")}catch{return}},listening:n=>new Promise(o=>{let r=Ad({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=$i("ps",["-o","lstart=,command=","-p",String(n)],{env:{...e,LC_ALL:"C",TZ:"UTC"},encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}),r=Pd.exec(o.trim().split(`
|
|
139
|
+
`)[0]??"");return r?.[1]&&r[2]!==void 0?{started:r[1],command:r[2]}:void 0}catch{return}},portsOf:n=>{try{let r=$i("lsof",["-nP","-a","-p",String(n),"-iTCP","-sTCP:LISTEN","-Fn"],{env:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).split(`
|
|
140
|
+
`).map(s=>_d.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)=>{vd(Mr(r.log),{recursive:!0});let s=Sd(r.log,"a"),a=vi(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=bi({env:e,exec:(n,o)=>t.exec(n,o),fetch:t.fetch,scratch:Gr(pt(e),"..","local")}),t}var Pd,_d,Od,ho=De(()=>{"use strict";Xe();pn();Pd=/^\s*(\w{3} \w{3} [ \d]\d \d\d:\d\d:\d\d \d{4})\s+(.*)$/,_d=/^n.*:(\d+)$/,Od=e=>Nr(Gr(e,"pnpm-workspace.yaml"))&&Nr(Gr(e,"apps","workspace","compose.yaml"))});import{createHash as Id}from"node:crypto";import{join as Un}from"node:path";async function Si(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 Rd(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 Ld(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 Fo(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=Z=>{let Ge=s.findIndex(he=>he.label===Z.label);Ge===-1?s.push(Z):s[Ge]=Z,t([...s])},c,i=(Z,Ge,he)=>(a({label:Z,mark:"fail",detail:Ge,fix:he}),c?{ok:!1,deployed:c}:{ok:!1}),l=o.root;if(!l||Ue(e)||r.deployed?.runner==="image")return Hr(e,t,n);if(hn(e))return i("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=tt(e)?.token,m=Ie(e),y=r.answers.tenant??Ne(r.answers.owner),b=Zt(r),g=Mt(r),w=`http://127.0.0.1:${b}`,v=`http://localhost:${g}`,C=r.answers.owner,N=await en(e);if("why"in N)return i("console sign-in",N.why,N.fix);let W=N.mode==="github"?r.answers.githubApp:void 0,ie=N.mode==="none"?{}:{...N.vars,...N.secrets,AUTH_URL:v};if(d==="compose"&&f.includes("claude")&&!p)return i("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 R=Un(l,"apps","workspace","dist","node.js"),Q=Un(l,"apps","console",".next"),L=o.exists(R),K=o.exists(Un(Q,"BUILD_ID"));if(L&&K)a({label:"build",mark:"ok",detail:"workspace and console are built"});else{if(!K&&o.exists(Q))return i("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 Z=[...L?[]:[{what:"workspace",args:["--filter","@fnmoa/workspace...","run","build"]}],...K?[]:[{what:"console",args:["--filter","@fnmoa/console","run","build"]}]],Ge=Z.map(Ve=>Ve.what).join(" and ");a({label:"build",mark:"wait",detail:`building the ${Ge} \u2014 a few minutes`});let he=await o.exec("pnpm",["install"],{cwd:l,timeoutMs:6e5});if(!he.ok)return i("build",`pnpm install: ${Mn(he.stderr||he.stdout)}`,`run \`pnpm install\` in ${l}, then Enter`);for(let Ve of Z){let ht=await o.exec("pnpm",Ve.args,{cwd:l,timeoutMs:9e5});if(!ht.ok)return i("build",`${Ve.what}: ${Mn(ht.stderr||ht.stdout)}`,`run \`pnpm ${Ve.args.join(" ")}\` in ${l}, then Enter`)}a({label:"build",mark:"ok",detail:`${Ge} built`})}let{token:re,pending:ye,before:te,had:$e}=await Yt(o,Nn,w);a({label:"owner token",mark:"ok",detail:`${D} \u2014 profile ${Nn} in ~/.moa/config.json${te===Nn?"":$e?`; \`moa\` now talks to this workspace, not profile ${te} (\`moa use ${te}\` to go back)`:"; `moa` now talks to this workspace"}`});let $=U({url:w,token:re},o.fetch),I=r.deployed?jn(o,r.deployed):void 0,se=Dd({runner:d,workspaceUrl:w,tenant:y,token:re,...r.answers.repo?{repo:r.answers.repo}:{},...C?{owner:C}:{},...W?{githubClientId:W.clientId}:{},...m?{locusgraph:m.secret,graph:m.graph,server:m.server}:{},...p?{claude:p}:{}}),T=I?.workspacePid!==void 0&&Ut(o,I.workspacePid,I.workspaceStarted,Et(I.workspaceUrl))?I.workspacePid:void 0,z=T?I?.workspaceStarted:void 0,ke=I?.workspaceFor,ne=I?.consolePid!==void 0&&Ut(o,I.consolePid,I.consoleStarted,Et(I.consoleUrl))?I.consolePid:void 0,Pe=ne?I?.consoleStarted:void 0,_e=I?.consoleFor,Ke=I?.memoryKept,Me=I?.workspaceUrl===w?I?.people:void 0,ze=()=>{c={runner:d,workspaceUrl:w,consoleUrl:v,...T?{workspacePid:T,...z?{workspaceStarted:z}:{}}:{},...ke?{workspaceFor:ke}:{},...ne?{consolePid:ne,...Pe?{consoleStarted:Pe}:{},..._e?{consoleFor:_e}:{}}:{},...Me?{people:Me}:{},...Ke?{memoryKept:Ke}:{},at:new Date().toISOString()},n(c)};if((T||ne)&&ze(),T!==void 0&&ke!==se){if(a({label:"workspace",mark:"wait",detail:"restarting \u2014 it was started with other answers"}),o.stop(T),!await Ld(o,T,15))return i("workspace",`the old workspace (pid ${T}) is still running`,`stop it (\`kill ${T}\`), then Enter`);T=void 0,z=void 0,ke=void 0,ze()}let ve=await $.get("/v1/whoami").then(()=>"ours",Z=>Z instanceof ue?"other":"down");if(ve==="other")return i("workspace",`something else answers on :${b} and refuses this token`,"stop it, or run `moa setup --workspace-port <port>`");if(ve==="ours"&&ke!==se&&d==="node")return i("workspace",`the workspace on :${b} was not started with these answers`,I?.runner==="compose"?`run \`docker compose -p ${be} down\`, then Enter`:"stop it, then Enter");if(ve==="ours"&&ke===se)a({label:"workspace",mark:"ok",detail:`${w} \u2014 already running`});else{a({label:"workspace",mark:"wait",detail:`starting on :${b}`});let Z={...o.env,...await zt(e),MOA_TOKENS:`${re}:${y}`,PORT:String(b),MOA_PORT:String(b),MOA_CONSOLE_PORT:String(g),...r.answers.repo?{MOA_REPO:r.answers.repo}:{},...C?{MOA_OWNER_LOGIN:C}:{},...W?{MOA_GITHUB_CLIENT_ID:W.clientId}:{},...m?{LOCUSGRAPH_AGENT_SECRET:m.secret,LOCUSGRAPH_GRAPH_ID:m.graph,LOCUSGRAPH_SERVER_URL:m.server}:{}};for(let he of["MOA_DATABASE_URL","MOA_SQLITE_FILE","MOA_TOKEN_FILE","MOA_WORKSPACE_ROOT","MOA_SELF_URL",...C?[]:["MOA_OWNER_LOGIN"],...W?[]:["MOA_GITHUB_CLIENT_ID"]])delete Z[he];if(d==="compose"){for(let Ve of["MOA_CLI_HOME","CLAUDE_CODE_OAUTH_TOKEN","ANTHROPIC_API_KEY"])delete Z[Ve];p&&(Z.CLAUDE_CODE_OAUTH_TOKEN=p);let he=await o.exec("docker",Dt(l,"up","-d","--build"),{cwd:l,env:Z,timeoutMs:12e5});if(!he.ok)return i("workspace",`docker compose: ${Mn(he.stderr||he.stdout)}`,"fix that, then Enter");T=void 0,z=void 0,ke=se,Ke=!!m,ze()}else{let he=pe(o,"workspace.db");T=o.start(process.execPath,[R],{cwd:Un(l,"apps","workspace"),env:{...Z,...p?{CLAUDE_CODE_OAUTH_TOKEN:p}:{},MOA_DATABASE_URL:`sqlite:${he}`,MOA_D1_ACCOUNT_ID:"",MOA_D1_DATABASE_ID:"",MOA_D1_API_TOKEN:"",MOA_SQLITE_FILE:he,MOA_WORKSPACE_ROOT:pe(o,"worktrees"),MOA_SELF_URL:w,MOA_PID_FILE:Fr(o)},log:pe(o,"workspace.log")}),z=o.processOf(T)?.started,ke=se,Ke=!!m,ze()}if(!await Si(o,`${w}/health`,d==="compose"?120:60))return i("workspace",`did not answer on :${b}`,d==="compose"?`look at \`docker compose -p ${be} logs workspace\`, then Enter`:`look at ${pe(o,"workspace.log")}, then Enter`);if(!await $.get("/v1/whoami").then(()=>!0,()=>!1))return i("workspace","it started but refuses the owner token","Enter to try again");a({label:"workspace",mark:"ok",detail:`${w}${T?` (pid ${T})`:""}`})}ye&&await Xt(o,Nn,w,re);let Le=await jr(o,w,a,d==="compose"?"postgres":"sqlite",d==="compose"?`look at \`docker compose -p ${be} logs\`, then Enter`:`look at ${pe(o,"workspace.log")}, then Enter`);if(Le)return i(Le.label,Le.detail,Le.fix);let O=await mn($,r,a,Me);if(O)return i(O.label,O.detail,O.fix);let F=Me;Me=r.answers.people??[],JSON.stringify(Me)!==JSON.stringify(F??[])&&ze();let Be=Ud(w,re,ie),rt=ne!==void 0&&await o.listening(g);if(rt&&ne!==void 0&&_e!==Be){if(a({label:"console",mark:"wait",detail:"restarting \u2014 it points at another workspace"}),o.stop(ne),ne=void 0,Pe=void 0,_e=void 0,ze(),!await Rd(o,g,15))return i("console",`the old console still holds :${g}`,"stop it, then Enter");rt=!1}if(rt)a({label:"console",mark:"ok",detail:`${v} \u2014 already running`});else{if(await o.listening(g))return i("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}`}),ne=o.start(process.execPath,[Un(l,"apps","console","node_modules","next","dist","bin","next"),"start","--port",String(g),"--hostname","127.0.0.1"],{cwd:Un(l,"apps","console"),env:{...Nd(o.env),MOA_URL:w,MOA_TOKEN:re,...ie},log:pe(o,"console.log")}),Pe=o.processOf(ne)?.started,_e=Be,ze(),!await Si(o,v,60))return i("console",`did not answer on :${g}`,`look at ${pe(o,"console.log")}, then Enter`);a({label:"console",mark:"ok",detail:`${v} (pid ${ne})${W?" \u2014 Sign in with GitHub":""}`})}let st=await Nt($,f,d==="compose"?"in the container":"on this machine",Z=>d==="compose"?"Esc back to Agent logins and log it in again, then Enter":`is ${Z} logged in on this machine? Run \`${Z}\` once and log in, then Enter`,a);if("label"in st)return i(st.label,st.detail,st.fix);let gt=st.runs[u],de=await gn($,a);if("label"in de)return i(de.label,de.detail,de.fix);let vn=de.memory;return de.live!==void 0&&(Ke=de.live),a({label:"Console",mark:"ok",detail:v}),a({label:"Workspace",mark:"ok",detail:w}),a({label:"Next",mark:"skip",detail:`moa whoami \xB7 moa agent list \xB7 moa run ${Gn(u)} "\u2026"`}),{ok:!0,deployed:{runner:d,workspaceUrl:w,consoleUrl:v,...T?{workspacePid:T,...z?{workspaceStarted:z}:{}}:{},...ke?{workspaceFor:ke}:{},...ne?{consolePid:ne,...Pe?{consoleStarted:Pe}:{},..._e?{consoleFor:_e}:{}}:{},...gt?{proofRunId:gt}:{},memory:vn,people:Me,...Ke?{memoryKept:Ke}:{},at:new Date().toISOString()}}}async function jr(e,t,n,o,r){let s=await e.fetch(`${t}/health`,{signal:AbortSignal.timeout(5e3)}).then(c=>c.json()).catch(c=>({ok:!1,error:String(c)})),a=s.database??o;if(s.ok!==!0)return{label:"database",detail:`${a} does not answer: ${Mn(String(s.error??"no answer"),1)}`,fix:r};n({label:"database",mark:"ok",detail:`${a} \u2014 migrated, and it answers`})}async function mn(e,t,n,o=[]){let r=t.answers.people??[],s=r.filter(i=>!o.some(l=>Md(l,i)));for(let i of s){n({label:"people",mark:"wait",detail:`adding ${i.login}`});try{await e.put(`/v1/access/people/${encodeURIComponent(i.login)}`,{role:i.role,dailyCapUsd:i.dailyCapUsd})}catch(l){let d=l instanceof Error?l.message:String(l);return{label:"people",detail:`${i.login}: ${d}`,fix:l instanceof ue&&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(i=>!r.some(l=>l.login.toLowerCase()===i));for(let i of a){n({label:"people",mark:"wait",detail:`removing ${i}`});try{await e.del(`/v1/access/people/${encodeURIComponent(i)}`)}catch(l){if(l instanceof ue&&l.status===404)continue;let d=l instanceof Error?l.message:String(l);return{label:"people",detail:`removing ${i}: ${d}`,fix:"Enter to try again"}}}let c=r.length-s.length;(r.length>0||a.length>0)&&n({label:"people",mark:"ok",detail:[...s.map(i=>`${i.login} (${i.role})`),...a.map(i=>`${i} removed`),...c>0?[`${c} as before \u2014 left as the Access page has them`]:[]].join(", ")})}async function Nt(e,t,n,o,r){let s={};for(let a of t){let c=`proof: ${a} run`,i=Gn(a);r({label:c,mark:"wait",detail:`${a} is answering ${n}`});try{if(!await e.post("/v1/agents",{name:i,cli:a,role:"Proves moa setup worked. Answers in one word."}).then(()=>!0,p=>{if(!(p instanceof ue&&p.status===409))throw p;return!1})){let p=await e.get(`/v1/agents/${i}`);if(p.cli!==a)return{label:c,detail:`agent ${i} already exists and runs ${p.cli??"another CLI"}`,fix:`run \`moa agent rm ${i}\`, then Enter`}}let d=await e.post(`/v1/agents/${i}/runs`,{prompt:In}),u=await Bt(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:c,detail:Mn(String(p),2),fix:o(a)}}s[a]=f.runId,r({label:c,mark:"ok",detail:`${a} answered "${Mn(f.result,1).slice(0,40)}" ${n} \u2014 moa logs ${f.runId}`})}catch(l){return{label:c,detail:l instanceof Error?l.message:String(l),fix:"Enter to try again"}}}return{runs:s}}async function gn(e,t){try{let n=await e.get("/v1/memory"),o=(n.types??[]).reduce((c,i)=>c+(i.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 Nn,Gn,Mn,xi,Dd,Ud,Nd,Md,Qt=De(()=>{"use strict";je();An();Qe();ct();Lt();Vt();Ln();Xe();ho();Nn="local",Gn=e=>`setup-proof-${e}`,Mn=(e,t=3)=>e.trim().split(`
|
|
141
|
+
`).slice(-t).join(" \xB7 ").slice(0,300);xi=e=>Id("sha256").update(e).digest("hex").slice(0,12),Dd=e=>xi(JSON.stringify([e.runner,e.workspaceUrl,e.tenant,e.repo??"",e.token,e.locusgraph??"",e.owner??"",e.githubClientId??"",e.graph??"",e.server??"",e.claude??""])),Ud=(e,t,n)=>`${e} ${xi(JSON.stringify([t,n.AUTH_GITHUB_ID??"",n.AUTH_GITHUB_SECRET??"",n.AUTH_SECRET??"",n.MOA_CONSOLE_PASSWORD_HASH??""]))}`,Nd=e=>Object.fromEntries(Object.entries(e).filter(([t])=>!t.startsWith("AUTH_")&&!t.startsWith("MOA_CONSOLE_")));Md=(e,t)=>e.login.toLowerCase()===t.login.toLowerCase()&&e.role===t.role&&e.dailyCapUsd===t.dailyCapUsd});var Jr={};Oc(Jr,{artifactRoot:()=>Hn,bundledWorker:()=>Kr,crashOf:()=>_i,cursorPrompt:()=>Ko,deployImage:()=>Hr,imageChecks:()=>Fn,imageCompose:()=>nt,imageComposePath:()=>tn,imageConfig:()=>Pi,loggedInImage:()=>nn,loginInImage:()=>Kn,logsInInContainer:()=>wo,releaseImage:()=>Re,waiting:()=>Bn,workerFromImage:()=>Br});import{chmod as Gd,mkdir as Ai,readFile as jd,writeFile as Hd}from"node:fs/promises";import{dirname as Ei,join as Ci}from"node:path";import{fileURLToPath as Fd}from"node:url";async function Br(e){let t=await e.exec("docker",["create","--platform","linux/amd64",Re()]);if(!t.ok)return{ok:!1,why:le(t.stderr)||"docker create failed"};let n=le(t.stdout),o=pe(e,Ci("cloudflare","worker.js"));await Ai(Ei(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:le(r.stderr)||"the image holds no worker.js"}}async function Fn(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"],Re()],{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(`
|
|
142
|
+
`)[0]} \u2014 ${o}`},{label:"image",mark:n.ok?"ok":"fail",detail:Re(),...n.ok?{}:{fix:"Sign in to ghcr.io with your account's package pull token, then check again."}}]}function Pi(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`}}:{}}}}function Ko(e){let t=/https:\/\/cursor\.com\/\S+/.exec(e);return t?{url:t[0]}:{}}async function nn(e,t){if(!e.exists(tn(e)))return!1;let n=Wr[t],o=await e.exec("docker",nt(e,"exec","-T","workspace",...n.status),{timeoutMs:6e4});return n.loggedIn(o)}function Kn(e,t,n){let o=Wr[t],r="";return e.run("docker",nt(e,"exec","-T",...o.env,"workspace",...o.login),{timeoutMs:16*6e4},s=>{r+=s,n(o.prompt(r))})}function _i(e){return e.split(`
|
|
143
|
+
`).map(n=>n.trim()).reverse().find(n=>/^[A-Z]\w*(Error|Exception)\b.*:/.test(n))}async function Hr(e,t,n){let o=[],r=$=>{o.push($),t([...o])},s=$=>(r({label:"image",mark:"fail",detail:$}),{ok:!1}),a=await vt(e);for(let $ of a)r($);if(a.some($=>$.mark==="fail"))return{ok:!1};let c=await mo(e.sys.env,"local");if(!c)return s("Activate the local install first.");for(let $ of await Fn(e))if(r($),$.mark==="fail")return{ok:!1};let{sys:i,state:l}=e,d={};if(l.deployed?.runner==="image"){let $=JSON.parse(await jd(tn(i),"utf8"));d=Object.fromEntries(Object.entries($.services.workspace.environment).map(([I,se])=>[I,se.replaceAll("$$","$")]))}let u=`http://127.0.0.1:${Zt(l)}`,f=`http://localhost:${Mt(l)}`,{token:p}=await Yt(i,"local",u),m=await en(e);if("why"in m)return s(`The console cannot sign anyone in: ${m.why}. ${m.fix}.`);let y=await i.exec("gh",["auth","token"]),b={...d,...await zt(e),MOA_TOKENS:`${p}:${l.answers.tenant??Ne(l.answers.owner)}`,...l.answers.owner?{MOA_OWNER_LOGIN:l.answers.owner}:{},...l.answers.repo?{MOA_REPO:l.answers.repo}:{},...y.ok?{GH_TOKEN:y.stdout.trim()}:{}},g=tt(e)?.token;g&&(b.CLAUDE_CODE_OAUTH_TOKEN=g);for(let $ of we){let I=Ee(e,$);I&&(b[Te[$]]=I)}let w=l.answers.loggedIn;if(w)for(let $ of we){if(w.includes($)||Ee(e,$))continue;let I=[Te[$],...$==="claude"&&!g?["CLAUDE_CODE_OAUTH_TOKEN"]:[]];for(let se of I)delete b[se]}let v=Ie(e);v&&Object.assign(b,{LOCUSGRAPH_AGENT_SECRET:v.secret,LOCUSGRAPH_GRAPH_ID:v.graph,LOCUSGRAPH_SERVER_URL:v.server});for(let $ of Oi)delete b[$];m.mode!=="none"&&Object.assign(b,m.vars,m.secrets,{AUTH_URL:f});let C=tn(i),N=$=>typeof $=="string"?$.replaceAll("$",()=>"$$"):Array.isArray($)?$.map(N):$&&typeof $=="object"?Object.fromEntries(Object.entries($).map(([I,se])=>[I,N(se)])):$,W=Pi({image:Re(),install:c.install,workspacePort:Zt(l),consolePort:Mt(l),postgres:l.answers.runner==="compose",env:b});if(await Ai(Ei(C),{recursive:!0,mode:448}),await Hd(C,JSON.stringify(N(W)),{mode:384}),await Gd(C,384),!(await i.exec("docker",nt(i,"up","-d","--wait"),{timeoutMs:6e5})).ok)return s("Docker Compose did not start the image. Run `moa setup logs` for details.");let R={runner:"image",workspaceUrl:u,consoleUrl:f,image:Re(),at:new Date().toISOString()};n(R);let Q=U({url:u,token:p},i.fetch),L=!1;for(let $=0;$<60;$++)try{await Q.get("/v1/whoami"),L=!0;break}catch{await i.sleep(1e3)}if(!L){let $=await i.exec("docker",nt(i,"logs","--tail","40","workspace"),{timeoutMs:3e4}),I=_i(`${$.stdout}
|
|
144
|
+
${$.stderr}`);return{...s(I?`The workspace did not become ready: ${I}. \`moa setup logs\` has the rest.`:"The workspace did not become ready. `moa setup logs` says why."),deployed:R}}await Xt(i,"local",u,p);let K=await mn(Q,l,r,l.deployed?.people);if(K)return{...s(K.detail),deployed:R};let re=l.answers.agentCli??"claude",ye=[...new Set([re,...l.answers.loggedIn??[]])];for(let $ of ye){if(!wo($)||Ee(e,$)||await nn(i,$))continue;r({label:$,mark:"wait",detail:"starting its login inside the container\u2026"});let se=await Kn(i,$,T=>{let z=Bn(T);z&&r({label:$,mark:"wait",detail:z})}).done;if(!se.ok||!await nn(i,$))return r({label:$,mark:"fail",detail:`the login did not finish: ${le(se.stderr)||"no answer"}`,fix:"Enter to log in again \u2014 or Esc back to Agent logins and \u2191 \u2193 to choose another agent"}),{ok:!1,deployed:R};r({label:$,mark:"ok",detail:"logged in inside the container \u2014 kept on its home volume"})}let te=await Nt(Q,ye,"in the image",$=>$==="claude"?"Esc back to Agent logins: t to connect your Claude subscription, or export ANTHROPIC_API_KEY and run setup again.":`\`moa setup login ${$}\` to log it in again, or export ${Te[$]} and run setup again.`,r);if("label"in te)return{...s(te.detail),deployed:R};let $e=await gn(Q,r);return"label"in $e?{...s($e.detail),deployed:R}:(r({label:"console",mark:"ok",detail:f}),{ok:!0,deployed:{...R,people:l.answers.people,memory:$e.memory,memoryKept:$e.live}})}var Re,Hn,Kr,tn,nt,Wr,wo,Bn,ct=De(()=>{"use strict";je();Qe();Qt();Lt();Vt();Ln();Xe();Re=()=>`ghcr.io/fnlog0/fnmoa:${qt()}`,Hn=()=>Fd(new URL(import.meta.url.endsWith("/main.js")?"../":"../../",import.meta.url)),Kr=()=>Ci(Hn(),"dist/assets/worker.js"),tn=e=>pe(e,"image/compose.json"),nt=(e,...t)=>["compose","--project-name","fnmoa-image","--file",tn(e),...t];Wr={codex:{status:["codex","login","status"],login:["codex","login","--device-auth"],env:[],loggedIn:e=>e.ok,prompt:dn},cursor:{status:["cursor-agent","status"],login:["cursor-agent","login"],env:["-e","NO_OPEN_BROWSER=1"],loggedIn:e=>e.ok&&!/not logged in/i.test(`${e.stdout}${e.stderr}`),prompt:Ko}},wo=e=>e in Wr;Bn=e=>e.url?e.code?`open ${e.url} and enter ${e.code} \u2014 waiting for you to approve it`:`open ${e.url} and sign in \u2014 waiting for you to approve it`:void 0});import{pbkdf2Sync as Kd,randomBytes as Ii}from"node:crypto";import{chmod as Bd,mkdir as Wd,writeFile as Jd}from"node:fs/promises";import{dirname as qd,join as zr}from"node:path";async function wn(e,t,n,o=["--version"]){let r=await e.exec(t,o);return r.ok?S(t,yo(r.stdout)):P(t,"not found",n)}function Yd(e=process){return e.platform==="darwin"&&e.env.MOA_KEEP_AWAKE!=="0"?S("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"):S("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 Xd(e,t=process){switch(e.target){case"local":return{lines:[S("This machine","Docker or local, nothing in the cloud"),Yd(t)]};case"cloudflare":return{lines:[S("Cloudflare","a Worker and its container on D1, on your own domain \u2014 Workers Paid plan")]};case"vercel":return{lines:[S("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&&Ut(e.sys,o,r,n)?S(`port ${n}`,`the ${t} the last setup started`):P(`port ${n}`,"something else is listening there",`stop it, or run \`moa setup ${s} <port>\``):S(`port ${n}`,`free for the ${t}`)}async function Ri(e){return[Number(process.versions.node.split(".")[0])>=22?S("node",process.versions.node):P("node",process.versions.node,"install node 22 or newer"),...await Promise.all([wn(e,"pnpm","install pnpm: `corepack enable`"),wn(e,"git","install git"),wn(e,"gh","install the GitHub CLI: https://cli.github.com")])]}function Yr(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 Xr(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 Qd(e){let{sys:t,state:n}=e,o=await Ri(t);if(Ue(e)){let{imageChecks:u}=await Promise.resolve().then(()=>(ct(),Jr));o.push(...await u(e))}else{let u=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);o.push(u.ok?S("docker",`${le(u.stdout)} \u2014 builds the container image from this checkout`):P("docker",u.missing?"not installed":"not running","wrangler builds the container image with Docker: start it, then r"))}let s=t.root??Hn();o.push(S(t.root?"checkout":"release",s));let a=await Je(t,s,["--version"],{timeoutMs:3e4});if(!a.ok)return o.push(t.root?P("wrangler","not installed in this checkout",`run \`pnpm install\` in ${s}, then r`):P("wrangler",`\`npx ${Co}\` did not run: ${le(a.stderr)||"no answer"}`,"check Node and npm can reach the npm registry, then r")),{lines:o};o.push(S("wrangler",yo(a.stdout)));let c=await wa(t,s),i;c.ok?(i=c.account.id,o.push(S("account",`${c.account.name} (${c.account.id})${c.email?` as ${c.email}`:""}${c.others>0?" \u2014 CLOUDFLARE_ACCOUNT_ID picks another":""}`))):o.push(P("account",c.why,c.loggedOut?`run \`pnpm --dir ${zr(s,"apps","workspace")} exec wrangler login\`, then r`:"fix that, then r"));let l=n.answers.domain;if(!l)o.push(P("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 ka(t,l),f=ro(l);o.push(u.zone&&u.onCloudflare?S("domain",`${f.consoleUrl} and ${f.workspaceUrl} \u2014 zone ${u.zone}`):P("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=Yr(e);if(!d)o.push(Vr(n)?H("D1 token","kept \u2014 the deployed Worker has it (k to replace it)"):P("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(i){let u=await ya(t,i,d.token);o.push(u.ok?S("D1 token",`${D} (${d.from}) \u2014 D1 answers to it`):P("D1 token",`${D} (${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(H("cost",`Workers Paid, plus the container (standard-4) while it runs \u2014 up to ${It}h after the last request`)),{lines:o,...i?{answers:{cloudflareAccount:i}}:{}}}async function Zd(e){let{sys:t,state:n}=e;if(Ue(e)&&n.target==="local"){let{imageChecks:a}=await Promise.resolve().then(()=>(ct(),Jr));return{lines:await a(e)}}if(n.target==="cloudflare")return Qd(e);let o=await Ri(t),r=n.deployed?jn(t,n.deployed):void 0,s=await t.exec("docker",["--version"]);return o.push(s.ok?S("docker",yo(s.stdout)):H("docker","not found \u2014 only needed for Postgres with docker compose")),o.push(t.root?S("checkout",t.root):P("checkout","not inside a fnMoa checkout","run moa setup from a clone of https://github.com/fnLog0/fnmoa")),o.push(await Ti(e,"workspace",Zt(n),r?.workspacePid,r?.workspaceStarted,"--workspace-port"),await Ti(e,"console",Mt(n),n.deployed?.consolePid,n.deployed?.consoleStarted,"--console-port"),H("account","this machine \u2014 no account needed")),{lines:o}}async function eu(e){let{sys:t,state:n}=e,o=n.target==="cloudflare"?{...t.env}:void 0;if(o)for(let i of["GH_TOKEN","GITHUB_TOKEN"])delete o[i];let r=await t.exec("gh",["api","user","--jq",".login"],o?{env:o}:{}),s=le(r.stdout);if(!r.ok||!s)return{lines:[P("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 nu(e,s);let a=[S("gh login",`${s} \u2014 on this machine the workspace uses your own login`)],c=n.answers.repo;if(!c)a.push(H("repo","none \u2014 a pipeline can name any repository later (e to set one)"));else{let i=await t.exec("gh",["repo","view",c,"--json","viewerPermission","--jq",".viewerPermission"]),l=le(i.stdout);a.push(i.ok&&["ADMIN","MAINTAIN","WRITE"].includes(l)?S("repo",`${c} \u2014 ${l.toLowerCase()}`):P("repo",i.ok?`${s} cannot push to ${c}`:`${c} 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 tu(e,t,n,o){let r=await e.exec("gh",["repo","view",t,"--json","viewerPermission","--jq",".viewerPermission"],o?{env:o}:{}),s=le(r.stdout);return r.ok&&["ADMIN","MAINTAIN","WRITE"].includes(s)?S("repo",`${t} \u2014 ${s.toLowerCase()} for ${n}`):P("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 nu(e,t){let{sys:n,state:o}=e,r=[S("gh login",`${t} \u2014 you, the first owner`)],s=Xr(e),a;if(!s)r.push(Vr(o)?H("token","kept \u2014 the deployed Worker has it (k to replace it)"):P("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 c={...n.env,GH_TOKEN:s.token};for(let d of["GITHUB_TOKEN"])delete c[d];let i=await n.exec("gh",["api","user","--jq",".login"],{env:c});a=le(i.stdout)||void 0,r.push(i.ok&&a?S("token",`${D} (${s.from}) \u2014 GitHub sees ${a}`):P("token",`${D} (${s.from}) \u2014 GitHub refused it`,"press k to paste another"));let l=o.answers.repo;l&&a&&r.push(await tu(n,l,a,c))}return o.answers.repo||r.push(H("repo","none \u2014 a pipeline can name any repository later (e to set one)")),{lines:r,answers:{owner:t}}}function tt(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 ru(e){let{sys:t,state:n}=e;if(Ue(e)&&n.target==="local")return su(e);if(n.target==="cloudflare")return iu(e);let o=ou(n),[r,s]=await Promise.all([t.exec("claude",["--version"]),t.exec("codex",["--version"])]),a=tt(e),c={claude:r.ok,codex:s.ok,cursor:!1},i=[o?S("agents run","in the container \u2014 only what is carried in here reaches it"):H("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 oi(t,t.root):void 0;if(d&&!d.ok)c.codex=!1,l=P("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 si(t,t.root);c.codex=m.loggedIn,l=m.loggedIn?S("codex",`${m.says} \u2014 its own session in the container (l to log in again)`):m.broken?P("codex",`the container did not run: ${m.broken}`,"start Docker, then press r"):H("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;c.codex=s.ok&&m?.ok===!0,l=s.ok?m?.ok?S("codex",`${yo(s.stdout)} \u2014 ${le(m.stdout)||"logged in"}`):H("codex","not logged in \u2014 run `codex login`, then press r"):H("codex","not found")}let u;if(a){let m=await Ir(t,a.token,o&&t.root?{container:t.root}:"here");c.claude=m.ok,u=m.ok?S("claude",`token ${D} (${a.from}) \u2014 "reply ok" ${o?"in the container":"on it alone"}`):P("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?(c.claude=!1,u=H("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?S("claude",`${yo(r.stdout)} \u2014 your existing login (t to carry a token instead)`):H("claude","not found");i.push(u,l);let f=e.state.answers.agentCli??(c.claude?"claude":c.codex?"codex":void 0);f?i.push(c[f]?S("proof run",`uses ${f} (\u2191 \u2193 to change)`):P("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")):i.push(P("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")),i.push(H("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)"));let p=["claude","codex"].filter(m=>c[m]);return{lines:i,answers:{...f?{agentCli:f}:{},loggedIn:p,...bo(i)?{loginsFor:o?"compose":"node"}:{}}}}async function su(e){let{sys:t,state:n}=e,o=n.deployed?.runner==="image",r=tt(e),s=new Set,a=[S("agents run","in the image's container \u2014 only what is carried in reaches it")];r?(s.add("claude"),a.push(S("claude",`token ${D} (${r.from}) \u2014 Deploy proves it in the image`))):Ee(e,"claude")?(s.add("claude"),a.push(S("claude",`ANTHROPIC_API_KEY ${D} (${ko(e,"claude")})`))):o&&n.answers.loggedIn?.includes("claude")?(s.add("claude"),a.push(H("claude","kept \u2014 the running container has its login (t to replace it)"))):a.push(H("claude","not connected \u2014 t for your Claude subscription (`claude setup-token`), or export ANTHROPIC_API_KEY"));let c={codex:"ChatGPT",cursor:"Cursor"};for(let l of["codex","cursor"])Ee(e,l)?(s.add(l),a.push(S(l,`${Te[l]} ${D} (${ko(e,l)})`))):o&&await nn(t,l)?(s.add(l),a.push(S(l,"logged in inside the container \u2014 kept on its home volume"))):a.push(H(l,`logs in at Deploy when chosen \u2014 or later, \`moa setup login ${l}\` with your ${c[l]} account`));let i=n.answers.agentCli??(s.has("claude")?"claude":s.values().next().value??"codex");return a.push(i!=="claude"||s.has("claude")?S("proof run",`uses ${i} (\u2191 \u2193 to change)`):P("proof run","claude has no login for the image","press t to connect your Claude subscription \u2014 or \u2191 \u2193 to choose codex or cursor")),{lines:a,answers:{agentCli:i,loggedIn:we.filter(l=>s.has(l))}}}async function au(e){let t=e.state.deployed;if(t?.runner!=="cloudflare")return;let n=await Oe(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 iu(e){let{sys:t,state:n}=e,o=tt(e),r=[S("agents run","in the Cloudflare container \u2014 only what is carried in reaches it")],s={claude:!1,codex:!0,cursor:!1};if(o){let d=await Ir(t,o.token,"here");s.claude=d.ok,r.push(d.ok?S("claude",`token ${D} (${o.from}) \u2014 "reply ok" on it alone; a Worker secret`):P("claude",`the token did not answer: ${d.why}`,"press c to make a new one with `claude setup-token`, or k for an API key"))}else Ee(e,"claude")?(s.claude=!0,r.push(S("claude",`ANTHROPIC_API_KEY ${D} (${ko(e,"claude")}) \u2014 a Worker secret`))):Vr(n)&&n.answers.loggedIn?.includes("claude")?(s.claude=!0,r.push(H("claude","kept \u2014 the deployed Worker has the token (c to replace it)"))):r.push(H("claude","not connected \u2014 c for your Claude subscription, or k for an API key"));let a=Ee(e,"codex"),c=a?void 0:await au(e);r.push(a?S("codex",`OPENAI_API_KEY ${D} (${ko(e,"codex")}) \u2014 a Worker secret`):c===!0?S("codex","logged in inside the container \u2014 its login is kept in R2 across restarts"):H("codex",c===!1?"not logged in inside the container \u2014 Deploy shows a link and a code (or k for an API key)":"logs in inside the container at Deploy \u2014 a link and a code (or k for an API key)")),s.cursor=Ee(e,"cursor")!==void 0,r.push(s.cursor?S("cursor",`CURSOR_API_KEY ${D} (${ko(e,"cursor")}) \u2014 a Worker secret`):H("cursor","not connected \u2014 on Cloudflare cursor runs on an API key: k"));let i=n.answers.agentCli??(s.claude?"claude":"codex");r.push(s[i]?S("proof run",`uses ${i} (\u2191 \u2193 to change)`):P("proof run",`${i} is not connected`,"press c to connect it or k for its API key \u2014 or \u2191 \u2193 to another agent"),H("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)"));let l=we.filter(d=>s[d]&&(d!=="codex"||a!==void 0||c===!0));return{lines:r,answers:{agentCli:i,loggedIn:l,...bo(r)?{loginsFor:"cloudflare"}:{}}}}function Li(e){let{sys:t,state:n,secrets:o}=e,r=cu(e),s=t.env.LOCUSGRAPH_AGENT_SECRET,a=s?.trim(),c=o.locusgraph||(s!==void 0?a:r.LOCUSGRAPH_AGENT_SECRET?.trim()),i=o.locusgraph?"pasted in this run":a?"your environment":"apps/workspace/.env";return{...c?{secret:c,from:i}:{},graph:n.answers.locusgraphGraph||t.env.LOCUSGRAPH_GRAPH_ID?.trim()||r.LOCUSGRAPH_GRAPH_ID?.trim()||void 0,server:t.env.LOCUSGRAPH_SERVER_URL?.trim()||r.LOCUSGRAPH_SERVER_URL||ii}}function Ie(e){let{secret:t,from:n,graph:o,server:r}=Li(e);return t&&n&&o?{secret:t,from:n,graph:o,server:r}:void 0}function Qr(e){let{secret:t,from:n,graph:o}=Li(e);if(t&&!o){let r=e.state.answers.tenant??Ne(e.state.answers.owner);return P("LocusGraph",`secret ${D} (${n}), but no graph ID`,e.state.deployed?.memoryKept?`press g to type the graph ID this secret belongs to \u2014 the last deploy used fnmoa-${r}`:"press g to type the graph ID this secret belongs to")}if(!t&&e.state.answers.locusgraphGraph)return P("LocusGraph",`graph ${e.state.answers.locusgraphGraph}, but no secret`,"press e to paste its agent secret \u2014 or g, then Enter on nothing, to go on without LocusGraph")}async function lu(e){let{sys:t,state:n}=e,o=Ie(e),r=[],s=Qr(e);if(s)r.push(s);else if(!o)r.push(hn(e)?P("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?H("LocusGraph","no secret \u2014 memory will not be kept in LocusGraph; it stays in the workspace process"):H("LocusGraph","not connected \u2014 memory stays in the workspace process (g for the graph ID, e for its secret)"));else{let i=n.answers.tenant??Ne(n.answers.owner),l=await Rn(t,o,i),d=`secret ${D} (${o.from}), graph ${o.graph}`;r.push(l.read.ok&&l.write.ok?S("LocusGraph",`${d} \u2014 one graph read, one event written`):P("LocusGraph",`${d} \u2014 ${l.read.ok?`the write failed: ${l.write.why}`:`the read failed: ${l.read.why}`}`,"e to paste the secret again, or g to check the graph ID \u2014 then r"))}if(n.target==="cloudflare")return r.push(S("database",`D1 \u2014 ${kt} on your account, made at deploy; the container reaches it over HTTP`)),{lines:r,answers:{runner:"cloudflare"}};let a=n.answers.runner??"node";if(a==="node")return r.push(S("database",`SQLite, ${pe(t,"workspace.db")} \u2014 migrated at deploy (\u2191 \u2193 for Postgres)`)),{lines:r,answers:{runner:a}};let c=await t.exec("docker",["info","--format","{{.ServerVersion}}"]);return r.push(c.ok?S("database","Postgres, with docker compose \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):P("database",c.missing?"docker is not installed":"docker is not running","start Docker, or \u2191 \u2193 to choose SQLite")),r.push(H("agent logins","in the container \u2014 Agent logins, next, carries them in and proves them")),{lines:r,answers:{runner:a}}}function on(e,t){try{let n=JSON.parse(e.read(Wo(e,t))??"");if(n.clientSecret&&n.authSecret)return{clientSecret:n.clientSecret,authSecret:n.authSecret}}catch{}}function Zr(e,t){try{let n=JSON.parse(e.read(Di(e,t))??"");if(n.hash&&n.authSecret)return{hash:n.hash,authSecret:n.authSecret}}catch{}}function du(e,t=21e4){let n=Ii(16),o=Kd(e,n,t,32,"sha256");return`pbkdf2-sha256$${t}$${n.toString("base64url")}$${o.toString("base64url")}`}async function uu(e,t,n){let o={hash:du(n),authSecret:Zr(e,t)?.authSecret??Ii(32).toString("base64url")},r=Di(e,t);return await Wd(qd(r),{recursive:!0,mode:448}),await Jd(r,`${JSON.stringify(o)}
|
|
145
|
+
`,{mode:384}),await Bd(r,384),o}async function en(e){let{state:t,sys:n,secrets:o}=e,r=kn(t),s=t.target??"local";if(r==="password"){let i=t.answers.owner;if(!i)return{why:"no GitHub login to sign in as",fix:"Esc back to GitHub"};let l=o.consolePassword?await uu(n,s,o.consolePassword):Zr(n,s);return l?{mode:r,vars:{MOA_CONSOLE_LOGIN:i},secrets:{MOA_CONSOLE_PASSWORD_HASH:l.hash,AUTH_SECRET:l.authSecret}}:{why:"no console password yet",fix:"Esc back to Console access and press p to choose one"}}let a=t.answers.githubApp;if(r===void 0||!a)return{mode:"none",vars:{},secrets:{}};let c=on(n,a);return c?{mode:"github",vars:{AUTH_GITHUB_ID:a.clientId,MOA_GITHUB_CLIENT_ID:a.clientId},secrets:{AUTH_GITHUB_SECRET:c.clientSecret,AUTH_SECRET:c.authSecret}}:{why:`the client secret for ${a.slug} is not on this machine`,fix:"Esc back to Console access and press g to create a new app"}}function es(e){let{state:t,sys:n}=e,o=t.answers.owner,r=t.answers.tenant??Ne(o),s=t.answers.githubApp,a=[];if(kn(t)==="password"){let l=Zr(n,t.target??"local");a.push(e.secrets.consolePassword||l?S("sign-in",`password \u2014 you sign in as ${o??"your GitHub login"} with it, ${e.secrets.consolePassword?"set in this run":"kept as a hash"} (p to change; \u2191 \u2193 for a GitHub App)`):P("sign-in","password \u2014 none chosen yet","press p and type one, 12 characters or more (or \u2191 \u2193 for a GitHub App)"),H("GitHub App","not needed \u2014 only you sign in, with the password")),(t.answers.people??[]).length>0&&a.push(P("people","teammates sign in with GitHub, and the password is yours alone","\u2191 \u2193 to GitHub App \u2014 or take them off with -login"))}else s?t.target!=="vercel"&&(s.consoleUrl??qr(t))!==Bo(t)?a.push(P("GitHub App",`${s.slug} signs people in at ${s.consoleUrl??qr(t)}, not ${Bo(t)}`,"press g to create one for this console")):a.push(on(n,s)?S("GitHub App",`${s.slug} (id ${s.id}) \u2014 client secret ${D} kept beside setup`):P("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?S("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"?H("GitHub App","none \u2014 the console stays on localhost, never exposed (g to create one anyway)"):P("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?S("owner",`${o} (you)`):P("owner","no GitHub login yet","Esc back to GitHub"),S("tenant",`${r} (e to change)`),S("owner token",`${D} \u2014 made at deploy, kept in ~/.moa/config.json`));let i=t.answers.people??[];i.length===0&&a.push(H("people","only you \u2014 a to add a teammate"));for(let l of i)a.push(S(l.login,`${pu(l.role)} \xB7 cap $${l.dailyCapUsd}/day \u2014 added at deploy`));return{lines:a,answers:{tenant:r}}}function jn(e,t){if(t.runner!=="node")return t;let n=t.workspacePid;if(n!==void 0&&Ut(e,n,t.workspaceStarted,Et(t.workspaceUrl)))return t;let[o="",r=""]=(e.read(Fr(e))??"").split(`
|
|
146
|
+
`),s=Number(o.trim()),a=r.trim();if(!Number.isSafeInteger(s)||s<=1||a===""||e.processOf(s)?.started!==a)return t;let c=Et(t.workspaceUrl);if(c===void 0)return t;let i=e.portsOf(s);return i!==void 0&&!i.includes(c)?t:{...t,workspacePid:s,workspaceStarted:a}}var Ee,ko,Ue,zd,Vd,Zt,Mt,qr,Bo,Ne,bo,S,P,H,le,yo,Vr,ou,cu,hn,Wo,kn,Di,Oi,pu,pe,Fr,Jo,Qe=De(()=>{"use strict";Ze();io();ct();Vt();Xe();ho();Ee=(e,t)=>e.secrets.keys?.[t]?.trim()||e.sys.env[Te[t]]?.trim()||void 0,ko=(e,t)=>e.secrets.keys?.[t]?.trim()?"typed in this run":"your environment",Ue=({sys:e,state:t})=>!e.root||On(t)==="image",zd=8787,Vd=3100,Zt=e=>e.answers.workspacePort??zd,Mt=e=>e.answers.consolePort??Vd,qr=e=>`http://localhost:${Mt(e)}`,Bo=e=>e.target==="vercel"&&e.answers.sandbox?e.answers.sandbox.consoleUrl:e.target==="cloudflare"&&e.answers.domain?ro(e.answers.domain).consoleUrl:qr(e),Ne=e=>(e??"").toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/^-+|-+$/g,"")||"local",bo=e=>e.length>0&&e.every(t=>t.mark==="ok"||t.mark==="skip"),S=(e,t)=>({label:e,mark:"ok",...t?{detail:t}:{}}),P=(e,t,n)=>({label:e,mark:"fail",detail:t,fix:n}),H=(e,t)=>({label:e,mark:"skip",detail:t}),le=e=>e.trim().split(`
|
|
147
|
+
`)[0]?.trim()??"",yo=e=>le(e).match(/\d+(\.\d+)+/)?.[0]??le(e);Vr=e=>e.deployed?.runner==="cloudflare";ou=e=>e.answers.runner==="compose";cu=e=>e.sys.root?ci(e.sys.read(zr(e.sys.root,"apps","workspace",".env"))):{};hn=e=>e.state.deployed?.memoryKept===!0&&!e.secrets.noLocusgraph&&Ie(e)===void 0;Wo=(e,t)=>pe(e,Oo(t));kn=e=>e.answers.consoleSignIn??(e.answers.githubApp?"github":e.target==="local"?void 0:"password"),Di=(e,t)=>pe(e,`console-password-${t}.json`);Oi=["AUTH_GITHUB_ID","AUTH_GITHUB_SECRET","MOA_GITHUB_CLIENT_ID","MOA_CONSOLE_PASSWORD_HASH","MOA_CONSOLE_LOGIN","AUTH_SECRET"];pu=e=>`${e[0]?.toUpperCase()??""}${e.slice(1)}`;pe=(e,t)=>zr(pt(e.env),"..","local",t),Fr=e=>pe(e,"workspace.pid");Jo={where:e=>Xd(e.state,{platform:process.platform,env:e.sys.env}),checks:Zd,github:eu,logins:ru,memory:lu,access:es}});import{readFileSync as zp,realpathSync as Vp}from"node:fs";import{fileURLToPath as Yp}from"node:url";function As(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(/^--?/,""),c=e[o+1];c!==void 0&&!c.startsWith("-")?(n.set(a,c),o+=1):n.set(a,!0)}return{positional:t,flags:n}}var _=(e,t)=>{let n=e.flags.get(t);return typeof n=="string"?n:void 0},E=(e,t)=>e.flags.has(t);function ge(e,t){let n=_(e,t);if(n===void 0)return;let o=Number(n);if(!Number.isFinite(o))throw new A(`--${t} must be a number`);return o}var A=class extends Error{constructor(t){super(t),this.name="Misuse"}};je();Ze();je();An();var k={ok:0,failed:1,halted:2,misuse:3};function j(e,t=process.stdout,n=process.stderr){return{line(o){e||t.write(`${o}
|
|
148
148
|
`)},data(o){e&&t.write(`${JSON.stringify(o,null,2)}
|
|
149
149
|
`)},warn(o){n.write(`${o}
|
|
150
|
-
`)}}}function
|
|
151
|
-
`)}var Xe=(e,t)=>`${e.padEnd(10)}${t}`,Y=(e,t=60)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,cr=e=>{let t=e.split(/\n\s*\n/).filter(n=>n.trim().length>0);return(t[t.length-1]??e).trim()};import{hostname as Lc}from"node:os";Ne();Ye();var Dc="usage: moa login <url> --token <token> or moa login <url> --github";async function Os(e,t=process.env,n={}){let o=E(e,"json"),r=G(o),s=e.flags.get("github"),a=e.positional[1]??(typeof s=="string"?s:void 0);if(!a)throw new A(Dc);if(s!==void 0&&e.flags.has("token"))throw new A("--token and --github are two ways in \u2014 use one");let i=a.replace(/\/+$/,""),c=n.fetch??fetch;if(s!==void 0){let y=await Uc(i,w=>o?r.warn(w):r.line(w),{...n,fetch:c}),b=await Ps(t,P(e,"profile"),{url:i,token:y.token}),m=y.role==="viewer"?", read-only: you may look and nothing else":"";return r.line(`logged in to ${i} as ${y.login}, ${y.role} on ${y.tenant} (profile ${b}${m})`),r.data({url:i,tenant:y.tenant,profile:b,login:y.login,role:y.role}),k.ok}let l=P(e,"token")??t.MOA_TOKEN;if(!l)throw new A("--token is required (or set MOA_TOKEN), or sign in with --github");let d={url:i,token:l},u=await N(d,c).get("/v1/whoami"),f=await Ps(t,P(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 Ps(e,t,n){let o=t??"default",r=await Ae(e);return r.profiles[o]=n,r.current=o,await kt(r,e),o}var _s=async e=>{let t=await e.text().catch(()=>"");try{return JSON.parse(t).error??t}catch{return t}};async function Uc(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 Tt(e,f)}},s=await r("/v1/access/device");if(!s.ok){let d=await _s(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??Lc)()} 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 _s(d)||`the workspace said ${d.status}`;throw d.status===403?new ie(403,`${u} \u2014 ask an owner to add your GitHub login on Access`):d.status===410?new ie(410,`${u} \u2014 run moa login again for a new code`):new ie(d.status,u)}}function rt(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 lr(e,t=new Date){if(!e)return;let n=Date.parse(e);if(!(Number.isNaN(n)||n<=t.getTime()))return rt(t.toISOString(),new Date(n))}async function Ts(e,t=process.env){let n=G(E(e,"json")),o=await se(P(e,"profile"),t),r=await N(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 Is(e,t=process.env){let n=G(E(e,"json")),o=e.positional[1]??P(e,"url"),r=o?{url:o,token:""}:await se(P(e,"profile"),t);try{let s=await N(r).get("/health"),a=rt(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?k.ok:k.failed}catch(s){if(s instanceof ie&&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}),k.failed;throw s}}function Nc(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}${rt(e.startedAt)?`, up ${rt(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=rt(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 Rs(e,t=process.env){let n=G(E(e,"json")),o=N(await se(P(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=Nc(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?k.ok:k.halted}async function Ls(e,t=process.env){let n=G(E(e,"json")),o=N(await se(P(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=lr(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)} ${Mc(s)}${l}${d}`)}return n.data({seats:r}),0}var Mc=e=>e.hasCredential?"your own credential":"the machine's login, shared with every tenant here";async function Ds(e,t=process.env){let n=G(E(e,"json")),o=await Ae(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 Us(e,t=process.env){let n=G(E(e,"json")),o=await Ae(t),r=e.positional[1]??o.current;if(!o.profiles[r]){let a=Object.keys(o.profiles);throw new A(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 kt(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}`),k.ok}async function Ns(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa use <profile>");let o=G(E(e,"json")),r=await Ae(t),s=r.profiles[n];if(!s){let i=Object.keys(r.profiles);throw new A(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 kt(r,t);let a=await N(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}:{}}),k.ok}function Gc(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 Ms(e,t=process.env){let n=G(E(e,"json")),o=N(await se(P(e,"profile"),t)),r=P(e,"since"),s=r===void 0?void 0:Gc(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?k.ok:(n.line(""),n.line(ye([["AGENT","RUNS","COST"],...i.byAgent.map(d=>[d.agent,String(d.runs),l(d.costUsd)])])),n.line(""),n.line(ye([["WHAT ASKED","RUNS","COST"],...i.byCause.map(d=>[d.cause,String(d.runs),l(d.costUsd)])])),k.ok)}async function js(e,t=process.env){let n=e.positional[1],o=N(await se(P(e,"profile"),t)),r=G(E(e,"json"));switch(n){case"create":return jc(o,e,r);case"list":case"ls":return Hc(o,r);case"show":return Fc(o,e,r);case"role":return Kc(o,e,r);case"grant":return Gs(o,e,r,"grant");case"revoke":return Gs(o,e,r,"revoke");case"rm":case"remove":return Bc(o,e,r);default:throw new A(`usage: moa agent <create|list|show|role|grant|revoke|rm> \u2026
|
|
152
|
-
moa agent create reviewer --cli codex --role "\u2026" --peers builder,planner`)}}async function
|
|
150
|
+
`)}}}function Os(e){if(e instanceof A)return{message:e.message,code:k.misuse};if(e instanceof to)return{message:e.message,code:k.halted};if(e instanceof ue){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:k.failed};if(e.status===429)return{message:e.message,code:k.halted};let n=e.status===401||e.status===403||e.status===400?k.misuse:k.failed;return{message:`${e.message}${t}`,code:n}}return{message:e instanceof Error?e.message:String(e),code:k.failed}}function Ae(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(`
|
|
151
|
+
`)}var et=(e,t)=>`${e.padEnd(10)}${t}`,X=(e,t=60)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,dr=e=>{let t=e.split(/\n\s*\n/).filter(n=>n.trim().length>0);return(t[t.length-1]??e).trim()};import{hostname as Nc}from"node:os";je();Ze();var Mc="usage: moa login <url> --token <token> or moa login <url> --github";async function Rs(e,t=process.env,n={}){let o=E(e,"json"),r=j(o),s=e.flags.get("github"),a=e.positional[1]??(typeof s=="string"?s:void 0);if(!a)throw new A(Mc);if(s!==void 0&&e.flags.has("token"))throw new A("--token and --github are two ways in \u2014 use one");let c=a.replace(/\/+$/,""),i=n.fetch??fetch;if(s!==void 0){let y=await Gc(c,w=>o?r.warn(w):r.line(w),{...n,fetch:i}),b=await Ts(t,_(e,"profile"),{url:c,token:y.token}),g=y.role==="viewer"?", read-only: you may look and nothing else":"";return r.line(`logged in to ${c} as ${y.login}, ${y.role} on ${y.tenant} (profile ${b}${g})`),r.data({url:c,tenant:y.tenant,profile:b,login:y.login,role:y.role}),k.ok}let l=_(e,"token")??t.MOA_TOKEN;if(!l)throw new A("--token is required (or set MOA_TOKEN), or sign in with --github");let d={url:c,token:l},u=await U(d,i).get("/v1/whoami"),f=await Ts(t,_(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 Ts(e,t,n){let o=t??"default",r=await Oe(e);return r.profiles[o]=n,r.current=o,await wt(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 Gc(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 Tt(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 c=`${(n.hostname??Nc)()} cli`,i=Math.max(a.interval??5,1),l=(a.expiresIn??900)+30;for(;;){await o(i*1e3),l-=i;let d=await r("/v1/access/device/token",{deviceCode:a.deviceCode,name:c});if(d.status===201)return await d.json();if(d.status===202){let f=await d.json().catch(()=>({}));if(i=Math.max(i,f.interval??i),l<=0)throw new Error("the code expired before it was entered \u2014 run moa login again");continue}if(d.status===429){if(i=Math.min(i*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 ue(403,`${u} \u2014 ask an owner to add your GitHub login on Access`):d.status===410?new ue(410,`${u} \u2014 run moa login again for a new code`):new ue(d.status,u)}}function at(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 ur(e,t=new Date){if(!e)return;let n=Date.parse(e);if(!(Number.isNaN(n)||n<=t.getTime()))return at(t.toISOString(),new Date(n))}async function Ls(e,t=process.env){let n=j(E(e,"json")),o=await ae(_(e,"profile"),t),r=await U(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=j(E(e,"json")),o=e.positional[1]??_(e,"url"),r=o?{url:o,token:""}:await ae(_(e,"profile"),t);try{let s=await U(r).get("/health"),a=at(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?k.ok:k.failed}catch(s){if(s instanceof ue&&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}),k.failed;throw s}}function jc(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}${at(e.startedAt)?`, up ${at(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=at(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=j(E(e,"json")),o=U(await ae(_(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}))]),c=jc(r,s.seats.map(l=>({...l,full:l.limit!==void 0&&(l.used??0)>=l.limit})),a.scope);for(let l of c)n.line(`${l.ok?"ok ":"NO "}${l.name.padEnd(14)}${l.detail}`);let i=c.filter(l=>!l.ok);return n.data({tenant:r.tenant,checks:c}),i.length===0?k.ok:k.halted}async function Ns(e,t=process.env){let n=j(E(e,"json")),o=U(await ae(_(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}`,c=ur(s.resetsAt),i=s.limit!==void 0&&s.used>=s.limit,l=s.resetsAt?`${i?" \xB7 full":""}, resets${c?` in ${c}`:" shortly"}`:"",d=s.installed===!1?" \xB7 not installed":"";n.line(`${s.cli.padEnd(8)} ${a.padEnd(8)} ${Hc(s)}${l}${d}`)}return n.data({seats:r}),0}var Hc=e=>e.hasCredential?"your own credential":"the machine's login, shared with every tenant here";async function Ms(e,t=process.env){let n=j(E(e,"json")),o=await Oe(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 Gs(e,t=process.env){let n=j(E(e,"json")),o=await Oe(t),r=e.positional[1]??o.current;if(!o.profiles[r]){let a=Object.keys(o.profiles);throw new A(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 wt(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}`),k.ok}async function js(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa use <profile>");let o=j(E(e,"json")),r=await Oe(t),s=r.profiles[n];if(!s){let c=Object.keys(r.profiles);throw new A(c.length===0?`no profile named ${n} \u2014 run \`moa login <url> --token <token>\` first`:`no profile named ${n} \u2014 there is ${c.join(", ")}`)}r.current=n,await wt(r,t);let a=await U(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}:{}}),k.ok}function Fc(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=j(E(e,"json")),o=U(await ae(_(e,"profile"),t)),r=_(e,"since"),s=r===void 0?void 0:Fc(r),a=s===void 0?"/v1/spend":`/v1/spend?since=${encodeURIComponent(s)}`,[c,i]=await Promise.all([o.get(a),o.get("/v1/workspace").catch(()=>{})]);n.data({...c,...i?.agentBudget?{agentBudget:i.agentBudget}:{}});let l=d=>`$${d.toFixed(4)}`;if(n.line(`${l(c.costUsd)} over ${c.runs} ${c.runs===1?"run":"runs"}`+(s===void 0?"":`, since ${s}`)),n.line("notional \u2014 what the CLIs reported, not a bill"),i?.agentBudget){let d=i.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 c.runs===0?k.ok:(n.line(""),n.line(Ae([["AGENT","RUNS","COST"],...c.byAgent.map(d=>[d.agent,String(d.runs),l(d.costUsd)])])),n.line(""),n.line(Ae([["WHAT ASKED","RUNS","COST"],...c.byCause.map(d=>[d.cause,String(d.runs),l(d.costUsd)])])),k.ok)}async function Ks(e,t=process.env){let n=e.positional[1],o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json"));switch(n){case"create":return Kc(o,e,r);case"list":case"ls":return Bc(o,r);case"show":return Wc(o,e,r);case"role":return Jc(o,e,r);case"grant":return Fs(o,e,r,"grant");case"revoke":return Fs(o,e,r,"revoke");case"rm":case"remove":return qc(o,e,r);default:throw new A(`usage: moa agent <create|list|show|role|grant|revoke|rm> \u2026
|
|
152
|
+
moa agent create reviewer --cli codex --role "\u2026" --peers builder,planner`)}}async function Kc(e,t,n){let o=t.positional[2],r=_(t,"cli");if(!o)throw new A("usage: moa agent create <name> --cli <cli> [--role \u2026] [--peers a,b]");if(!r)throw new A("--cli is required (claude, codex, cursor, amp, gemini, crush)");let s=(_(t,"peers")??"").split(",").map(c=>c.trim()).filter(Boolean),a=await e.post("/v1/agents",{name:o,cli:r,..._(t,"role")===void 0?{}:{role:_(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 Bc(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(Ae([["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${at(a.oldest)??"?"}`:"\u2014",X(s.role??"\u2014",40)]})])),t.data({agents:n,waiting:o?.waiting??[]}),0}async function Wc(e,t,n){let o=t.positional[2];if(!o)throw new A("usage: moa agent show <name>");let[r,s]=await Promise.all([e.get(`/v1/agents/${o}`),e.get(`/v1/agents/${o}/history`)]);n.line(et("name",r.name)),n.line(et("cli",r.cli)),n.line(et("status",r.status)),n.line(et("role",r.role??"\u2014"));let a=s.peers.filter(i=>!i.revokedAt).map(i=>i.to),c=s.peers.filter(i=>i.revokedAt).map(i=>`${i.to} (revoked)`);return n.line(et("peers",[...a,...c].join(", ")||"\u2014")),s.history.length>0&&(n.line(""),n.line(Ae(s.history.map(i=>[i.at.slice(0,19).replace("T"," "),i.kind,i.actor,X(i.detail??"",40)])))),n.data({agent:r,...s}),0}async function Jc(e,t,n){let o=t.positional[2],r=t.positional[3]??_(t,"role");if(!o||!r)throw new A('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: ${X(s.role??"",60)}`),n.data(s),0}async function Fs(e,t,n,o){let r=t.positional[2],s=t.positional[3];if(!r||!s)throw new A(`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 qc(e,t,n){let o=t.positional[2];if(!o)throw new A("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 Bs(e,t=process.env){let n=U(await ae(_(e,"profile"),t)),o=j(E(e,"json")),r=E(e,"all"),{peers:s}=await n.get("/v1/peers"),a=r?s:s.filter(i=>!i.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"),k.ok;let c=new Set(s.filter(i=>!i.revokedAt).map(i=>`${i.from}\0${i.to}`));return o.line(Ae([["FROM","TO","GRANTED","BY","NOTE"],...a.map(i=>[i.from,i.to,i.grantedAt.slice(0,19).replace("T"," "),i.grantedBy,i.revokedAt?`revoked ${i.revokedAt.slice(0,19).replace("T"," ")}`:c.has(`${i.to}\0${i.from}`)?"":"one way"])])),k.ok}je();Ze();var Wt=async(e,t)=>U(await ae(_(e,"profile"),t)),pr="mesh";function Ws(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 xo(e){if(e.from===pr)return/^undelivered:\s*(.+)$/s.exec(e.body)?.[1]?.trim()}async function Js(e,t=process.env){let[,n,o,r]=e.positional;if(!n||!o||!r)throw new A('usage: moa send <from> <a,b,c> "<body>"');let s=o.split(",").map(i=>i.trim()).filter(Boolean);if(s.length===0)throw new A("name at least one recipient");let a=j(E(e,"json")),c=await(await Wt(e,t)).post("/v1/messages",{from:n,to:s.length===1?s[0]:s,body:r,..._(e,"thread")===void 0?{}:{thread:_(e,"thread")}});return fr(c,a,`${n} \u2192 ${s.join(", ")}`)}async function qs(e,t=process.env){let n=e.positional[1];if(!n)return zc(e,t);let o=j(E(e,"json")),{messages:r}=await(await Wt(e,t)).get(`/v1/agents/${n}/inbox`);if(r.length===0)o.line(`${n} has no unread mail`);else{o.line(Ae([["FROM","KIND","BODY"],...r.map(s=>{let a=xo(s);return[s.from,a?"undelivered":s.kind??"status",X(En(a??s.body),60)]})]));for(let s of r){let a=xo(s);if(!a)continue;let c=Ws(a);o.line(""),o.line(c.say);for(let i of c.run)o.line(` ${i}`)}}return o.data({messages:r}),0}async function zc(e,t){let n=j(E(e,"json")),{waiting:o}=await(await Wt(e,t)).get("/v1/mail");return n.data({waiting:o}),o.length===0?(n.line("nobody has unread mail"),k.ok):(n.line(Ae([["AGENT","UNREAD","WAITING","FROM"],...o.map(r=>[r.to,String(r.unread),at(r.oldest)??"\u2014",r.from])])),o.some(r=>r.from===pr)&&(n.line(""),n.line(`${pr} is the bus: that mail never arrived. moa inbox <name> says why.`)),k.ok)}async function zs(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa mail <name> [--limit n]");let o=j(E(e,"json")),r=ge(e,"limit")??20,{messages:s}=await(await Wt(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`),k.ok):(o.line(Ae([["WHEN","WITH","KIND","BODY"],...s.map(a=>[a.createdAt.slice(0,19).replace("T"," "),a.from===n?`\u2192 ${a.to}`:`\u2190 ${a.from}`,(xo(a)?"undelivered":a.kind??"status")+(a.deliveredAt?"":" \xB7unread"),X(En(xo(a)??a.body),56)])])),k.ok)}async function Vs(e,t=process.env){let[,n,o,r]=e.positional;if(!n||!o||!r)throw new A('usage: moa delegate <from> <to> "<task>"');let s=j(E(e,"json")),a=await(await Wt(e,t)).post("/v1/delegations",{from:n,to:o,task:r,..._(e,"parent")===void 0?{}:{parent:_(e,"parent")}});return a.delivered?(s.line(`${n} handed "${X(r,50)}" to ${o}`),s.line(` thread ${a.thread}`),s.line(` ${n} answers for it until ${o} says done`),s.data(a),k.ok):fr(a,s,`${n} \u2192 ${o}`)}async function Ys(e,t=process.env){let n=j(E(e,"json")),o=await(await Wt(e,t)).get("/v1/delegations"),r=E(e,"blocked")?o.delegations.filter(s=>s.blockedBy):o.delegations;if(r.length===0)n.line(E(e,"blocked")?"nothing is blocked":"nothing is open");else{n.line(Ae([["FROM","TO","STATE","TASK"],...r.map(s=>[s.from,s.to,s.blockedBy?"BLOCKED":"open",X(En(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(` "${X(En(s.blockedBy?.body??""),70)}"`),n.line(` moa reply ${s.thread} ${s.from} --kind answer "\u2026"`)}return n.data(o),0}async function Xs(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa thread <thread>");let o=j(E(e,"json")),r=await(await Wt(e,t)).get(`/v1/delegations/${n}`),{delegation:s}=r;if(o.line(et("task",X(En(s.task),60))),o.line(et("from",s.from)),o.line(et("to",s.to)),o.line(et("state",s.closedAt?"closed":s.blockedBy?"BLOCKED":"open")),s.hops!==void 0&&s.maxHops!==void 0){let a=s.hops>=s.maxHops;o.line(et("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(et("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(` ${En(a.body)}`);return o.data(r),0}async function Qs(e,t=process.env){let[,n,o,r]=e.positional,s=_(e,"kind");if(!n||!o||!r||!s)throw new A('usage: moa reply <thread> <from> --kind <question|answer|finding|done> "<body>"');let a=j(E(e,"json")),c=await(await Wt(e,t)).post(`/v1/delegations/${n}/messages`,{from:o,kind:s,body:r});if(!c.delivered)return fr(c,a,`${o} \u2192 thread`);let i=c.delegation?.closedAt?"closed":c.delegation?.blockedBy?"BLOCKED":"open",l=s==="done"?"finished":`sent ${/^[aeiou]/.test(s)?"an":"a"} ${s}`;return a.line(`${o} ${l} \u2014 the delegation is ${i}`),a.data(c),k.ok}function fr(e,t,n){if(e.delivered)return t.line(`${n}: delivered`),t.data(e),k.ok;let o=e.reason??"the bus refused it";t.warn(`moa: not delivered \u2014 ${o}`);let r=Ws(o);t.warn(` ${r.say}`);for(let s of r.run)t.warn(` ${s}`);return t.data(e),k.halted}var En=e=>e.replace(/\s+/g," ").trim();je();Ze();An();var Vc=`usage: moa manager say "<text>" [--timeout s]
|
|
153
153
|
moa manager proposals
|
|
154
154
|
moa manager approve <id>
|
|
155
155
|
moa manager dismiss <id>
|
|
156
|
-
moa manager watch`;async function
|
|
157
|
-
`,{mode:384}),await
|
|
156
|
+
moa manager watch`;async function ea(e,t=process.env){let n=e.positional[1],o={say:Yc,proposals:el,approve:(a,c,i)=>Zs(a,c,i,"approve"),dismiss:(a,c,i)=>Zs(a,c,i,"dismiss"),watch:rl},r=n!==void 0&&Object.hasOwn(o,n)?o[n]:void 0;if(!r)throw new A(Vc);if(n!=="say"&&e.flags.has("timeout"))throw new A(`moa manager ${n} has no --timeout`);let s=U(await ae(_(e,"profile"),t));return r(s,j(E(e,"json")),e)}async function Yc(e,t,n){let o=n.positional.slice(2).join(" ").trim();if(!o)throw new A('usage: moa manager say "<text>" [--timeout s]');let r=ge(n,"timeout");if(n.flags.has("timeout")&&(r===void 0||r<0))throw new A("--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:c}=await e.post("/v1/manager/messages",{body:o}),i=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 Tt))throw p;if(d??=Date.now()+lr,!(Date.now()<d&&Date.now()<i))throw p});u!==void 0&&(d=void 0);let f=u===void 0?void 0:typeof c=="string"?Xc(u,c):Qc(u,a,o);if(f)return Zc(e,t,f);if(u!==void 0&&Date.now()>i)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`),k.halted;await new Promise(p=>setTimeout(p,l)),l=Math.min(l*1.5,2e3)}}function Xc(e,t){return e.find(n=>n.from==="manager"&&n.answers===t)}function Qc(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 Zc(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"),c=s.filter(l=>l.state===void 0||l.state==="open"),i=l=>l.title??l.kind??"a proposal";if(n.failed?t.warn(`moa: ${n.body}`):t.line(n.body),c.length>0){n.failed||t.line(""),t.line(n.failed?"Before it failed, it proposed:":"It proposes:");for(let l of c)t.line(` ${i(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(` ${i(l)}`),t.line(` moa manager dismiss ${l.id}`)}return t.data({message:n,proposals:s}),n.failed?k.failed:k.ok}async function el(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"),k.ok):(t.line(Ae([["KIND","WHERE","WHAT","WHY","ID"],...n.proposals.map(o=>[o.kind,tl(o),X(o.title,50),X(o.why.replace(/\s+/g," "),50),o.id])])),k.ok)}var tl=e=>e.repo?`${e.repo}${e.issue?`#${e.issue}`:""}`:"";async function Zs(e,t,n,o){let r=n.positional[2];if(!r)throw new A(`usage: moa manager ${o} <id>`);let s=await ol(e,r,o);if(o==="dismiss"){let c=await e.post(`/v1/manager/proposals/${encodeURIComponent(s)}/dismiss`,{});return t.line(`dismissed: ${c.title}`),t.data(c),k.ok}let a;try{a=await e.post(`/v1/manager/proposals/${encodeURIComponent(s)}/approve`,{})}catch(c){let i=c instanceof ue&&c.status===502?nl(c):void 0;if(!i)throw c;return t.warn(`moa: ${i.title} failed${i.result?` \u2014 ${i.result}`:""}`),t.data(i),k.failed}return t.line(`${a.state==="waiting"?"approved, for later":"approved"}: ${a.title}`),a.result&&t.line(` ${a.result}`),t.data(a),k.ok}function nl(e){try{let t=JSON.parse(e.message);return typeof t.id=="string"&&typeof t.title=="string"?t:void 0}catch{return}}async function ol(e,t,n){let o=n==="dismiss"?["open","waiting"]:["open"],s=(await Promise.all(o.map(c=>e.get(`/v1/manager/proposals?state=${c}`)))).flatMap(c=>c.proposals.map(i=>i.id));if(s.includes(t))return t;let a=s.filter(c=>c.startsWith(t));if(a.length>1)throw new A(`${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 A(`${t} is not a proposal's id`);return t}async function rl(e,t){let n=await e.get("/v1/manager/watch");if(t.data(n),n.watching.length===0)return t.line("nothing is running"),k.ok;t.line(Ae([["HEALTH","KIND","WHAT","WHO","DOING","COST","JOB"],...n.watching.map(o=>[o.held?`${o.health} (held)`:o.health,o.kind,o.ref||X(o.title,30),o.worker??"",X(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 k.ok}import{chmod as sl,writeFile as al}from"node:fs/promises";import{resolve as il}from"node:path";je();Ze();async function ta(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa mcp <agent> [--file <path>]");let o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json"));if(E(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`),k.ok}let s=await o.get(`/v1/agents/${n}/mcp-token`),a=await o.post(`/v1/agents/${n}/mcp-token`,{}),i=(await o.rpc(a.url,a.token,"tools/list")).tools??[];r.data({url:a.url,expiresAt:a.expiresAt,tools:i.map(f=>f.name),outstanding:s.tokens.length}),r.line(`${n} reaches ${a.url}`);for(let f of i)r.line(` ${f.name.padEnd(12)}${f.description??""}`);i.length===0&&r.warn("moa: it answered, and offered no tools");let l=_(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"),k.ok;let d={mcpServers:{fnmoa:{type:"http",url:a.url,headers:{Authorization:`Bearer ${a.token}`}}}},u=il(l);return await al(u,`${JSON.stringify(d,null,2)}
|
|
157
|
+
`,{mode:384}),await sl(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`),k.ok}je();Ze();An();async function*no(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:c}=await o.read();if(c)break;s+=r.decode(a,{stream:!0});let i=s.indexOf(`
|
|
158
158
|
|
|
159
|
-
`);for(;
|
|
159
|
+
`);for(;i!==-1;){let l=s.slice(0,i);s=s.slice(i+2);let d=cl(l);d&&(yield d),i=s.indexOf(`
|
|
160
160
|
|
|
161
|
-
`)}}}finally{await o.cancel().catch(()=>{})}}function
|
|
162
|
-
`)){if(r.startsWith(":")||r.trim()==="")continue;let s=r.indexOf(":"),a=s===-1?r:r.slice(0,s),
|
|
163
|
-
`),...n===void 0?{}:{id:n}}}
|
|
164
|
-
`)[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} `:""}${
|
|
165
|
-
`)[0]??"",44),
|
|
161
|
+
`)}}}finally{await o.cancel().catch(()=>{})}}function cl(e){let t="message",n,o=[];for(let r of e.split(`
|
|
162
|
+
`)){if(r.startsWith(":")||r.trim()==="")continue;let s=r.indexOf(":"),a=s===-1?r:r.slice(0,s),c=s===-1?"":r.slice(s+1).replace(/^ /,"");a==="event"?t=c:a==="data"?o.push(c):a==="id"&&(n=c)}if(!(o.length===0&&t==="message"))return{event:t,data:o.join(`
|
|
163
|
+
`),...n===void 0?{}:{id:n}}}je();Ze();An();async function na(e,t=process.env){if(e.positional[1]!=="work")throw new A('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 A("usage: moa team work <a,b,c>");let o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json")),s=new AbortController,a=E(e,"watch")?n.map(async u=>{let f=await o.stream(`/v1/agents/${u}/events`,s.signal);await ul(f,s,r,u)}):[],c;try{c=await o.post("/v1/team/work",{agents:n,...ge(e,"max-turns")===void 0?{}:{maxTurns:ge(e,"max-turns")},...ge(e,"max-cost")===void 0?{}:{maxCostUsd:ge(e,"max-cost")},..._(e,"nudge")===void 0?{}:{nudge:_(e,"nudge")},...E(e,"route-replies")?{routeReplies:!0}:{}},Cn(e))}catch(u){throw s.abort(),u}let i=xn(o,c.id,u=>r.warn(u)),l;try{l=await Bt(o,c.id,ge(e,"timeout")??3600,E(e,"watch")?u=>r.line(`\xB7 ${mr(u)}`):void 0)}finally{i.stop()}if(s.abort(),await Promise.allSettled(a),l.state==="failed")return r.warn(`moa: ${l.error??"the team loop failed"}`),r.data(l),k.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} ${X(u.task,40)}`+(u.blockedBy?` BLOCKED: "${X(u.blockedBy.body,40)}"`:""));return r.data(l),d.outcome==="settled"?k.ok:k.halted}async function oa(e,t=process.env){let n=Number(e.positional[1]);if(!Number.isInteger(n)||n<1)throw new A("usage: moa ship <issue>");let o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json")),s=E(e,"watch"),a=await o.post("/v1/pipelines",{issue:n,..._(e,"repo")===void 0?{}:{repo:_(e,"repo")},...ge(e,"max-steps")===void 0?{}:{maxSteps:ge(e,"max-steps")},...ge(e,"max-cost")===void 0?{}:{maxCostUsd:ge(e,"max-cost")}},Cn(e));r.line(`shipping #${n} \u2014 ${a.id}`);let c=xn(o,a.id,d=>r.warn(d)),i;try{i=await Bt(o,a.id,ge(e,"timeout")??7200,s?d=>r.line(` ${mr(d)}`):void 0)}finally{c.stop()}if(i.state==="failed")return r.warn(`moa: ${i.error??"the pipeline failed"}`),r.data(i),k.failed;let l=i.result;return r.line(`${l.outcome}: ${l.reason}`),r.line(` ${l.steps} step(s), $${l.costUsd.toFixed(3)} notional`),r.data(i),l.outcome==="merged"?k.ok:k.halted}function mr(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 ra(e,t=process.env){let n=U(await ae(_(e,"profile"),t)),o=j(E(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=Ao(s.runSlots);if(r.jobs.length===0)return o.line("nothing has run yet"),a&&o.line(a),k.ok;let c=r.jobs.some(i=>(i.issue?.pipelines??0)>1);return o.line(Ae([["STATE","KIND","WHAT","WHERE",...c?["ISSUE IN ALL"]:[],"JOB"],...r.jobs.map(i=>[i.state,i.kind,i.subject??i.agent??"",X(dl(i),44),...c?[ll(i)]:[],i.id])])),a&&(o.line(""),o.line(a)),k.ok}function ll(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 Ao(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 dl(e){if(e.error)return e.error;if(e.state==="running")return e.progress?mr(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 sa(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa stop <jobId>");let o=U(await ae(_(e,"profile"),t)),r=j(E(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),k.ok}async function ul(e,t,n,o){try{for await(let r of no(e,t.signal)){if(r.event!=="activity")continue;let s=JSON.parse(r.data);n.line(` ${o}: ${s.tool?`${s.tool} `:""}${X(String(s.text??""),80)}`)}}catch{}}var Cn=e=>_(e,"key")??crypto.randomUUID();function Eo(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 pl(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 ia(e,t=process.env){let n=e.positional[1],o=e.positional[2];if(!n||!o)throw new A('usage: moa run <name> "<prompt>" [--watch]');let r=U(await ae(_(e,"profile"),t)),s=j(E(e,"json")),a=E(e,"watch"),c=new AbortController,i=a?await r.stream(`/v1/agents/${n}/events?from=next`,c.signal):void 0,l;try{l=await r.post(`/v1/agents/${n}/runs`,{prompt:o},Cn(e))}catch(m){throw c.abort(),m}if(E(e,"detach"))return c.abort(),s.line(`started ${l.id}`),s.data(l),k.ok;let d=i?la(i,c,s,r):void 0,u=yl(r,c.signal,m=>s.line(m)),f=xn(r,l.id,m=>s.warn(m)),p;try{p=await Bt(r,l.id,ge(e,"timeout")??3600,void 0,()=>pl(r,n,o,Cn(e),m=>s.warn(m)))}finally{f.stop()}return c.abort(),await d,await u,f.asked()?(s.data(p),k.halted):bl(p,s)}var ca="the workspace stopped while this run was going",fl=e=>e===ca||Eo(e)!==void 0;async function ml(e,t,n=new Date,o){if(!t)return"nothing yet \u2014 it has not said anything";if(fl(o))return;let r=await e.get("/v1/workspace").then(c=>c.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 gl(e,t){let n=U(await ae(_(e,"profile"),t)),o=j(E(e,"json")),r=(ge(e,"every")??2)*1e3,s=new AbortController,a=()=>s.abort();process.on("SIGINT",a);try{await hl(n,o,r,s.signal)}finally{process.off("SIGINT",a)}return k.ok}async function hl(e,t,n,o){let r=new Map,s,a=!0;for(;!o.aborted;){let[c,i]=await Promise.all([e.get("/v1/jobs").catch(()=>({jobs:[]})),e.get("/v1/workspace").catch(()=>({runSlots:void 0}))]);for(let d of[...c.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)} ${wl(d)}`.trimEnd()),t.data({job:d.id,kind:d.kind,state:d.state,was:u})}a=!1;let l=Ao(i.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 wl=e=>{if(e.error)return X(e.error,60);let t=e.result;if(!t)return"";let n=t.outcome??t.result??"",o=t.costUsd?` $${t.costUsd.toFixed(4)}`:"";return`${X(String(n).replace(/\s+/g," "),52)}${o}`.trim()};async function kl(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=ur(o.resetsAt);return`waiting for a window \u2014 ${o.cli} ${o.used}/${o.limit}${r?`, resets in ${r}`:""}`}async function yl(e,t,n,o=2500){if(await new Promise(a=>{let c=setTimeout(a,o);t.addEventListener("abort",()=>{clearTimeout(c),a()},{once:!0})}),t.aborted)return;let r=await e.get("/v1/workspace").catch(()=>({runSlots:void 0}));if(t.aborted)return;let s=Ao(r.runSlots);s&&n(` \xB7 ${s}`)}async function la(e,t,n,o){let r=!1;try{for await(let s of no(e,t.signal)){let a=JSON.parse(s.data);if(s.event==="activity"){let c=typeof a.tool=="string"?a.tool:"";n.line(` ${c?`${c} `:""}${X(String(a.text??""),100)}`)}else s.event==="status"&&a.status==="rate-limited"&&!r&&(r=!0,n.line(` \xB7 ${await kl(o)}`))}}catch{}}function bl(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?Eo(o)??o:"the run failed")}`),t.data(e),k.failed}let n=e.result;return t.data(e),n&&"stopped"in n?(t.warn(`moa: ${n.reason}`),k.halted):(n?.result&&t.line(Eo(n.result)??n.result),n?.isError?k.failed:k.ok)}async function da(e,t=process.env){let n=e.positional[1];if(!n)throw new A("usage: moa logs <runId> [--tail n] [--since seq]");let o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json")),s=ge(e,"tail"),a=ge(e,"since");if(s!==void 0&&a!==void 0)throw new A("moa logs takes --tail or --since, not both");let[c,i]=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=i.activity,d=i.total??l.length;if(r.line(`${c.agent}: ${X(dr(c.prompt).split(`
|
|
164
|
+
`)[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} `:""}${X(p.text,100)}`);let u=E(e,"watch")&&!c.endedAt;if(d===0&&!u){let p=await ml(o,c.endedAt,new Date,c.result);p&&r.line(` ${p}`)}let f=u?await $l(o,n,l[l.length-1]?.seq??-1,r,e):c;return f.result&&(r.line(""),r.line(Eo(f.result)??f.result)),r.data({run:f,activity:l}),f.isError?k.failed:k.ok}async function $l(e,t,n,o,r){let s=(ge(r,"every")??1)*1e3,a=new AbortController,c=()=>a.abort();process.on("SIGINT",c);let i=n;try{for(;;){let l=await e.get(i<0?`/v1/runs/${t}/activity`:`/v1/runs/${t}/activity?since=${i}`);for(let d of l.activity)i=d.seq,o.line(` ${d.tool?`${d.tool} `:""}${X(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",c)}}async function ua(e,t=process.env){let n=e.positional[1],o=e.positional[2],r=e.positional[3];if(!n||!o||!r)throw new A('usage: moa ask <from> <to> "<question>" [--watch]');let s=U(await ae(_(e,"profile"),t)),a=j(E(e,"json")),c=ge(e,"timeout"),i=new AbortController,l=E(e,"watch")?await s.stream(`/v1/agents/${o}/events?from=next`,i.signal):void 0,d;try{d=await s.post("/v1/calls",{from:n,to:o,question:r,...c===void 0?{}:{timeoutMs:c*1e3}},Cn(e))}catch(y){throw i.abort(),y}if(E(e,"detach"))return i.abort(),a.line(`started ${d.id}`),a.data(d),k.ok;let u=l?la(l,i,a,s):void 0,f=xn(s,d.id,y=>a.warn(y)),p;try{p=await Bt(s,d.id,c??3600)}finally{f.stop()}if(i.abort(),await u,a.data(p),p.state==="failed")return a.warn(`moa: ${p.error??"the call failed"}`),k.failed;let m=p.result;return m?.answered?(a.line(m.answer),k.ok):(a.warn(`moa: ${m?.reason??"no answer"}`),k.halted)}var gr=e=>e.endedAt===void 0?"running":e.isError?e.result===ca?"cut short":"failed":"done";async function vl(e){let t=await e.get("/v1/workspace").catch(()=>{}),n=at(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 aa=["running","done","failed","interrupted"];function Sl(e){let t=_(e,"state");if(t!==void 0){if(!aa.includes(t))throw new A(`--state must be one of ${aa.join(", ")}`);return t}return E(e,"failed")?"failed":void 0}var xl=(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 pa(e,t=process.env){let n=e.positional[1],o=Sl(e),r=U(await ae(_(e,"profile"),t)),s=j(E(e,"json")),a=ge(e,"limit")??10,c=_(e,"before"),i=`limit=${a}${o===void 0?"":`&state=${o}`}${c===void 0?"":`&before=${encodeURIComponent(c)}`}`,l=n?(await r.get(`/v1/agents/${n}/runs?${i}`)).runs:(await r.get(`/v1/runs?${i}`)).runs;if(s.data({runs:l}),l.length===0){if(c!==void 0)return s.line(n?`nothing ${n} ran before that one`:"nothing ran before that one"),k.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}`),k.ok}return s.line(n?`${n} has not run yet`:"nothing has run yet"),k.ok}s.line(Ae([["AGE","STATE",...n?[]:["AGENT"],"CAUSE","COST","TASK","RUN"],...l.map(m=>[at(m.startedAt)??"\u2014",gr(m),...n?[]:[m.agent??""],m.cause??"\u2014",m.costUsd===void 0?"\u2014":`$${m.costUsd.toFixed(4)}`,X(dr(m.prompt).split(`
|
|
165
|
+
`)[0]??"",44),m.id])]));let d=l.filter(m=>gr(m)==="failed").length,u=l.filter(m=>gr(m)==="cut short").length,f=u>0?await vl(r):void 0;o===void 0&&d+u>0&&(s.line(`
|
|
166
166
|
${[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(`
|
|
167
|
-
older: ${
|
|
167
|
+
older: ${xl(n,o,a,p)}`),k.ok}async function fa(e,t=process.env){let n=e.positional[1];if(!n)return gl(e,t);let o=U(await ae(_(e,"profile"),t)),r=j(E(e,"json")),s=new AbortController,a=()=>s.abort();process.on("SIGINT",a);let c=await o.stream(`/v1/agents/${n}/events`,s.signal);try{for await(let i of no(c,s.signal)){let l=JSON.parse(i.data);if(i.event==="run")r.line(`\u2014 ${String(l.runId??"")} started ${String(l.startedAt??"")}`);else if(i.event==="activity"){let d=typeof l.tool=="string"?l.tool:"";r.line(` ${d?`${d} `:""}${X(String(l.text??""),100)}`)}else i.event==="status"?r.line(` \xB7 ${String(l.status??"")}`):i.event==="end"?r.line(` = ${X(String(l.result??""),100)}`):i.event==="closing"&&r.warn("moa: the workspace is shutting down");r.data({event:i.event,data:l})}}catch{}finally{process.off("SIGINT",a)}return k.ok}var Al=["--json","--watch","--detach","--all","--limit","--since","--timeout","--nudge","--route-replies","--profile","--file","--revoke","--token","--github","--help"];function ma(e,t){let n=[...t].sort().join(" "),o=Al.join(" ");if(e==="bash")return`# moa completion for bash \u2014 eval "$(moa completion bash)"
|
|
168
168
|
_moa() {
|
|
169
169
|
local commands="${n}"
|
|
170
170
|
local flags="${o}"
|
|
@@ -187,58 +187,58 @@ _moa() {
|
|
|
187
187
|
fi
|
|
188
188
|
}
|
|
189
189
|
compdef _moa moa
|
|
190
|
-
`;throw new A(`moa completion takes bash or zsh, not ${JSON.stringify(e)}`)}import{spawnSync as
|
|
191
|
-
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),
|
|
192
|
-
${
|
|
193
|
-
`).map(Kt=>Kt.replace(/\[[0-9;]*m/g,"").trim()).find(Kt=>/ERROR|✘/.test(Kt));return l("image",`Could not push to the Cloudflare registry${
|
|
194
|
-
`),
|
|
195
|
-
${
|
|
196
|
-
`,{mode:384}),
|
|
190
|
+
`;throw new A(`moa completion takes bash or zsh, not ${JSON.stringify(e)}`)}import{spawnSync as jp}from"node:child_process";import{createInterface as ys}from"node:readline/promises";import{Writable as Hp}from"node:stream";import{render as Fp}from"ink";import{createElement as Kp}from"react";je();Ze();Qe();Qt();ct();Xe();import{readFile as fu,writeFile as mu}from"node:fs/promises";var Ui=e=>we.includes(e);async function Ni(e,t){let n=tn(e.sys),o=JSON.parse(await fu(n,"utf8")),r=o.services.workspace.environment;for(let[a,c]of Object.entries(t))c===void 0?delete r[a]:r[a]=c.replaceAll("$",()=>"$$");await mu(n,JSON.stringify(o),{mode:384});let s=await e.sys.exec("docker",nt(e.sys,"up","-d","--wait"),{timeoutMs:6e5});return s.ok?{ok:!0}:{ok:!1,why:le(s.stderr)||"compose up failed"}}async function gu(e,t,n){let o=e.state.deployed?.workspaceUrl,r=await Oe(e.sys.env),s=Object.values(r.profiles).find(i=>i.url===o);if(!o||!s)return n({label:"proof",mark:"fail",detail:"no profile on this machine for the workspace",fix:"run `moa setup` and Deploy"}),!1;let a=U({url:o,token:s.token},e.sys.fetch);for(let i=0;i<60;i++)try{await a.get("/v1/whoami");break}catch{await e.sys.sleep(1e3)}return!("label"in await Nt(a,[t],"in the image",()=>`run \`moa setup login ${t}\` again`,n))}var Mi=(e,t,n)=>{let o=new Set(e.answers.loggedIn??[]);return n?o.add(t):o.delete(t),{...e,answers:{...e.answers,loggedIn:we.filter(r=>o.has(r))}}};function Gi(e,t){return e.state.deployed?.runner==="image"&&e.sys.exists(tn(e.sys))?!0:(t({label:"agent login",mark:"fail",detail:e.state.deployed===void 0?"nothing is deployed yet":"one agent at a time works on the published image on this machine for now",fix:"run `moa setup login` and use Agent logins, then Deploy"}),!1)}async function ji(e,t,n,o){let{print:r}=o;if(!Gi(e,r))return{ok:!1};if(n||!wo(t)){n||await o.setupToken();let a=n?Te[t]:"CLAUDE_CODE_OAUTH_TOKEN",c=(await o.secret(n?`${a}: `:"Paste the token `claude setup-token` printed: ")).trim();if(!c||/\s/.test(c))return r({label:t,mark:"fail",detail:"nothing pasted \u2014 nothing was changed",fix:`run \`moa setup login ${t}${n?" --key":""}\` again`}),{ok:!1};r({label:t,mark:"wait",detail:"restarting the workspace on it \u2014 data kept"});let i=t==="claude"?n?"CLAUDE_CODE_OAUTH_TOKEN":Te.claude:void 0,l=await Ni(e,{[a]:c,...i?{[i]:void 0}:{}});if(!l.ok)return r({label:t,mark:"fail",detail:l.why,fix:"`moa setup logs` says why"}),{ok:!1}}else if(await nn(e.sys,t))r({label:t,mark:"ok",detail:"already logged in inside the container"});else{r({label:t,mark:"wait",detail:"starting its login inside the container\u2026"});let a=!1,i=await Kn(e.sys,t,l=>{let d=Bn(l);d&&!a&&(a=!0,r({label:t,mark:"wait",detail:d}))}).done;if(!i.ok||!await nn(e.sys,t))return r({label:t,mark:"fail",detail:`the login did not finish: ${le(i.stderr)||"no answer"}`,fix:`run \`moa setup login ${t}\` again`}),{ok:!1};r({label:t,mark:"ok",detail:"logged in inside the container \u2014 kept on its home volume"})}let s=await gu(e,t,r);return{ok:s,state:Mi(e.state,t,s)}}var ts={codex:["codex","logout"],cursor:["cursor-agent","logout"]};async function Hi(e,t,n){if(!Gi(e,n))return{ok:!1};wo(t)&&await e.sys.exec("docker",nt(e.sys,"exec","-T","workspace",...ts[t]),{timeoutMs:6e4});let o=await Ni(e,t==="claude"?{CLAUDE_CODE_OAUTH_TOKEN:void 0,ANTHROPIC_API_KEY:void 0}:{[Te[t]]:void 0});if(!o.ok)return n({label:t,mark:"fail",detail:o.why,fix:"`moa setup logs` says why"}),{ok:!1};n({label:t,mark:"ok",detail:`disconnected \u2014 \`moa setup login ${t}\` connects it again`});let r=Mi(e.state,t,!1);return r.answers.agentCli===t&&(r.answers={...r.answers,agentCli:r.answers.loggedIn?.[0]}),{ok:!0,state:r}}import{Box as Fe,Text as Y,useApp as Vu,useInput as Yu,usePaste as Xu}from"ink";import{useEffect as us,useMemo as Qu,useRef as mt,useState as lt}from"react";Qe();Qt();je();Qe();io();Qt();ct();Lt();Vt();Ln();Xe();import{createHash as hu}from"node:crypto";import{mkdir as wu,readFile as ku,rm as yu,writeFile as ns}from"node:fs/promises";import{dirname as bu,join as qo}from"node:path";var Wn="cloudflare",$o=(e,t=2)=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
191
|
+
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),Fi=e=>hu("sha256").update(e).digest("hex").slice(0,12);async function os(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 $u(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} ${$o(await r.text().catch(()=>""))}`};let s="",a=r.body.getReader(),c=new TextDecoder;for(;;){let{done:l,value:d}=await a.read();if(l)break;s+=c.decode(d,{stream:!0});let u=dn(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:$o(s.replace(/\[moa\].*$/m,""))||"codex login did not finish"}}async function vu(e,t){let n=t.match(/^ghcr\.io\/([^:]+):(.+)$/);if(!n)return{why:`not a ghcr.io image: ${t}`};let[,o,r]=n;try{let s=await(await e.fetch(`https://ghcr.io/token?scope=repository:${o}:pull`)).json(),c=(await(await e.fetch(`https://ghcr.io/v2/${o}/manifests/${r}`,{headers:{authorization:`Bearer ${s.token??""}`,accept:"application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json"}})).json()).manifests?.find(i=>i.platform?.os==="linux"&&i.platform?.architecture==="amd64");return c?{image:`ghcr.io/${o}@${c.digest}`}:{why:`${t} has no linux/amd64 image`}}catch(s){return{why:s instanceof Error?s.message:String(s)}}}async function zo(e,t,n=()=>{},o=()=>{}){let{sys:r,state:s}=e,a=[],c=M=>{let J=a.findIndex(Se=>Se.label===M.label);J===-1?a.push(M):a[J]=M,t([...a])},i,l=(M,J,Se)=>(c({label:M,mark:"fail",detail:J,fix:Se}),i?{ok:!1,deployed:i}:{ok:!1}),d=r.root??Hn(),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(hn(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=Yr(e)?.token,y=Xr(e)?.token,b=tt(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(!y&&!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&&!Ee(e,"claude")&&!p)return l("claude token","none in this run \u2014 it is never saved","Esc back to Agent logins and press c (or k for an API key, or choose another agent), then Enter");let w=await en(e);if("why"in w)return l("console sign-in",w.why,w.fix);if(w.mode==="none")return l("console sign-in","none \u2014 a console on a domain needs somebody to sign in","Esc back to Console access: \u2191 \u2193 to password, or press g for a GitHub App");let{workspaceUrl:v,consoleUrl:C}=ro(u),N=s.answers.tenant??Ne(s.answers.owner),W=s.answers.owner,ie=Ie(e),R=qo(d,"apps","workspace"),Q,L;if(Ue(e)){for(let me of await vt(e))if(c(me),me.mark==="fail")return{ok:!1};for(let me of await Fn(e))if(c(me),me.mark==="fail")return{ok:!1};let M=`fnmoa:${qt()}`,J=await Sa(r,d,f,M);J&&c({label:"image",mark:"ok",detail:`${M} \u2014 already in Cloudflare's registry`});let Se=J?{image:""}:await vu(r,Re());if(!J){if("why"in Se)return l("image",`no linux/amd64 image: ${Se.why}`,"Check your network and retry");let me=await r.exec("docker",["pull","--platform","linux/amd64",Se.image],{timeoutMs:9e5});if(!me.ok)return l("image",`could not pull ${Se.image}: ${le(me.stderr)}`,"Check Docker and retry");if(!(await r.exec("docker",["tag",Se.image,M])).ok)return l("image","Could not tag the release image","Check Docker and retry");c({label:"image",mark:"wait",detail:"pushing it to Cloudflare's registry"});let Zn="",eo=await ga(r,d,["containers","push",M],{account:f,timeoutMs:36e5},Sn=>{Zn+=Sn;let Kt=ha(Zn);Kt.total>0&&c({label:"image",mark:"wait",detail:"pushing it to Cloudflare's registry",progress:{...Kt,unit:"layers"}})}).done;if(!eo.ok){let Sn=`${eo.stderr}
|
|
192
|
+
${eo.stdout}`.split(`
|
|
193
|
+
`).map(Kt=>Kt.replace(/\[[0-9;]*m/g,"").trim()).find(Kt=>/ERROR|✘/.test(Kt));return l("image",`Could not push to the Cloudflare registry${Sn?`: ${Sn.replace(/^✘\s*\[ERROR\]\s*/,"")}`:""}`,"Check your Cloudflare login and retry")}}if(Q=`registry.cloudflare.com/${f}/${M}`,r.root){let me=await Br(r);if(!me.ok)return l("worker",`Could not copy the Worker out of ${Re()}: ${me.why}`,"Check Docker and retry");L=me.path}else L=Kr();c({label:"image",mark:"ok",detail:`${Re()} \u2014 the Worker and the container`})}else{c({label:"build",mark:"wait",detail:"building the Worker"});let M=await r.exec("pnpm",["install"],{cwd:d,timeoutMs:6e5}),J=M.ok?await r.exec("pnpm",["--filter","@fnmoa/workspace...","run","build"],{cwd:d,timeoutMs:9e5}):M;if(!J.ok)return l("build",$o(J.stderr||J.stdout),`run \`pnpm install && pnpm --filter @fnmoa/workspace... run build\` in ${d}, then Enter`);c({label:"build",mark:"ok",detail:"the Worker is built from this checkout"})}let{token:K,pending:re,before:ye,had:te}=await Yt(r,Wn,v);c({label:"owner token",mark:"ok",detail:`${D} \u2014 profile ${Wn} in ~/.moa/config.json${ye===Wn||!te?"":`; \`moa\` now talks to this workspace, not profile ${ye} (\`moa use ${ye}\` to go back)`}`});let $e=s.cloudflare??p?.cloudflare,$=$e?.account===f?$e:void 0,I=$!==void 0&&$.older!==!0&&($.d1?.unsure===!0||$.r2?.unsure===!0),se=p!==void 0&&$e===void 0||$?.older===!0||I,T={...$,account:f,...se?{older:!0}:{}},z=M=>{T={...T,...M},o(T)},ke=async M=>{let J=await va(r,d,f),Se=J?.image&&!(T.unsureImages??[]).includes(J.image)?[J.image]:[],me=[...new Set([...T.images??[],...Se])];z({...M?{worker:Ye}:{},...J?{containerId:J.id}:{},...me.length>0?{images:me}:{}})};if(se&&($===void 0||I||$.imagesUnlisted)){let M=await so(r,d,f);if(!M.ok)z({imagesUnlisted:!0}),c({label:"images",mark:"skip",detail:`could not list Cloudflare's registry \u2014 ${M.why}; the next deploy, or \`down\`, lists it again for the tags an older moa pushed`});else{let{imagesUnlisted:J,...Se}=T,me=[...new Set([...T.unsureImages??[],...M.images.filter(Ft=>!(T.images??[]).includes(Ft))])];T={...Se,...me.length>0?{unsureImages:me}:{}},o(T),me.length>0&&c({label:"images",mark:"ok",detail:`${me.length} tag${me.length===1?"":"s"} of ${We} already in Cloudflare's registry \u2014 an older moa pushed them, so \`down\` asks before deleting them`})}}c({label:"D1",mark:"wait",detail:"finding the database, or making it"});let ne=await ba(r,d,f);if(!ne.ok)return l("D1",ne.why,"fix that, then Enter");let Pe=ne.made||$?.d1?.id===ne.id&&$.d1.made,_e=!Pe&&(se||$?.d1?.id===ne.id&&$.d1.unsure===!0);z({d1:{id:ne.id,made:Pe,..._e?{unsure:!0}:{}}}),c({label:"D1",mark:"ok",detail:`${ne.made?"made":"found"} fnmoa-workspace (${ne.id})`});let Ke=await $a(r,d,f);if(!Ke.ok)return l("R2",Ke.why,"R2 must be enabled on the account (dashboard \u2192 R2), then Enter");let Me=Ke.made||$?.r2?.name===yt&&$.r2.made,ze=!Me&&(se||$?.r2?.name===yt&&$.r2.unsure===!0);z({r2:{name:yt,made:Me,...ze?{unsure:!0}:{}}}),c({label:"R2",mark:"ok",detail:"fnmoa-state \u2014 codex's login, kept across restarts"});let ve=an(u),Le={MOA_D1_ACCOUNT_ID:f,MOA_D1_DATABASE_ID:ne.id,MOA_CONSOLE_HOST:ve.console,MOA_PUBLIC_URL:v,MOA_IDLE_HOURS:String(It),...w.vars,AUTH_URL:C,...W?{MOA_OWNER_LOGIN:W}:{},...s.answers.repo?{MOA_REPO:s.answers.repo}:{},...ie?{LOCUSGRAPH_GRAPH_ID:ie.graph,LOCUSGRAPH_SERVER_URL:ie.server}:{}},O={...await zt(e),MOA_TOKENS:`${K}:${N}`,AUTH_GITHUB_SECRET:"",MOA_CONSOLE_PASSWORD_HASH:"",...w.secrets,...m?{MOA_D1_API_TOKEN:m}:{},...y?{GH_TOKEN:y}:{},...b?{CLAUDE_CODE_OAUTH_TOKEN:b}:{},...Object.fromEntries(we.map(M=>[Te[M],Ee(e,M)??""])),LOCUSGRAPH_AGENT_SECRET:ie?ie.secret:""},F=await r.exec("git",["rev-parse","HEAD"],{cwd:d}),Be=Fi(JSON.stringify([Le,F.stdout.trim()])),rt=Fi(JSON.stringify(O)),st=pe(r,qo("cloudflare","wrangler.json")),gt=pe(r,qo("cloudflare","secrets.json")),de=pe(r,qo("cloudflare","secrets.fingerprint")),vn=(await ku(de,"utf8").catch(()=>"")).trim();await wu(bu(st),{recursive:!0}),await ns(st,`${JSON.stringify(xa({root:d,account:f,databaseId:ne.id,domain:u,vars:Le,...Q?{image:Q,worker:L}:{}}),null,2)}
|
|
194
|
+
`),c({label:"deploy",mark:"wait",detail:"the Worker, the container image and your two names \u2014 the first build takes several minutes"});let Z;try{await ns(gt,JSON.stringify(O),{mode:384}),Z=await Je(r,d,["deploy","--config",st,"--secrets-file",gt],{account:f,timeoutMs:24e5})}finally{await yu(gt,{force:!0})}if(!Z.ok){let M=T.worker!==void 0||(await Je(r,d,["deployments","list","--name",Ye,"--json"],{account:f})).ok;await ke(M);let J=`${Z.stdout}
|
|
195
|
+
${Z.stderr}`;return l("deploy",$o(J.split(gt).join("secrets.json")),/containers?.*(plan|paid|not enabled|entitle)/i.test(J)?"containers need the Workers Paid plan: upgrade the account, then Enter":/docker/i.test(J)?"start Docker, then Enter":"fix that, then Enter")}await ke(!0);let Ge=!!ie,he=s.deployed?.workspaceUrl===v?s.deployed.people:void 0;i={runner:"cloudflare",workspaceUrl:v,consoleUrl:C,...he?{people:he}:{},...p?.workspaceFor?{workspaceFor:p.workspaceFor}:{},...Ge?{memoryKept:Ge}:{},cloudflare:T,at:new Date().toISOString()},n(i),c({label:"deploy",mark:"ok",detail:`Worker ${Ye} \u2014 ${ve.workspace}, ${ve.console}`});let Ve=U({url:v,token:K},r.fetch),ht=(M,J={})=>r.fetch(`${v}${M}`,{...J,headers:{authorization:`Bearer ${K}`},signal:AbortSignal.timeout(3e4)});if(c({label:"container",mark:"wait",detail:`waiting for ${ve.workspace} to answer`}),!await os(r,600,async()=>(await ht("/_moa/box")).ok))return l("container",`${v} 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!==Be||vn!==rt)&&await ht("/_moa/restart",{method:"POST"}).catch(()=>{}),c({label:"container",mark:"wait",detail:"starting the workspace and console in it"});let Pt={};if(!await os(r,600,async()=>(Pt=await(await r.fetch(`${v}/health`,{signal:AbortSignal.timeout(13e4)})).json().catch(()=>({})),Pt.ok===!0)))return l("container",`the workspace did not come up: ${$o(String(Pt.error??"no answer"),1)}`,`look at \`pnpm --dir ${R} exec wrangler tail ${Ye}\`, then Enter`);if(await ns(de,`${rt}
|
|
196
|
+
`,{mode:384}),i={...i,workspaceFor:Be},n(i),c({label:"container",mark:"ok",detail:`${v} \u2014 kept up ${It}h after the last request`}),c({label:"database",mark:"ok",detail:`${Pt.database??"d1"} \u2014 migrated, and it answers`}),!await Ve.get("/v1/whoami").then(()=>!0,()=>!1))return l("workspace","it answers but refuses the owner token","Enter to try again");re&&await Xt(r,Wn,v,K);let jt=await mn(Ve,s,c,he);if(jt)return l(jt.label,jt.detail,jt.fix);i={...i,people:s.answers.people??[]},n(i);let Qn=[...new Set([g,...s.answers.loggedIn??[]])];if(Qn.includes("codex")&&!Ee(e,"codex"))if((await ht("/_moa/codex").then(J=>J.json()).catch(()=>({}))).loggedIn)c({label:"codex",mark:"ok",detail:"logged in inside the container \u2014 kept in R2"});else{let J=await $u(r,v,K,c).catch(Se=>({ok:!1,why:Se instanceof Error?Se.message:String(Se)}));if(!J.ok)return l("codex",J.why,"Enter to log in again");c({label:"codex",mark:"ok",detail:"logged in inside the container \u2014 saved to R2"})}let _t=await Nt(Ve,Qn,"in the Cloudflare container",M=>M==="claude"?"Esc back to Agent logins and press t for a new token, then Enter":"Enter to log codex in again",c);if("label"in _t)return l(_t.label,_t.detail,_t.fix);let Ht=await gn(Ve,c);if("label"in Ht)return l(Ht.label,Ht.detail,Ht.fix);let Ot=Ht.live??Ge;if(!await os(r,180,async()=>(await r.fetch(C,{redirect:"manual",signal:AbortSignal.timeout(13e4)})).status<500))return l("console",`${C} did not answer`,`look at \`pnpm --dir ${R} exec wrangler tail ${Ye}\`, then Enter`);c({label:"Console",mark:"ok",detail:`${C} \u2014 Sign in with GitHub`}),c({label:"Workspace",mark:"ok",detail:v}),c({label:"Next",mark:"skip",detail:`moa whoami \xB7 moa agent list \xB7 moa run ${Gn(g)} "\u2026"`});let dt=_t.runs[g];return{ok:!0,deployed:{runner:"cloudflare",workspaceUrl:v,consoleUrl:C,workspaceFor:Be,...dt?{proofRunId:dt}:{},memory:Ht.memory,people:s.answers.people??[],...Ot?{memoryKept:!0}:{},cloudflare:T,at:new Date().toISOString()}}}Qe();import{randomBytes as Ki}from"node:crypto";import{chmod as Su,mkdir as xu,rm as Au,writeFile as Eu}from"node:fs/promises";import{createServer as Cu}from"node:http";import{dirname as Pu}from"node:path";function _u(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:{url:`${o}/api/github/webhook`,active:!1}}}var Ou=(e,t)=>`https://github.com${e?`/organizations/${encodeURIComponent(e)}`:""}/settings/apps/new?state=${encodeURIComponent(t)}`,Tu=(e,t)=>t?`https://github.com/organizations/${encodeURIComponent(t)}/settings/apps/${e}`:`https://github.com/settings/apps/${e}`,rs=e=>e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]),Iu=(e,t)=>`<!doctype html>
|
|
197
197
|
<meta charset="utf-8"><title>moa setup \u2014 GitHub App</title>
|
|
198
|
-
<form id="f" method="post" action="${
|
|
199
|
-
<input type="hidden" name="manifest" value="${
|
|
198
|
+
<form id="f" method="post" action="${rs(e)}">
|
|
199
|
+
<input type="hidden" name="manifest" value="${rs(JSON.stringify(t))}">
|
|
200
200
|
<p>Taking you to GitHub to create the app\u2026 <button type="submit">Go</button></p>
|
|
201
201
|
</form>
|
|
202
202
|
<script>document.getElementById("f").submit()</script>
|
|
203
|
-
`,
|
|
204
|
-
`,{mode:384}),await
|
|
203
|
+
`,Vo=e=>`<!doctype html><meta charset="utf-8"><title>moa setup</title><p>${rs(e)}</p>`;async function Ru(e){let t=Ki(16).toString("hex"),n,o=new Promise((i,l)=>{n={ok:i,no:l}}),r=Cu((i,l)=>{let d=new URL(i.url??"/","http://127.0.0.1"),u=(p,m,y)=>{l.writeHead(p,{"content-type":"text/html; charset=utf-8"}),l.end(m,y)};if(d.pathname==="/"){let p=r.address().port,m=_u(e.consoleUrl,e.name,`http://127.0.0.1:${p}/created`);return u(200,Iu(Ou(e.org,t),m))}if(d.pathname!=="/created")return u(404,Vo("Nothing here."));let f=d.searchParams.get("code");if(!f||d.searchParams.get("state")!==t)return u(400,Vo("That is not the app moa setup asked for. Try again from setup."));Lu(e.fetch,f).then(p=>u(200,Vo(`Created ${p.slug}. Back to the terminal.`),()=>n?.ok(p)),p=>u(502,Vo(`GitHub would not hand the app over: ${p.message}`),()=>n?.no(p)))});await new Promise((i,l)=>{r.once("error",l),r.listen(0,"127.0.0.1",()=>i())});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),c=()=>n?.no(new Error("stopped"));e.signal?.addEventListener("abort",c,{once:!0});try{return e.onPage?.(s),await e.open(s),await o}finally{clearTimeout(a),e.signal?.removeEventListener("abort",c),r.closeAllConnections(),r.close()}}async function Lu(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:c,html_url:i,owner:l}=o;if(typeof r!="number"||!s||!a||!c)throw new Error("GitHub's answer had no app in it");return{id:r,slug:s,clientId:a,clientSecret:c,htmlUrl:i??`https://github.com/apps/${s}`,...l?{owner:l}:{}}}async function Du(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 ss(e,t,n){let o=Wo(e,t);await xu(Pu(o),{recursive:!0}),await Eu(o,`${JSON.stringify(n,null,2)}
|
|
204
|
+
`,{mode:384}),await Su(o,384)}async function as(e,t){await Au(Wo(e,t),{force:!0})}async function Bi(e,t,n,o=r=>Du(e.sys,r)){let{state:r,sys:s}=e,a=r.answers.appOrg,c=r.answers.tenant??Ne(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 i=Bo(r),l=await Ru({consoleUrl:i,name:`fnmoa-${c}`.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?on(s,d)?.authSecret:void 0)??Ki(32).toString("base64url");await ss(s,l,{clientSecret:l.clientSecret,authSecret:u}),d&&d.id!==l.id&&await as(s,d);let f=l.owner?.type==="Organization"?l.owner.login:a;return{githubApp:{id:l.id,slug:l.slug,clientId:l.clientId,settingsUrl:Tu(l.slug,f),consoleUrl:i}}}ct();Lt();Vt();Qe();io();ct();Xe();pn();var Uu={local:"This machine",vercel:"Vercel Sandbox",cloudflare:"Cloudflare"};function Wi(e){let{state:t,sys:n}=e,{answers:o}=t,r=t.target??"local",s=[{label:"Target",value:Uu[r]}];s.push({label:"fnMoa",value:Ue(e)?Re():`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:Ie(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 ${Zt(t)} \xB7 console ${Mt(t)}`});let a=ut.filter(i=>i!=="deploy"),c=a.filter(i=>t.passed.includes(i)).length;return s.push({label:"Checks",value:`${c} 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 ${It}h after the last request`:r==="vercel"?Go(xt(o.vercelPlan??"hobby")):"nothing billed \u2014 it runs on this machine"}),s}Xe();je();Ze();Qe();Qt();Lt();Ln();Xe();pn();Qe();ct();Vt();Xe();pn();Qe();ct();Lt();pn();import{randomUUID as Nu}from"node:crypto";import{mkdtemp as Ji,readFile as Mu,rm as qi,writeFile as Gu}from"node:fs/promises";import{tmpdir as zi}from"node:os";import{join as Jn}from"node:path";async function ju(e,t){if(!t.writeFiles)throw new Error("This sandbox SDK cannot upload the release archive.");let n=await Ji(Jn(zi(),"fnmoa-image-")),o=`fnmoa-export-${Nu()}`,r=!1;try{if(!(await e.sys.exec("docker",["create","--platform","linux/amd64","--name",o,Re()])).ok)throw new Error("Could not open the release image.");if(r=!0,!(await e.sys.exec("docker",["cp",`${o}:/repo`,Jn(n,"repo")],{timeoutMs:6e5})).ok)throw new Error("Could not export the built release.");if(!(await e.sys.exec("tar",["-czf",Jn(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 Mu(Jn(n,"release.tgz"))}]),await jo(t),!(await He(t,`set -eu
|
|
205
205
|
mkdir -p "$HOME/moa/release-next"
|
|
206
206
|
tar -xzf /tmp/fnmoa-release.tgz -C "$HOME/moa/release-next"
|
|
207
207
|
rm -f "$HOME/moa/fnmoa"
|
|
208
208
|
ln -s "$HOME/moa/release-next/repo" "$HOME/moa/fnmoa"
|
|
209
|
-
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
|
|
209
|
+
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 qi(n,{recursive:!0,force:!0})}}async function Vi(e,t,n){let o=e.sys.vercel;if(!o)return P("sandbox","Vercel SDK unavailable","Update the CLI");try{for(let i of await vt(e))if(i.mark==="fail")return i;let r=go(e.state.answers.tenant??e.state.answers.owner??"workspace"),s=await o.get(t,r,!0),a=qt();if(s&&await Ho(s)===a)return{box:s,built:!1,info:{name:r,workspaceUrl:s.domain(8787),consoleUrl:s.domain(3100),revision:a}};for(let i of await Fn(e))if(i.mark==="fail")return i;if(s)n("Updating the application files; keeping the sandbox's data disk"),await ju(e,s);else{let i=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(!i.ok)throw new Error("Could not resolve the Vercel project's registry name.");let{name:l}=await i.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 Ji(Jn(zi(),"fnmoa-vcr-"));try{if(await Gu(Jn(u,"config.json"),JSON.stringify({auths:{"vcr.vercel.com":{auth:Buffer.from(`${t.teamId}:${t.token}`).toString("base64")}}}),{mode:384}),n(`Copying ${Re()} to Vercel`),!(await e.sys.exec("docker",["tag",Re(),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 qi(u,{recursive:!0,force:!0})}if(n("Starting the sandbox from the release image"),s=await o.create(t,{name:r,size:xt(t.plan),image:`fnmoa:${a}`}),!(await He(s,`set -eu
|
|
210
210
|
mkdir -p "$HOME/moa"
|
|
211
|
-
ln -s /repo "$HOME/moa/fnmoa"`)).ok)throw new Error("Could not prepare the sandbox's application directory.")}if(!(await
|
|
212
|
-
`)[0]??"":String(p),"check the account on Checks & account, then press r")}let f=!1;if(await Mo(l)!==r.revision){n("stopping the servers before the build");try{await No(l)}catch(y){return C("sandbox",`the servers did not stop: ${y instanceof Error?y.message:String(y)}`,"press r to try again")}n(`building the image at ${r.revision.slice(0,7)} \u2014 several minutes the first time`);let p=await zo(e),g=await gi(l,r.url,r.revision,p&&"token"in p?p.token:void 0);if(!g.ok)return C("sandbox",`the image did not build: ${At(g.stderr||g.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(ft),consoleUrl:l.domain(dn),...d?{snapshotId:d,teamId:t.teamId,projectId:t.projectId}:{},revision:r.revision}}}var Gu=async(e,t)=>(await Me(e,`test -s "$HOME/moa/env/workspace/${t}"`)).ok;async function ju(e,t){return Me(e,t?'claude -p "$PROMPT"':'CLAUDE_CODE_OAUTH_TOKEN=$(cat "$HOME/moa/env/workspace/CLAUDE_CODE_OAUTH_TOKEN") exec claude -p "$PROMPT"',{PROMPT:In,...t?{CLAUDE_CODE_OAUTH_TOKEN:t}:{}},18e4)}async function Hu(e){let{state:t}=e,n=await is(e);if(qo(n))return{lines:[n]};let o=await cs(e,n);if(qo(o))return{lines:[o]};let{box:r,info:s}=o,a=xt(n.plan),i=[S("sandbox",`${s.name} \u2014 ${r.vcpus??a.vcpus} vCPU, persistent, fnMoa ${s.revision?.slice(0,7)??""}`)],c={claude:!1,codex:!1,cursor:!1},l=Ze(e),d=!l&&await Gu(r,"CLAUDE_CODE_OAUTH_TOKEN");if(l||d){let p=await ju(r,l?.token);c.claude=p.ok&&/\bok\b/i.test(p.stdout),i.push(c.claude?S("claude",`token ${U} (${l?l.from:"kept in the sandbox"}) \u2014 "reply ok" in the sandbox`):C("claude",`the token did not answer: ${At(p.stdout||p.stderr)||"no answer"}`,"press c to make a new one with `claude setup-token`, or k for an API key"))}else be(e,"claude")?(c.claude=!0,i.push(S("claude",`ANTHROPIC_API_KEY ${U} \u2014 carried into the sandbox`))):i.push(j("claude","not connected \u2014 c for your Claude subscription, or k for an API key"));if(be(e,"codex"))c.codex=!0,i.push(S("codex",`OPENAI_API_KEY ${U} \u2014 carried into the sandbox`));else{let p=await r.exec("codex",["login","status"],{timeoutMs:6e4});c.codex=p.ok,i.push(p.ok?S("codex",`${ae(p.stdout)||"logged in"} \u2014 its own session in the sandbox (c to log in again)`):j("codex","not connected \u2014 c to log in with a code (ChatGPT), or k for an API key"))}if(be(e,"cursor"))c.cursor=!0,i.push(S("cursor",`CURSOR_API_KEY ${U} \u2014 carried into the sandbox`));else{let p=await r.exec("cursor-agent",["status"],{timeoutMs:6e4});c.cursor=p.ok&&!/not logged in/i.test(`${p.stdout}${p.stderr}`),i.push(c.cursor?S("cursor","logged in \u2014 its own session in the sandbox (c to log in again)"):j("cursor","not connected \u2014 c to log in with your Cursor account, or k for an API key"))}let u=pe.filter(p=>c[p]),f=t.answers.agentCli??u[0];return f?i.push(c[f]?S("proof run",`uses ${f} (\u2191 \u2193 to change) \xB7 ${u.length} of 3 connected`):C("proof run",`${f} is not connected in the sandbox`,"press c to connect it or k for its API key \u2014 or \u2191 \u2193 to another agent")):i.push(C("agent","no agent is connected in the sandbox","\u2191 \u2193 to an agent, then c to connect it or k for its API key")),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:u}}}function Vi(e,t,n="codex"){let o=!1,r=()=>{o=!0};return{done:(async()=>{let a=await is(e);if(qo(a))return{ok:!1,stdout:"",stderr:a.detail??"no account"};let i=e.state.answers.sandbox?.name??fo(as(e.state)),c=await ss(e.sys).get(a,i,!0);if(!c)return{ok:!1,stdout:"",stderr:`${i} is not there`};if(o)return{ok:!1,stdout:"",stderr:"stopped"};let l="",d=n==="codex"?c.stream("codex",["login","--device-auth"],u=>{l+=u,t(ln(l))}):c.stream("env",["NO_OPEN_BROWSER=1","cursor-agent","login"],u=>{l+=u,t(jo(l))});return r=d.cancel,d.done})(),cancel:()=>r()}}async function Fu(e){let{sys:t,state:n,secrets:o}=e,r=Ce(e),s=[],a=Yr(e);if(a)s.push(a);else if(r){let c=await Rn(t,r,as(n)),l=`secret ${U} (${r.from}), graph ${r.graph}`;s.push(c.read.ok&&c.write.ok?S("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 g to check the 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","not connected \u2014 memory stays in the workspace process (g for the graph ID, e for its secret)"));let i=n.answers.store??"postgres";return s.push(i==="postgres"?S("database","Postgres, in the sandbox on its kept disk \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):S("database","SQLite, on the sandbox's kept disk \u2014 migrated at deploy (\u2191 \u2193 for Postgres)")),{lines:s,answers:{store:i}}}function Ku(e){let t=Qr(e),{githubApp:n,sandbox:o}=e.state.answers,r=[...t.lines];return o?wn(e.state)==="password"?r.unshift(S("console",`${o.consoleUrl} \u2014 you sign in there with your password`)):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(S("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 Yi={...Ko,checks:Nu,github:Mu,logins:Hu,memory:Fu,access:Ku};var kn="vercel";async function Vo(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 Bu=e=>typeof e=="object"&&e!==null&&"mark"in e;async function Yo(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 yn=e=>typeof e=="object"&&e!==null&&"fix"in e&&"label"in e;async function Xo(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=F=>{let je=s.findIndex(nt=>nt.label===F.label);je===-1?s.push(F):s[je]=F,t([...s])},i,c=(F,je,nt)=>(a({label:F,mark:"fail",detail:je,fix:nt}),i?{ok:!1,deployed:i}:{ok:!1}),l=await Zt(e);if("why"in l)return c("console sign-in",l.why,l.fix);if(l.mode==="none")return c("console sign-in","none \u2014 a public console needs somebody to sign in","Esc back to Console access: \u2191 \u2193 to password, or press g for a GitHub App");let d=l.mode==="github"?r.answers.githubApp:void 0;a({label:"sandbox",mark:"wait",detail:"looking for it"});let u=await Yo(e);if(yn(u))return c(u.label,u.detail,u.fix);let f=await cs(e,u,F=>a({label:"sandbox",mark:"wait",detail:F}));if(Bu(f))return c("sandbox",f.detail??"",f.fix??"Enter to try again");let{box:p,info:g,built:y}=f,b=xt(u.plan);a({label:"sandbox",mark:"ok",detail:`${g.name} \u2014 ${p.vcpus??b.vcpus} vCPU, persistent${y?", image built":""}`});let m=g.workspaceUrl,w=g.consoleUrl;if(d&&d.consoleUrl!==w)return c("GitHub App",`${d.slug} signs people in at ${d.consoleUrl??"localhost"}, not ${w}`,"Esc back to Console access and press g");let{token:v,pending:_,before:H,had:W}=await Vt(o,kn,m);a({label:"owner token",mark:"ok",detail:`${U} \u2014 profile ${kn} in ~/.moa/config.json${H===kn||!W?"":`; \`moa\` now talks to the sandbox, not profile ${H} (\`moa use ${H}\` to go back)`}`});let le=r.answers.tenant??Ie(r.answers.owner),L=r.answers.owner,X=Ce(e),D=Ze(e)?.token,B=await zo(e),oe={...Object.fromEntries(Object.entries(await qt(e)).map(([F,je])=>[`workspace/${F}`,je])),store:r.answers.store??"postgres","workspace/MOA_TOKENS":`${v}:${le}`,"workspace/PORT":String(ft),"workspace/MOA_PORT":String(ft),"workspace/MOA_SELF_URL":`http://127.0.0.1:${ft}`,"workspace/MOA_MAX_CONCURRENT_RUNS":String(b.maxRuns),"workspace/MOA_REPO":r.answers.repo,"workspace/MOA_OWNER_LOGIN":L,"workspace/MOA_GITHUB_CLIENT_ID":l.vars.MOA_GITHUB_CLIENT_ID,"workspace/LOCUSGRAPH_AGENT_SECRET":X?.secret,"workspace/LOCUSGRAPH_GRAPH_ID":X?.graph,"workspace/LOCUSGRAPH_SERVER_URL":X?.server,"workspace/CLAUDE_CODE_OAUTH_TOKEN":D,...Object.fromEntries(pe.map(F=>[`workspace/${Ee[F]}`,be(e,F)])),"workspace/GH_TOKEN":B&&"token"in B?B.token:void 0,"console/MOA_URL":`http://127.0.0.1:${ft}`,"console/MOA_TOKEN":v,"console/AUTH_GITHUB_ID":l.vars.AUTH_GITHUB_ID,"console/AUTH_GITHUB_SECRET":l.secrets.AUTH_GITHUB_SECRET,"console/AUTH_SECRET":l.secrets.AUTH_SECRET,"console/MOA_CONSOLE_LOGIN":l.vars.MOA_CONSOLE_LOGIN,"console/MOA_CONSOLE_PASSWORD_HASH":l.secrets.MOA_CONSOLE_PASSWORD_HASH,"console/AUTH_URL":w,"console/AUTH_TRUST_HOST":"true"};if(!B)return c("secrets","no GitHub token for the sandbox","Esc back to GitHub and press k to paste one");let he=[...["workspace/MOA_GITHUB_CLIENT_ID","console/AUTH_GITHUB_ID","console/AUTH_GITHUB_SECRET","console/MOA_CONSOLE_LOGIN","console/MOA_CONSOLE_PASSWORD_HASH"].filter(F=>oe[F]===void 0),"workspace/MOA_CLIENT_ADDRESS",...r.answers.repo?[]:["workspace/MOA_REPO"],...L?[]:["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 Z=await fi(p,oe,he);if(!Z.ok)return c("secrets",`not written: ${Z.why??"no answer"}`,"Enter to try again");let ve=["workspace/MOA_TOKENS","workspace/LOCUSGRAPH_AGENT_SECRET","workspace/CLAUDE_CODE_OAUTH_TOKEN",...pe.map(F=>`workspace/${Ee[F]}`),"workspace/GH_TOKEN","console/MOA_TOKEN","console/AUTH_GITHUB_SECRET","console/AUTH_SECRET","console/MOA_CONSOLE_PASSWORD_HASH"].filter(F=>oe[F]!==void 0);a({label:"secrets",mark:"ok",detail:`${ve.length} written \u2014 none shown; no Vercel token in the sandbox`});let $=Z.changed||y;a({label:"workspace",mark:"wait",detail:$?`starting on :${ft}`:"checking it runs"});try{await Lr(p,$)}catch(F){return c("workspace",`the start did not run: ${F instanceof Error?F.message:String(F)}`,"Enter to try again")}let I=r.deployed?.workspaceUrl===m?r.deployed.people:void 0,re=F=>{i={runner:"vercel",workspaceUrl:m,consoleUrl:w,...I?{people:I}:{},...F?{memoryKept:F}:{},at:new Date().toISOString()},n(i)},O=r.deployed?.runner==="vercel"?r.deployed.memoryKept:void 0,V=X?!0:e.secrets.noLocusgraph?!1:O;if(re(V),!await Vo(o,`${m}/health`,180))return c("workspace",`did not answer at ${m} \u2014 ${await Dn(p,"workspace")||"no log yet"}`,"fix that, then Enter");let fe=N({url:m,token:v},o.fetch);if(!await fe.get("/v1/whoami").then(()=>!0,()=>!1))return c("workspace","it started but refuses the owner token","Enter to try again");_&&await Yt(o,kn,m,v),a({label:"workspace",mark:"ok",detail:`${m} \u2014 :${ft} in the sandbox`});let Se=await Mr(o,m,a,r.answers.store??"postgres",`look at the sandbox's ~/moa/logs \u2014 ${await Dn(p,"postgres")}`);if(Se)return c(Se.label,Se.detail,Se.fix);let xe=await fn(fe,r,a,I);if(xe)return c(xe.label,xe.detail,xe.fix);let Ge=I;if(I=r.answers.people??[],JSON.stringify(I)!==JSON.stringify(Ge??[])&&re(V),a({label:"console",mark:"wait",detail:`waiting for :${dn}`}),!await Vo(o,w,180))return c("console",`did not answer at ${w} \u2014 ${await Dn(p,"console")||"no log yet"}`,"fix that, then Enter");a({label:"console",mark:"ok",detail:`${w} \u2014 Sign in with GitHub`}),$&&await mi(p);let Re=r.answers.agentCli??"claude",Ke=[...new Set([Re,...r.answers.loggedIn??[]])],ke=await Mt(fe,Ke,"in the sandbox",()=>"Esc back to Agent logins and log it in again, then Enter",a);if("label"in ke)return c(ke.label,ke.detail,ke.fix);let _e=await mn(fe,a);if("label"in _e)return c(_e.label,_e.detail,_e.fix);_e.live!==void 0&&(V=_e.live),a({label:"Console",mark:"ok",detail:w}),a({label:"Workspace",mark:"ok",detail:m}),a({label:"Next",mark:"skip",detail:`moa setup status \xB7 moa setup stop \xB7 moa run ${Gn(Re)} "\u2026"`});let T=ke.runs[Re];return{ok:!0,deployed:{runner:"vercel",workspaceUrl:m,consoleUrl:w,...T?{proofRunId:T}:{},memory:_e.memory,people:I,...V?{memoryKept:V}:{},at:new Date().toISOString()}}}async function Qo(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 Yo(e);if(yn(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 Xi(e,t){let{sys:n,state:o}=e,r=await Qo(e,!0);if(yn(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 Lr(s,!1)}catch(y){return t({label:"workspace",mark:"fail",detail:`the start did not run: ${At(y instanceof Error?y.message:String(y))}`,fix:"try again, or run `moa setup` to deploy it again"}),!1}let i={workspace:s.domain(ft),console:s.domain(dn)};if(i.workspace!==a.workspaceUrl||i.console!==a.consoleUrl){let y=[...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 ${y.join("; ")}`,fix:"run `moa setup` to deploy it again, and press g on Console access for the new callback"}),!1}if(!await Vo(n,`${a.workspaceUrl}/health`,180))return t({label:"workspace",mark:"fail",detail:`no answer at ${a.workspaceUrl} \u2014 ${await Dn(s,"workspace")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!1;let l=(await Ae(n.env)).profiles[kn];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=N(l,n.fetch),u=await n.fetch(`${a.workspaceUrl}/health`,{signal:AbortSignal.timeout(5e3)}).then(y=>y.json()).catch(()=>({})),f=await d.get("/v1/whoami").then(()=>{},y=>y instanceof ie?`${y.status}`:String(y));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(y=>y.jobs).catch(()=>{});if(p){let y=p.filter(b=>b.state==="running"||b.state==="queued");t({label:"pipelines",mark:"ok",detail:y.length===0?"nothing running":`${y.length} running or waiting \u2014 carried on from before the stop (moa jobs)`})}let g=await Vo(n,a.consoleUrl,180);return t(g?{label:"console",mark:"ok",detail:`${a.consoleUrl} \u2014 Sign in with GitHub`}:{label:"console",mark:"fail",detail:`no answer at ${a.consoleUrl} \u2014 ${await Dn(s,"console")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!f&&g}async function Qi(e,t){let n=await Qo(e,!1);if(yn(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: ${At(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 ne,jsxs as ge}from"react/jsx-runtime";var $e=(e,t)=>({...e,answers:{...e.answers,...t}}),Vu=[{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"}],Yu={where:{options:e=>Vu.map(t=>{let n=Oo(t.value,uo(e,t.value));return n?{...t,note:n}:t}),value:e=>e.target,set:(e,t)=>st(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=>On(e),set:(e,t)=>$e(e,{source:t})},logins:{options:()=>[{value:"claude",label:"claude",note:"a Claude subscription, or an API key"},{value:"codex",label:"codex",note:"a ChatGPT plan, or an API key"},{value:"cursor",label:"cursor",note:"a Cursor plan, or an API key"}],value:e=>e.answers.agentCli,set:(e,t)=>$e(e,{agentCli:t})},access:{options:e=>[...e.target==="local"?[{value:"none",label:"nobody",note:"the console stays on this machine, no sign-in"}]:[],{value:"password",label:"password",note:"just you: your GitHub login and a password"},{value:"github",label:"GitHub App",note:"a team: everyone signs in with GitHub"}],value:e=>wn(e)??"none",set:(e,t)=>$e(e,{consoleSignIn:t==="none"?void 0: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"?$e(e,{store:t}):$e(e,{runner:t})}},ds=e=>e.target==="vercel",nc=/^[a-z\d](?:[a-z\d-]{0,38})$/i;function Xu(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(y=>!o(f)(y)),g=[...new Set([...e.answers.removed??[],f.toLowerCase()])];return $e(e,{people:p.length>0?p:void 0,removed:g})}if(!nc.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??xr;for(let f of a){let p=f.toLowerCase(),g=Number(p.replace(/^\$|\/day$/g,""));if(p==="member"||p==="viewer")c=p;else if(Number.isFinite(g)&&g>=0&&g<=1e4)l=g;else return r}let d={login:s,role:c,dailyCapUsd:l},u=(e.answers.removed??[]).filter(f=>f!==s.toLowerCase());return $e(e,{people:i?n.map(f=>o(s)(f)?d:f):[...n,d],removed:u.length>0?u:void 0})}var Zo=e=>e.target==="cloudflare",Qu="\x1B",Zu=new RegExp(`(?:${Qu})?\\[20[01]~|[\\u0000-\\u001f\\u007f]`,"g"),Zi=e=>e.replace(Zu,""),ep={checks:[{key:"l",label:"Licence key",secret:!0,shown:(e,t)=>t.licence?U:"saved key, or none",commit:(e,t,n)=>(t.licence=n||void 0,e)},{key:"d",label:"Domain",when:Zo,shown:e=>e.answers.domain??"none",commit:(e,t,n)=>{let o=n.toLowerCase().replace(/^https?:\/\//,"").replace(/\/+$/,"");return o===""?$e(e,{domain:void 0}):mr.test(o)?$e(e,{domain:o}):"a domain on your Cloudflare account, like example.com"}},{key:"k",label:"D1 token",secret:!0,when:Zo,shown:(e,t)=>t.cloudflare?U:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces":(t.cloudflare=n||void 0,e)},{key:"e",label:"Vercel team",when:ds,shown:e=>e.answers.vercelTeam??"the one Vercel picks",initial:e=>e.answers.vercelTeam??"",commit:(e,t,n)=>n===""||/^[\w-]{1,100}$/.test(n)?$e(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)?$e(e,{repo:n}):"owner/name, like fnLog0/fnmoa"},{key:"k",label:"GitHub token",secret:!0,when:e=>ds(e)||Zo(e),shown:(e,t)=>t.github?U:ds(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?U:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces \u2014 what `claude setup-token` printed":(t.claude=n||void 0,e)},{key:"k",label:"API key",secret:!0,shown:(e,t)=>{let n=e.answers.agentCli??"claude";return`${Ee[n]} ${t.keys?.[n]?U:"none"}`},commit:(e,t,n)=>{if(/\s/.test(n))return"one key, no spaces";let o=e.answers.agentCli??"claude";return t.keys={...t.keys,[o]:n||void 0},e}}],memory:[{key:"g",label:"LocusGraph graph ID",shown:e=>e.answers.locusgraphGraph??"none",initial:e=>e.answers.locusgraphGraph??"",commit:(e,t,n)=>n===""||/^[\w.:-]{1,100}$/.test(n)?$e(e,{locusgraphGraph:n||void 0}):"the graph's ID, as LocusGraph shows it \u2014 letters, digits, - _ . :"},{key:"e",label:"LocusGraph secret",secret:!0,shown:(e,t)=>t.locusgraph?U:"none",commit:(e,t,n)=>(t.locusgraph=n||void 0,t.noLocusgraph=!n,e)}],access:[{key:"p",label:"Console password",secret:!0,when:e=>wn(e)==="password",shown:(e,t)=>t.consolePassword?U:"the one kept from a deploy, or none",commit:(e,t,n)=>n.length<12?"12 characters or more \u2014 it guards a console on the internet":(t.consolePassword=n,$e(e,{consoleSignIn:"password"}))},{key:"e",label:"Tenant",shown:e=>e.answers.tenant??"",commit:(e,t,n)=>/^[a-z0-9][a-z0-9-]{0,62}$/.test(n)?$e(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===""||nc.test(n)?$e(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)=>Xu(e,n)}]},us=(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},ec=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],tp=100,np=e=>{let t=Math.floor(e/1e3);return`${Math.floor(t/60)}:${String(t%60).padStart(2,"0")}`},op=(e,t=16)=>{let n=e.total>0?Math.min(1,e.done/e.total):0,o=Math.round(n*t);return`${"\u2593".repeat(o)}${"\u2591".repeat(t-o)} ${e.done}/${e.total} ${e.unit}`},tc={ok:{char:"\u2713",color:"green"},fail:{char:"\u2717",color:"red"},wait:{char:"\u2026",color:"yellow"},skip:{char:"\u2013",color:"gray"}},ps={where:"Where",checks:"Checks",github:"GitHub",memory:"Memory",logins:"Logins",access:"Access",deploy:"Deploy"},rp=Object.fromEntries(ut.filter(e=>e!=="where"&&e!=="deploy").map((e,t)=>[String(t+1),e])),sp={local:"this machine",vercel:"the Vercel Sandbox",cloudflare:"Cloudflare"},ap={ok:"ok",fail:"failing",wait:"checking"};function ip({screen:e,passed:t}){return ne(Ve,{columnGap:2,flexWrap:"wrap",children:ut.map(n=>n===e?ge(te,{bold:!0,color:"cyan",children:["\u25B8 ",ps[n]]},n):t.includes(n)?ge(te,{color:"green",children:["\u2713 ",ps[n]]},n):ge(te,{dimColor:!0,children:[" ",ps[n]]},n))})}function er({title:e,aside:t,children:n}){return ge(Ve,{flexDirection:"column",marginTop:1,children:[ge(Ve,{justifyContent:"space-between",children:[ne(te,{bold:!0,dimColor:!0,children:e.toUpperCase()}),t?ne(te,{dimColor:!0,children:t}):null]}),n]})}function cp({rows:e}){let t=Math.max(...e.map(n=>n.label.length));return ne(er,{title:"review",children:e.map(n=>ge(Ve,{children:[ne(Ve,{flexShrink:0,width:t+2,children:ne(te,{dimColor:!0,children:n.label})}),ne(te,{children:n.value})]},n.label))})}function lp({hints:e}){return ne(Ve,{columnGap:2,flexWrap:"wrap",paddingX:1,children:e.map(t=>{let n=t.indexOf(" ");return ge(te,{children:[ne(te,{bold:!0,children:t.slice(0,n)}),ne(te,{dimColor:!0,children:t.slice(n)})]},t)})})}var dp=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 oc({initial:e,sys:t,save:n,checks:o,deploy:r,createApp:s=ji,onDone:a,secrets:i,onHandOver:c,pasteFirst:l=!1,deviceLogin:d=ni,after:u={}}){let{exit:f}=Wu(),[p,g]=it(()=>e.screen==="where"&&!e.target?{...e,target:"local"}:e),[y,b]=it([]),[m,w]=it(!1),[v,_]=it(0),H=l&&e.screen==="logins",[W,le]=it(H?"":void 0),[L,X]=it(H?"e":void 0),D=mt(void 0),B=mt([]),oe=h=>us(h,B.current),[he,Z]=it(void 0),[ve,$]=it(!1),[I,re]=it("no"),O=mt(i??{}).current,V=mt(void 0),[fe,ee]=it(0),Se=mt(Promise.resolve()),xe=mt(0),[Ge,Re]=it(!1),Ke=mt(!1),ke=h=>{Ke.current=h,Re(h)},_e=mt(e.screen!=="where").current,T=p.screen,F=p.target==="vercel",je=o??(F?Yi:Ko),nt=r??(F?Xo:p.target==="cloudflare"?Wo:Go),ot=T==="checks"&&Te({sys:t,state:p}),gt=F||p.answers.runner==="compose",me=mt(p);me.current=p;let $n=mt(p),Q=h=>{$n.current!==h&&($n.current=h,Se.current=Se.current.then(()=>n(h)).catch(()=>{}))};ls(()=>Q(p),[p,n]);let Le=()=>(Q(me.current),Se.current),{answers:ue}=p,Be=JSON.stringify([T,p.target,ue,v,fe]);ls(()=>{if(T==="deploy"){m||b(dp(p));return}let h=++xe.current;b(x=>x.map(q=>B.current.some(M=>M.label===q.label)?q:{...q,mark:"wait"})),w(!0),Promise.resolve(je[T]?.({state:p,sys:t,secrets:O})??{lines:[]}).then(async x=>({...x,lines:[...x.lines,...!o&&T==="checks"?await vt({state:p,sys:t,secrets:O}):[]]})).then(x=>{if(h!==xe.current)return;let q=oe(x.lines);b(q),D.current||w(!1);let M=x.answers??{};Object.entries(M).some(([dt,Sn])=>p.answers[dt]!==Sn)&&g(dt=>$e(dt,M));let He=Ke.current;ke(!1),He&&ko(q)&&Qn(T)},x=>{h===xe.current&&(b(oe([{label:"check",mark:"fail",detail:String(x),fix:"press r to check again"}])),D.current||w(!1),ke(!1))})},[Be]);let ht=h=>{D.current?.abort(),Le().then(()=>{a?.(me.current,h),f()})},ks=()=>{w(!0),Z(void 0);let h=M=>b([{label:"licence",mark:"wait",detail:M},{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 x=new AbortController,q={done:To({state:p,sys:t,secrets:O},M=>h(`open ${M.url} and check it shows ${M.code}`),x.signal).then(()=>({ok:!0,stdout:"",stderr:""}),M=>({ok:!1,stdout:"",stderr:M instanceof Error?M.message:String(M)})),cancel:()=>x.abort()};V.current=q,q.done.then(M=>{V.current===q&&(V.current=void 0,w(!1),M.ok||Z(`not connected: ${M.stderr}`),_(R=>R+1))})},Pt=(h="codex")=>{let x=Te({sys:t,state:p})?void 0:t.root,q=!x&&p.deployed?.runner==="image";if(!x&&!F&&!q)return;w(!0);let M=F?"sandbox":"container",R=dt=>b([{label:h,mark:"wait",detail:Bn(dt)??`starting its login inside the ${M}\u2026`},{label:"note",mark:"skip",detail:"its own session, not a copy of this machine's \u2014 Esc to stop"}]);R({});let He=F?Vi({state:p,sys:t,secrets:O},R,h):q?Kn(t,h,R):d(t,x,R);V.current=He,He.done.then(dt=>{V.current===He&&(V.current=void 0,w(!1),dt.ok||Z(`${h} login did not finish: ${(dt.stderr||dt.stdout).trim().split(`
|
|
213
|
-
`).slice(-1)[0]??""}`),
|
|
214
|
-
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),zn=e=>e instanceof Error?e.message:String(e);function
|
|
211
|
+
ln -s /repo "$HOME/moa/fnmoa"`)).ok)throw new Error("Could not prepare the sandbox's application directory.")}if(!(await He(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 P("sandbox",r instanceof Error?r.message:String(r),"Check your account and retry; data is kept.")}}var is=e=>{if(!e.vercel)throw new Error("this build of moa cannot reach Vercel");return e.vercel},cs=e=>e.answers.tenant??Ne(e.answers.owner),Yi=e=>{let t=/^git@([^:]+):(.+)$/.exec(e.trim());return t?`https://${t[1]}/${t[2]}`:e.trim()};async function Qi(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=(...i)=>e.exec("git",["-C",t,...i]),[o,r,s]=await Promise.all([n("rev-parse","HEAD"),n("remote","get-url","origin"),n("branch","-r","--contains","HEAD")]),a=le(o.stdout),c=le(r.stdout);return!o.ok||!a||!r.ok||!c?{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 ${Yi(c)} \u2014 the sandbox clones it from there`,fix:"push it, or check out a commit that is pushed, then press r"}:{url:Yi(c),revision:a}}async function ls(e){try{let{vercelTeam:t,vercelScope:n}=e.state.answers;return await is(e.sys).account(t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return P("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 Yo=e=>typeof e=="object"&&e!==null&&"mark"in e;async function Hu(e){let{sys:t}=e,o=[Number(process.versions.node.split(".")[0])>=22?S("node",process.versions.node):P("node",process.versions.node,"install node 22 or newer"),...await Promise.all([wn(t,"git","install git"),wn(t,"gh","install the GitHub CLI: https://cli.github.com"),wn(t,"vercel","install the Vercel CLI: npm i -g vercel")])],r=Ue(e)?{url:"GHCR",revision:Re()}:await Qi(t);o.push("why"in r?P("fnMoa",r.why,r.fix):S("fnMoa",`${r.revision.slice(0,7)} from ${r.url} \u2014 built in the sandbox`));let s=await ls(e);if(Yo(s))return o.push(s),{lines:o};let a=xt(s.plan);return o.push(S("account",`${s.user} \xB7 team ${s.team} \xB7 ${gi(s.plan,a)}`),S("estimate",Go(a)),S("storage",hi(a))),s.plan==="hobby"&&o.push(H("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 Xo(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=le(t.stdout);return t.ok&&n?{token:n,from:"own"}:void 0}async function Xi(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 Fu(e){let{sys:t,state:n}=e,o=await t.exec("gh",["api","user","--jq",".login"]),r=le(o.stdout);if(!o.ok||!r)return{lines:[P("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=[S("owner",`${r} \u2014 you, the first owner`)],a=await Xo(e),c,i;if(!a)s.push(P("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)i="pasted",s.push(H("GitHub token","the one pasted before, kept in the sandbox (k to replace it)"));else{let d=await Xi(t,a.token,"/user");typeof d=="number"?s.push(P("GitHub token",`GitHub refused it${d?` (${d})`:""}`,"press k to paste another \u2014 contents, pull requests, issues read/write; checks, actions read")):(c=a.token,i=a.from,s.push(S("GitHub token",a.from==="pasted"?`${D} \u2014 ${d.login??"?"}${d.login?.toLowerCase()===r.toLowerCase()?" (your own)":" (bot account)"}`:`${D} \u2014 your own gh login; a bot account's is better (k to paste one)`)))}let l=n.answers.repo;if(!l)s.push(H("repo","none \u2014 a pipeline can name any repository later (e to set one)"));else if(c){let d=await Xi(t,c,`/repos/${l}`);s.push(typeof d!="number"&&d.permissions?.push?S("repo",`${l} \u2014 the token can push`):P("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,...i?{githubToken:i}:{}}}}async function ds(e,t,n=()=>{}){if(Ue(e))return Vi(e,t,n);let o=is(e.sys),r=await Qi(e.sys);if("why"in r)return P("sandbox",r.why,r.fix);let s=go(cs(e.state)),a=e.state.answers.sandbox?.name===s?e.state.answers.sandbox:void 0,c=a?.teamId===t.teamId&&a.projectId===t.projectId?a.snapshotId:void 0,i=xt(t.plan),l,d=c,u=!1;try{n(`looking for ${s}`),l=await o.get(t,s,!0),!l&&d&&(n(`making ${s} from its snapshot \u2014 ${i.vcpus} vCPU, persistent`),l=await o.create(t,{name:s,size:i,snapshotId:d}).catch(()=>{}),l||(d=void 0)),l||(n(`making ${s} \u2014 ${i.vcpus} vCPU, persistent`),l=await o.create(t,{name:s,size:i}),u=!0)}catch(p){return P("sandbox",p instanceof Error?p.message.split(`
|
|
212
|
+
`)[0]??"":String(p),"check the account on Checks & account, then press r")}let f=!1;if(await Ho(l)!==r.revision){n("stopping the servers before the build");try{await jo(l)}catch(y){return P("sandbox",`the servers did not stop: ${y instanceof Error?y.message:String(y)}`,"press r to try again")}n(`building the image at ${r.revision.slice(0,7)} \u2014 several minutes the first time`);let p=await Xo(e),m=await yi(l,r.url,r.revision,p&&"token"in p?p.token:void 0);if(!m.ok)return P("sandbox",`the image did not build: ${At(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(ft),consoleUrl:l.domain(un),...d?{snapshotId:d,teamId:t.teamId,projectId:t.projectId}:{},revision:r.revision}}}var Ku=async(e,t)=>(await He(e,`test -s "$HOME/moa/env/workspace/${t}"`)).ok;async function Bu(e,t){return He(e,t?'claude -p "$PROMPT"':'CLAUDE_CODE_OAUTH_TOKEN=$(cat "$HOME/moa/env/workspace/CLAUDE_CODE_OAUTH_TOKEN") exec claude -p "$PROMPT"',{PROMPT:In,...t?{CLAUDE_CODE_OAUTH_TOKEN:t}:{}},18e4)}async function Wu(e){let{state:t}=e,n=await ls(e);if(Yo(n))return{lines:[n]};let o=await ds(e,n);if(Yo(o))return{lines:[o]};let{box:r,info:s}=o,a=xt(n.plan),c=[S("sandbox",`${s.name} \u2014 ${r.vcpus??a.vcpus} vCPU, persistent, fnMoa ${s.revision?.slice(0,7)??""}`)],i={claude:!1,codex:!1,cursor:!1},l=tt(e),d=!l&&await Ku(r,"CLAUDE_CODE_OAUTH_TOKEN");if(l||d){let p=await Bu(r,l?.token);i.claude=p.ok&&/\bok\b/i.test(p.stdout),c.push(i.claude?S("claude",`token ${D} (${l?l.from:"kept in the sandbox"}) \u2014 "reply ok" in the sandbox`):P("claude",`the token did not answer: ${At(p.stdout||p.stderr)||"no answer"}`,"press c to make a new one with `claude setup-token`, or k for an API key"))}else Ee(e,"claude")?(i.claude=!0,c.push(S("claude",`ANTHROPIC_API_KEY ${D} \u2014 carried into the sandbox`))):c.push(H("claude","not connected \u2014 c for your Claude subscription, or k for an API key"));if(Ee(e,"codex"))i.codex=!0,c.push(S("codex",`OPENAI_API_KEY ${D} \u2014 carried into the sandbox`));else{let p=await r.exec("codex",["login","status"],{timeoutMs:6e4});i.codex=p.ok,c.push(p.ok?S("codex",`${le(p.stdout)||"logged in"} \u2014 its own session in the sandbox (c to log in again)`):H("codex","not connected \u2014 c to log in with a code (ChatGPT), or k for an API key"))}if(Ee(e,"cursor"))i.cursor=!0,c.push(S("cursor",`CURSOR_API_KEY ${D} \u2014 carried into the sandbox`));else{let p=await r.exec("cursor-agent",["status"],{timeoutMs:6e4});i.cursor=p.ok&&!/not logged in/i.test(`${p.stdout}${p.stderr}`),c.push(i.cursor?S("cursor","logged in \u2014 its own session in the sandbox (c to log in again)"):H("cursor","not connected \u2014 c to log in with your Cursor account, or k for an API key"))}let u=we.filter(p=>i[p]),f=t.answers.agentCli??u[0];return f?c.push(i[f]?S("proof run",`uses ${f} (\u2191 \u2193 to change) \xB7 ${u.length} of 3 connected`):P("proof run",`${f} is not connected in the sandbox`,"press c to connect it or k for its API key \u2014 or \u2191 \u2193 to another agent")):c.push(P("agent","no agent is connected in the sandbox","\u2191 \u2193 to an agent, then c to connect it or k for its API key")),c.push(H("note","owner only: your subscriptions; teammates use them, never see them (moa setup login)")),{lines:c,answers:{sandbox:s,...f?{agentCli:f}:{},loggedIn:u}}}function Zi(e,t,n="codex"){let o=!1,r=()=>{o=!0};return{done:(async()=>{let a=await ls(e);if(Yo(a))return{ok:!1,stdout:"",stderr:a.detail??"no account"};let c=e.state.answers.sandbox?.name??go(cs(e.state)),i=await is(e.sys).get(a,c,!0);if(!i)return{ok:!1,stdout:"",stderr:`${c} is not there`};if(o)return{ok:!1,stdout:"",stderr:"stopped"};let l="",d=n==="codex"?i.stream("codex",["login","--device-auth"],u=>{l+=u,t(dn(l))}):i.stream("env",["NO_OPEN_BROWSER=1","cursor-agent","login"],u=>{l+=u,t(Ko(l))});return r=d.cancel,d.done})(),cancel:()=>r()}}async function Ju(e){let{sys:t,state:n,secrets:o}=e,r=Ie(e),s=[],a=Qr(e);if(a)s.push(a);else if(r){let i=await Rn(t,r,cs(n)),l=`secret ${D} (${r.from}), graph ${r.graph}`;s.push(i.read.ok&&i.write.ok?S("LocusGraph",`${l} \u2014 one graph read, one event written`):P("LocusGraph",`${l} \u2014 ${i.read.ok?`the write failed: ${i.write.why}`:`the read failed: ${i.read.why}`}`,"e to paste the secret again, or g to check the graph ID \u2014 then r"))}else n.deployed?.runner==="vercel"&&n.deployed.memoryKept&&!o.noLocusgraph?s.push(H("LocusGraph","the secret the last deploy gave the sandbox, kept there (e to replace it)")):s.push(H("LocusGraph","not connected \u2014 memory stays in the workspace process (g for the graph ID, e for its secret)"));let c=n.answers.store??"postgres";return s.push(c==="postgres"?S("database","Postgres, in the sandbox on its kept disk \u2014 migrated at deploy (\u2191 \u2193 for SQLite)"):S("database","SQLite, on the sandbox's kept disk \u2014 migrated at deploy (\u2191 \u2193 for Postgres)")),{lines:s,answers:{store:c}}}function qu(e){let t=es(e),{githubApp:n,sandbox:o}=e.state.answers,r=[...t.lines];return o?kn(e.state)==="password"?r.unshift(S("console",`${o.consoleUrl} \u2014 you sign in there with your password`)):n&&n.consoleUrl!==o.consoleUrl?r.unshift(P("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(S("console",`${o.consoleUrl} \u2014 GitHub sends people back here`)):r.unshift(P("console","no sandbox yet","Esc back to Agent logins: the sandbox is made there")),{lines:r,answers:t.answers??{}}}var ec={...Jo,checks:Hu,github:Fu,logins:Wu,memory:Ju,access:qu};var yn="vercel";async function Qo(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 zu=e=>typeof e=="object"&&e!==null&&"mark"in e;async function Zo(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 bn=e=>typeof e=="object"&&e!==null&&"fix"in e&&"label"in e;async function er(e,t,n=()=>{}){let{sys:o,state:r}=e,s=[],a=F=>{let Be=s.findIndex(rt=>rt.label===F.label);Be===-1?s.push(F):s[Be]=F,t([...s])},c,i=(F,Be,rt)=>(a({label:F,mark:"fail",detail:Be,fix:rt}),c?{ok:!1,deployed:c}:{ok:!1}),l=await en(e);if("why"in l)return i("console sign-in",l.why,l.fix);if(l.mode==="none")return i("console sign-in","none \u2014 a public console needs somebody to sign in","Esc back to Console access: \u2191 \u2193 to password, or press g for a GitHub App");let d=l.mode==="github"?r.answers.githubApp:void 0;a({label:"sandbox",mark:"wait",detail:"looking for it"});let u=await Zo(e);if(bn(u))return i(u.label,u.detail,u.fix);let f=await ds(e,u,F=>a({label:"sandbox",mark:"wait",detail:F}));if(zu(f))return i("sandbox",f.detail??"",f.fix??"Enter to try again");let{box:p,info:m,built:y}=f,b=xt(u.plan);a({label:"sandbox",mark:"ok",detail:`${m.name} \u2014 ${p.vcpus??b.vcpus} vCPU, persistent${y?", image built":""}`});let g=m.workspaceUrl,w=m.consoleUrl;if(d&&d.consoleUrl!==w)return i("GitHub App",`${d.slug} signs people in at ${d.consoleUrl??"localhost"}, not ${w}`,"Esc back to Console access and press g");let{token:v,pending:C,before:N,had:W}=await Yt(o,yn,g);a({label:"owner token",mark:"ok",detail:`${D} \u2014 profile ${yn} in ~/.moa/config.json${N===yn||!W?"":`; \`moa\` now talks to the sandbox, not profile ${N} (\`moa use ${N}\` to go back)`}`});let ie=r.answers.tenant??Ne(r.answers.owner),R=r.answers.owner,Q=Ie(e),L=tt(e)?.token,K=await Xo(e),re={...Object.fromEntries(Object.entries(await zt(e)).map(([F,Be])=>[`workspace/${F}`,Be])),store:r.answers.store??"postgres","workspace/MOA_TOKENS":`${v}:${ie}`,"workspace/PORT":String(ft),"workspace/MOA_PORT":String(ft),"workspace/MOA_SELF_URL":`http://127.0.0.1:${ft}`,"workspace/MOA_MAX_CONCURRENT_RUNS":String(b.maxRuns),"workspace/MOA_REPO":r.answers.repo,"workspace/MOA_OWNER_LOGIN":R,"workspace/MOA_GITHUB_CLIENT_ID":l.vars.MOA_GITHUB_CLIENT_ID,"workspace/LOCUSGRAPH_AGENT_SECRET":Q?.secret,"workspace/LOCUSGRAPH_GRAPH_ID":Q?.graph,"workspace/LOCUSGRAPH_SERVER_URL":Q?.server,"workspace/CLAUDE_CODE_OAUTH_TOKEN":L,...Object.fromEntries(we.map(F=>[`workspace/${Te[F]}`,Ee(e,F)])),"workspace/GH_TOKEN":K&&"token"in K?K.token:void 0,"console/MOA_URL":`http://127.0.0.1:${ft}`,"console/MOA_TOKEN":v,"console/AUTH_GITHUB_ID":l.vars.AUTH_GITHUB_ID,"console/AUTH_GITHUB_SECRET":l.secrets.AUTH_GITHUB_SECRET,"console/AUTH_SECRET":l.secrets.AUTH_SECRET,"console/MOA_CONSOLE_LOGIN":l.vars.MOA_CONSOLE_LOGIN,"console/MOA_CONSOLE_PASSWORD_HASH":l.secrets.MOA_CONSOLE_PASSWORD_HASH,"console/AUTH_URL":w,"console/AUTH_TRUST_HOST":"true"};if(!K)return i("secrets","no GitHub token for the sandbox","Esc back to GitHub and press k to paste one");let ye=[...["workspace/MOA_GITHUB_CLIENT_ID","console/AUTH_GITHUB_ID","console/AUTH_GITHUB_SECRET","console/MOA_CONSOLE_LOGIN","console/MOA_CONSOLE_PASSWORD_HASH"].filter(F=>re[F]===void 0),"workspace/MOA_CLIENT_ADDRESS",...r.answers.repo?[]:["workspace/MOA_REPO"],...R?[]:["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 te=await wi(p,re,ye);if(!te.ok)return i("secrets",`not written: ${te.why??"no answer"}`,"Enter to try again");let $e=["workspace/MOA_TOKENS","workspace/LOCUSGRAPH_AGENT_SECRET","workspace/CLAUDE_CODE_OAUTH_TOKEN",...we.map(F=>`workspace/${Te[F]}`),"workspace/GH_TOKEN","console/MOA_TOKEN","console/AUTH_GITHUB_SECRET","console/AUTH_SECRET","console/MOA_CONSOLE_PASSWORD_HASH"].filter(F=>re[F]!==void 0);a({label:"secrets",mark:"ok",detail:`${$e.length} written \u2014 none shown; no Vercel token in the sandbox`});let $=te.changed||y;a({label:"workspace",mark:"wait",detail:$?`starting on :${ft}`:"checking it runs"});try{await Ur(p,$)}catch(F){return i("workspace",`the start did not run: ${F instanceof Error?F.message:String(F)}`,"Enter to try again")}let I=r.deployed?.workspaceUrl===g?r.deployed.people:void 0,se=F=>{c={runner:"vercel",workspaceUrl:g,consoleUrl:w,...I?{people:I}:{},...F?{memoryKept:F}:{},at:new Date().toISOString()},n(c)},T=r.deployed?.runner==="vercel"?r.deployed.memoryKept:void 0,z=Q?!0:e.secrets.noLocusgraph?!1:T;if(se(z),!await Qo(o,`${g}/health`,180))return i("workspace",`did not answer at ${g} \u2014 ${await Dn(p,"workspace")||"no log yet"}`,"fix that, then Enter");let ke=U({url:g,token:v},o.fetch);if(!await ke.get("/v1/whoami").then(()=>!0,()=>!1))return i("workspace","it started but refuses the owner token","Enter to try again");C&&await Xt(o,yn,g,v),a({label:"workspace",mark:"ok",detail:`${g} \u2014 :${ft} in the sandbox`});let Pe=await jr(o,g,a,r.answers.store??"postgres",`look at the sandbox's ~/moa/logs \u2014 ${await Dn(p,"postgres")}`);if(Pe)return i(Pe.label,Pe.detail,Pe.fix);let _e=await mn(ke,r,a,I);if(_e)return i(_e.label,_e.detail,_e.fix);let Ke=I;if(I=r.answers.people??[],JSON.stringify(I)!==JSON.stringify(Ke??[])&&se(z),a({label:"console",mark:"wait",detail:`waiting for :${un}`}),!await Qo(o,w,180))return i("console",`did not answer at ${w} \u2014 ${await Dn(p,"console")||"no log yet"}`,"fix that, then Enter");a({label:"console",mark:"ok",detail:`${w} \u2014 Sign in with GitHub`}),$&&await ki(p);let Me=r.answers.agentCli??"claude",ze=[...new Set([Me,...r.answers.loggedIn??[]])],ve=await Nt(ke,ze,"in the sandbox",()=>"Esc back to Agent logins and log it in again, then Enter",a);if("label"in ve)return i(ve.label,ve.detail,ve.fix);let Le=await gn(ke,a);if("label"in Le)return i(Le.label,Le.detail,Le.fix);Le.live!==void 0&&(z=Le.live),a({label:"Console",mark:"ok",detail:w}),a({label:"Workspace",mark:"ok",detail:g}),a({label:"Next",mark:"skip",detail:`moa setup status \xB7 moa setup stop \xB7 moa run ${Gn(Me)} "\u2026"`});let O=ve.runs[Me];return{ok:!0,deployed:{runner:"vercel",workspaceUrl:g,consoleUrl:w,...O?{proofRunId:O}:{},memory:Le.memory,people:I,...z?{memoryKept:z}:{},at:new Date().toISOString()}}}async function tr(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 Zo(e);if(bn(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 tc(e,t){let{sys:n,state:o}=e,r=await tr(e,!0);if(bn(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 Ur(s,!1)}catch(y){return t({label:"workspace",mark:"fail",detail:`the start did not run: ${At(y instanceof Error?y.message:String(y))}`,fix:"try again, or run `moa setup` to deploy it again"}),!1}let c={workspace:s.domain(ft),console:s.domain(un)};if(c.workspace!==a.workspaceUrl||c.console!==a.consoleUrl){let y=[...c.workspace!==a.workspaceUrl?[`the workspace is at ${c.workspace}, not ${a.workspaceUrl}`]:[],...c.console!==a.consoleUrl?[`the console is at ${c.console}, not ${a.consoleUrl}`]:[]];return t({label:"URLs",mark:"fail",detail:`the sandbox's domain has changed \u2014 ${y.join("; ")}`,fix:"run `moa setup` to deploy it again, and press g on Console access for the new callback"}),!1}if(!await Qo(n,`${a.workspaceUrl}/health`,180))return t({label:"workspace",mark:"fail",detail:`no answer at ${a.workspaceUrl} \u2014 ${await Dn(s,"workspace")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!1;let l=(await Oe(n.env)).profiles[yn];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=U(l,n.fetch),u=await n.fetch(`${a.workspaceUrl}/health`,{signal:AbortSignal.timeout(5e3)}).then(y=>y.json()).catch(()=>({})),f=await d.get("/v1/whoami").then(()=>{},y=>y instanceof ue?`${y.status}`:String(y));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(y=>y.jobs).catch(()=>{});if(p){let y=p.filter(b=>b.state==="running"||b.state==="queued");t({label:"pipelines",mark:"ok",detail:y.length===0?"nothing running":`${y.length} running or waiting \u2014 carried on from before the stop (moa jobs)`})}let m=await Qo(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 Dn(s,"console")||"no log"}`,fix:"run `moa setup` to deploy it again"}),!f&&m}async function nc(e,t){let n=await tr(e,!1);if(bn(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: ${At(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 q,jsxs as fe}from"react/jsx-runtime";var Ce=(e,t)=>({...e,answers:{...e.answers,...t}}),Zu=[{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"}],ep={where:{options:e=>Zu.map(t=>{let n=Ro(t.value,fo(e,t.value));return n?{...t,note:n}:t}),value:e=>e.target,set:(e,t)=>it(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=>On(e),set:(e,t)=>Ce(e,{source:t})},logins:{options:()=>[{value:"claude",label:"claude",note:"a Claude subscription, or an API key"},{value:"codex",label:"codex",note:"a ChatGPT plan, or an API key"},{value:"cursor",label:"cursor",note:"a Cursor plan, or an API key"}],value:e=>e.answers.agentCli,set:(e,t)=>Ce(e,{agentCli:t})},access:{options:e=>[...e.target==="local"?[{value:"none",label:"nobody",note:"the console stays on this machine, no sign-in"}]:[],{value:"password",label:"password",note:"just you: your GitHub login and a password"},{value:"github",label:"GitHub App",note:"a team: everyone signs in with GitHub"}],value:e=>kn(e)??"none",set:(e,t)=>Ce(e,{consoleSignIn:t==="none"?void 0: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"?Ce(e,{store:t}):Ce(e,{runner:t})}},ps=e=>e.target==="vercel",sc=/^[a-z\d](?:[a-z\d-]{0,38})$/i;function tp(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(y=>!o(f)(y)),m=[...new Set([...e.answers.removed??[],f.toLowerCase()])];return Ce(e,{people:p.length>0?p:void 0,removed:m})}if(!sc.test(s))return r;if(e.answers.owner&&s.toLowerCase()===e.answers.owner.toLowerCase())return`${s} is you \u2014 the owner already`;let c=n.find(o(s)),i=c?.role??"member",l=c?.dailyCapUsd??Er;for(let f of a){let p=f.toLowerCase(),m=Number(p.replace(/^\$|\/day$/g,""));if(p==="member"||p==="viewer")i=p;else if(Number.isFinite(m)&&m>=0&&m<=1e4)l=m;else return r}let d={login:s,role:i,dailyCapUsd:l},u=(e.answers.removed??[]).filter(f=>f!==s.toLowerCase());return Ce(e,{people:c?n.map(f=>o(s)(f)?d:f):[...n,d],removed:u.length>0?u:void 0})}var nr=e=>e.target==="cloudflare",np="\x1B",op=new RegExp(`(?:${np})?\\[20[01]~|[\\u0000-\\u001f\\u007f]`,"g"),oc=e=>e.replace(op,""),rp={checks:[{key:"l",label:"Licence key",secret:!0,shown:(e,t)=>t.licence?D:"saved key, or none",commit:(e,t,n)=>(t.licence=n||void 0,e)},{key:"d",label:"Domain",when:nr,shown:e=>e.answers.domain??"none",commit:(e,t,n)=>{let o=n.toLowerCase().replace(/^https?:\/\//,"").replace(/\/+$/,"");return o===""?Ce(e,{domain:void 0}):hr.test(o)?Ce(e,{domain:o}):"a domain on your Cloudflare account, like example.com"}},{key:"k",label:"D1 token",secret:!0,when:nr,shown:(e,t)=>t.cloudflare?D:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces":(t.cloudflare=n||void 0,e)},{key:"e",label:"Vercel team",when:ps,shown:e=>e.answers.vercelTeam??"the one Vercel picks",initial:e=>e.answers.vercelTeam??"",commit:(e,t,n)=>n===""||/^[\w-]{1,100}$/.test(n)?Ce(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)?Ce(e,{repo:n}):"owner/name, like fnLog0/fnmoa"},{key:"k",label:"GitHub token",secret:!0,when:e=>ps(e)||nr(e),shown:(e,t)=>t.github?D:ps(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?D:"none",commit:(e,t,n)=>/\s/.test(n)?"one token, no spaces \u2014 what `claude setup-token` printed":(t.claude=n||void 0,e)},{key:"k",label:"API key",secret:!0,shown:(e,t)=>{let n=e.answers.agentCli??"claude";return`${Te[n]} ${t.keys?.[n]?D:"none"}`},commit:(e,t,n)=>{if(/\s/.test(n))return"one key, no spaces";let o=e.answers.agentCli??"claude";return t.keys={...t.keys,[o]:n||void 0},e}}],memory:[{key:"g",label:"LocusGraph graph ID",shown:e=>e.answers.locusgraphGraph??"none",initial:e=>e.answers.locusgraphGraph??"",commit:(e,t,n)=>n===""||/^[\w.:-]{1,100}$/.test(n)?Ce(e,{locusgraphGraph:n||void 0}):"the graph's ID, as LocusGraph shows it \u2014 letters, digits, - _ . :"},{key:"e",label:"LocusGraph secret",secret:!0,shown:(e,t)=>t.locusgraph?D:"none",commit:(e,t,n)=>(t.locusgraph=n||void 0,t.noLocusgraph=!n,e)}],access:[{key:"p",label:"Console password",secret:!0,when:e=>kn(e)==="password",shown:(e,t)=>t.consolePassword?D:"the one kept from a deploy, or none",commit:(e,t,n)=>n.length<12?"12 characters or more \u2014 it guards a console on the internet":(t.consolePassword=n,Ce(e,{consoleSignIn:"password"}))},{key:"e",label:"Tenant",shown:e=>e.answers.tenant??"",commit:(e,t,n)=>/^[a-z0-9][a-z0-9-]{0,62}$/.test(n)?Ce(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===""||sc.test(n)?Ce(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)=>tp(e,n)}]},fs=(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},rc=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],sp=100,ap=e=>{let t=Math.floor(e/1e3);return`${Math.floor(t/60)}:${String(t%60).padStart(2,"0")}`},ip=(e,t=16)=>{let n=e.total>0?Math.min(1,e.done/e.total):0,o=Math.round(n*t);return`${"\u2593".repeat(o)}${"\u2591".repeat(t-o)} ${e.done}/${e.total} ${e.unit}`},or={ok:{char:"\u2713",color:"green"},fail:{char:"\u2717",color:"red"},wait:{char:"\u2026",color:"yellow"},skip:{char:"\u2013",color:"gray"}},ms={where:"Where",checks:"Checks",github:"GitHub",memory:"Memory",logins:"Logins",access:"Access",deploy:"Deploy"},cp=Object.fromEntries(ut.filter(e=>e!=="where"&&e!=="deploy").map((e,t)=>[String(t+1),e])),lp={local:"this machine",vercel:"the Vercel Sandbox",cloudflare:"Cloudflare"},dp={ok:"ok",fail:"failing",wait:"checking"};function up({screen:e,passed:t}){return q(Fe,{columnGap:2,flexWrap:"wrap",children:ut.map(n=>n===e?fe(Y,{bold:!0,color:"cyan",children:["\u25B8 ",ms[n]]},n):t.includes(n)?fe(Y,{color:"green",children:["\u2713 ",ms[n]]},n):fe(Y,{dimColor:!0,children:[" ",ms[n]]},n))})}function vo({title:e,aside:t,children:n}){return fe(Fe,{flexDirection:"column",marginTop:1,children:[fe(Fe,{justifyContent:"space-between",children:[q(Y,{bold:!0,dimColor:!0,children:e.toUpperCase()}),t?q(Y,{dimColor:!0,children:t}):null]}),n]})}var pp=(e,t=new Date)=>{let n=new Date(e),o=`${String(n.getHours()).padStart(2,"0")}:${String(n.getMinutes()).padStart(2,"0")}`;return n.toDateString()===t.toDateString()?o:`${n.getDate()} ${n.toLocaleString("en",{month:"short"})} ${o}`};function fp({last:e}){let t=e.lines.find(o=>o.mark==="fail"||o.mark==="wait"),n=Math.max(...e.lines.map(o=>o.label.length),8);return fe(vo,{title:"last run",aside:`${pp(e.at)} \xB7 ${e.ok?"deployed":t?`stopped at ${t.label}`:"did not finish"}`,children:[e.lines.map(o=>fe(Fe,{children:[q(Fe,{flexShrink:0,width:n+4,children:fe(Y,{dimColor:o.mark==="ok",children:[q(Y,{color:o.mark==="wait"?"yellow":or[o.mark].color,children:o.mark==="wait"?"\u25CB":or[o.mark].char})," ",o.label]})}),q(Y,{dimColor:o.mark==="ok",children:o.detail??""})]},o.label)),e.ok?null:q(Y,{color:"yellow",children:"Enter resumes: what is already done passes quickly, and the rest runs."})]})}function mp({rows:e}){let t=Math.max(...e.map(n=>n.label.length));return q(vo,{title:"review",children:e.map(n=>fe(Fe,{children:[q(Fe,{flexShrink:0,width:t+2,children:q(Y,{dimColor:!0,children:n.label})}),q(Y,{children:n.value})]},n.label))})}function gp({hints:e}){return q(Fe,{columnGap:2,flexWrap:"wrap",paddingX:1,children:e.map(t=>{let n=t.indexOf(" ");return fe(Y,{children:[q(Y,{bold:!0,children:t.slice(0,n)}),q(Y,{dimColor:!0,children:t.slice(n)})]},t)})})}var hp=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 ac({initial:e,sys:t,save:n,checks:o,deploy:r,createApp:s=Bi,onDone:a,secrets:c,onHandOver:i,pasteFirst:l=!1,deviceLogin:d=ai,after:u={}}){let{exit:f}=Vu(),[p,m]=lt(()=>e.screen==="where"&&!e.target?{...e,target:"local"}:e),[y,b]=lt([]),[g,w]=lt(!1),[v,C]=lt(0),N=l&&e.screen==="logins",[W,ie]=lt(N?"":void 0),[R,Q]=lt(N?"e":void 0),L=mt(void 0),K=mt([]),re=h=>fs(h,K.current),[ye,te]=lt(void 0),[$e,$]=lt(!1),[I,se]=lt("no"),T=mt(c??{}).current,z=mt(void 0),[ke,ne]=lt(0),Pe=mt(Promise.resolve()),_e=mt(0),[Ke,Me]=lt(!1),ze=mt(!1),ve=h=>{ze.current=h,Me(h)},Le=mt(e.screen!=="where").current,O=p.screen,F=p.target==="vercel",Be=o??(F?ec:Jo),rt=r??(F?er:p.target==="cloudflare"?zo:Fo),st=O==="checks"&&Ue({sys:t,state:p}),gt=F||p.answers.runner==="compose",de=mt(p);de.current=p;let vn=mt(p),Z=h=>{vn.current!==h&&(vn.current=h,Pe.current=Pe.current.then(()=>n(h)).catch(()=>{}))};us(()=>Z(p),[p,n]);let Ge=()=>(Z(de.current),Pe.current),{answers:he}=p,Ve=JSON.stringify([O,p.target,he,v,ke]);us(()=>{if(O==="deploy"){g||b(hp(p));return}let h=++_e.current;b(x=>x.map(oe=>K.current.some(ee=>ee.label===oe.label)?oe:{...oe,mark:"wait"})),w(!0),Promise.resolve(Be[O]?.({state:p,sys:t,secrets:T})??{lines:[]}).then(async x=>({...x,lines:[...x.lines,...!o&&O==="checks"?await vt({state:p,sys:t,secrets:T}):[]]})).then(x=>{if(h!==_e.current)return;let oe=re(x.lines);b(oe),L.current||w(!1);let ee=x.answers??{};Object.entries(ee).some(([ce,xe])=>p.answers[ce]!==xe)&&m(ce=>Ce(ce,ee));let V=ze.current;ve(!1),V&&bo(oe)&&Qn(O)},x=>{h===_e.current&&(b(re([{label:"check",mark:"fail",detail:String(x),fix:"press r to check again"}])),L.current||w(!1),ve(!1))})},[Ve]);let ht=h=>{L.current?.abort(),Ge().then(()=>{a?.(de.current,h),f()})},bs=()=>{w(!0),te(void 0);let h=ee=>b([{label:"licence",mark:"wait",detail:ee},{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 x=new AbortController,oe={done:Lo({state:p,sys:t,secrets:T},ee=>h(`open ${ee.url} and check it shows ${ee.code}`),x.signal).then(()=>({ok:!0,stdout:"",stderr:""}),ee=>({ok:!1,stdout:"",stderr:ee instanceof Error?ee.message:String(ee)})),cancel:()=>x.abort()};z.current=oe,oe.done.then(ee=>{z.current===oe&&(z.current=void 0,w(!1),ee.ok||te(`not connected: ${ee.stderr}`),C(G=>G+1))})},Pt=(h="codex")=>{let x=Ue({sys:t,state:p})?void 0:t.root,oe=!x&&p.deployed?.runner==="image";if(!x&&!F&&!oe)return;w(!0);let ee=F?"sandbox":"container",G=ce=>b([{label:h,mark:"wait",detail:Bn(ce)??`starting its login inside the ${ee}\u2026`},{label:"note",mark:"skip",detail:"its own session, not a copy of this machine's \u2014 Esc to stop"}]);G({});let V=F?Zi({state:p,sys:t,secrets:T},G,h):oe?Kn(t,h,G):d(t,x,G);z.current=V,V.done.then(ce=>{z.current===V&&(z.current=void 0,w(!1),ce.ok||te(`${h} login did not finish: ${(ce.stderr||ce.stdout).trim().split(`
|
|
213
|
+
`).slice(-1)[0]??""}`),C(xe=>xe+1))})},$s=h=>{if(te(void 0),h==="claude"){if(!i)return te("run `claude setup-token`, then e to paste the token");Ge().then(()=>{i(de.current),f()});return}let x=Ue({sys:t,state:p});if(F)return Pt(h);if(p.target==="cloudflare")return te(h==="codex"?"codex logs in inside the container at Deploy \u2014 a link and a code; or k for its API key":"on Cloudflare cursor runs on an API key: k");if(x)return p.deployed?.runner==="image"?Pt(h):te(`${h} logs in inside the container at Deploy when it is picked \u2014 or k for its API key`);if(gt)return h==="codex"?Pt("codex"):te("cursor runs in the published image or on Vercel \u2014 k for its API key");te(`run \`${h==="codex"?"codex login":"cursor-agent login"}\` in a terminal, then r`)},vs=h=>{if(te(void 0),T.keys={...T.keys,[h]:void 0},h==="claude"&&(T.claude=void 0),ne(x=>x+1),m(x=>Ce(x,{loggedIn:(x.answers.loggedIn??[]).filter(oe=>oe!==h)})),h!=="claude"&&p.deployed?.runner==="image"&&Ue({sys:t,state:p})){t.exec("docker",nt(t,"exec","-T","workspace",...ts[h]),{timeoutMs:6e4}).then(()=>C(x=>x+1));return}C(x=>x+1)},jt=h=>{se("no"),te(void 0),ve(!1),K.current=[],b([]),m(x=>({...x,screen:h}))},Qn=h=>{de.current.screen===h&&(m(x=>({...x,passed:[...new Set([...x.passed,h])]})),jt(u[h]??qa(h)))},Ss=()=>{w(!0),b([]);let h=V=>{let ce={...de.current,deployed:V};de.current=ce,Z(ce),m(xe=>({...xe,deployed:V}))},x=V=>{let ce={...de.current,cloudflare:V};de.current=ce,Z(ce),m(xe=>({...xe,cloudflare:V}))},oe=[],ee=V=>{oe=V,b(V)},G=V=>{let ce={target:de.current.target??"local",at:new Date().toISOString(),ok:V,lines:oe.map(({label:sn,mark:Pc,detail:xs})=>({label:sn,mark:Pc,...xs?{detail:xs}:{}}))},xe={...de.current,lastDeploy:ce};de.current=xe,Z(xe),m(sn=>({...sn,lastDeploy:ce}))};rt({state:p,sys:t,secrets:T},ee,h,x).then(V=>{G(V.ok);let ce=V.deployed;V.ok&&ce?($(!0),m(xe=>({...xe,answers:{...xe.answers,removed:void 0},deployed:ce,passed:[...new Set([...xe.passed,"deploy"])]}))):ce&&m(xe=>({...xe,deployed:ce}))}).catch(V=>{ee([...oe,{label:"deploy",mark:"fail",detail:String(V),fix:"Enter to try again"}]),G(!1)}).finally(()=>w(!1))},_t=h=>{K.current=fs(K.current,h),b(x=>fs(x,h))},Ht=()=>{let h=new AbortController;L.current=h,w(!0),te(void 0),_t([{label:"GitHub App",mark:"wait",detail:"starting a page for GitHub\u2026"}]),s({state:p,sys:t,secrets:T},_t,h.signal).then(x=>{L.current=void 0,w(!1),K.current=[],m(oe=>Ce(oe,x)),C(oe=>oe+1)},x=>{L.current=void 0,w(!1),!h.signal.aborted&&_t([{label:"GitHub App",mark:"fail",detail:x instanceof Error?x.message:String(x),fix:"press g to try again"}])})},Ot=(rp[O]??[]).filter(h=>!h.when||h.when(p)),cr=Ot.find(h=>h.key===R),dt=O==="memory"&&nr(p)||O==="checks"&&!t.root?void 0:ep[O],M=O==="access"?p.answers.githubApp:void 0;Xu(h=>{let x=oc(h);x&&ie(oe=>`${oe??""}${x}`)},{isActive:W!==void 0}),Yu((h,x)=>{if(W!==void 0&&cr){if(x.escape){ie(void 0),Q(void 0);return}if(x.return){let G=cr.commit(p,T,W.trim());if(typeof G=="string"){te(G);return}te(void 0),ie(void 0),Q(void 0),ne(V=>V+1),m(G);return}if(x.backspace||x.delete){ie(G=>(G??"").slice(0,-1));return}if(h&&!x.ctrl&&!x.meta){let G=oc(h);G&&ie(V=>`${V??""}${G}`)}return}if(z.current){(x.escape||h==="q")&&(z.current.cancel(),z.current=void 0,w(!1),C(G=>G+1));return}if(h==="q")return ht($e);if(x.escape){if(I==="asking")return se("no");O!=="where"&&!(g&&(O==="deploy"||L.current))&&jt(za(O));return}if(g){x.return&&O!=="deploy"&&ve(!0);return}if(K.current=[],h==="r")return C(G=>G+1);let oe=O==="deploy"&&!$e?cp[h]:void 0;if(oe)return jt(oe);if(O==="logins"&&h==="t"&&i){Ge().then(()=>{i(de.current),f()});return}if(O==="logins"&&h==="l"&>)return Pt();if(O==="logins"&&(h==="c"||h==="x")){let G=p.answers.agentCli??"claude";return h==="c"?$s(G):vs(G)}if(O==="checks"&&h==="c"&&st)return bs();let ee=Ot.find(G=>G.key===h);if(ee){te(void 0),Q(ee.key),ie(ee.secret?"":ee.initial?ee.initial(p):ee.shown(p,T)==="none"?"":ee.shown(p,T));return}if(O==="access"&&h==="g")return Ht();if(M&&h==="d"){m(G=>{let V=G.answers.githubApp;return V?Ce(G,{githubApp:{...V,deviceFlow:!V.deviceFlow}}):G});return}if((x.upArrow||x.downArrow)&&dt){let G=dt.options(p).map(sn=>sn.value),V=G.indexOf(dt.value(p)??G[0]??""),ce=x.upArrow?-1:1,xe=G[(V+ce+G.length)%G.length];xe!==void 0&&m(sn=>dt.set(sn,xe));return}if(x.return){if(O==="deploy")return $e?ht(!0):I==="no"?se("asking"):(se("yes"),Ss());if(!bo(y))return;Qn(O)}});let J=y.some(h=>h.mark==="wait"),[Se,me]=lt(0);us(()=>{if(!J)return;let h=setInterval(()=>me(x=>x+1),sp);return()=>clearInterval(h)},[J]);let Ft=mt(new Map);for(let h of y)h.mark!=="wait"?Ft.current.delete(h.label):Ft.current.has(h.label)||Ft.current.set(h.label,Date.now());let Zn=Math.max(...y.map(h=>h.label.length),8),eo=[O==="deploy"?$e?"Enter done":"Enter deploy":"Enter continue",O==="where"?void 0:"Esc back","q quit",dt?"\u2191 \u2193 choose":void 0,...Ot.length===1?["e edit"]:Ot.map(h=>`${h.key} ${h.label.toLowerCase()}`),O==="access"?M?"g new GitHub App":"g create GitHub App":void 0,M?M.deviceFlow?"d undo device flow":"d device flow is ticked":void 0,O==="logins"?"c connect \xB7 x disconnect":void 0,st?"c connect account":void 0,O==="deploy"?void 0:"r check again",O==="deploy"&&!$e?"1\u20135 change a step":void 0].filter(Boolean),Sn=Qu(()=>O==="deploy"?Wi({state:p,sys:t,secrets:T}):[],[O,p,t,T,ke]),Kt=["ok","fail","wait"].map(h=>[h,y.filter(x=>x.mark===h).length]).filter(([,h])=>h>0).map(([h,x])=>`${x} ${dp[h]}`),Cc=Math.max(...Ot.map(h=>h.label.length),0);return fe(Fe,{flexDirection:"column",children:[fe(Fe,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",paddingX:1,children:[q(Y,{bold:!0,color:"cyan",children:O==="where"?"moa setup":`${Va(O)} ${po[O]}`}),O==="where"?null:q(up,{screen:O,passed:p.passed}),O==="where"?q(Y,{children:po.where}):null,Le&&O===e.screen&&O!=="where"?q(Y,{color:"yellow",children:"resumed where the last setup stopped"}):null,dt?q(vo,{title:"choose",children:dt.options(p).map(h=>{let x=dt.value(p)===h.value;return fe(Y,{color:x?"cyan":void 0,children:[x?" \u25CF ":" \u25CB ",q(Y,{bold:x,children:h.label.padEnd(16)}),q(Y,{dimColor:!0,children:h.note})]},h.value)})}):null,Ot.length>0?q(vo,{title:"settings",children:Ot.map(h=>fe(Y,{children:[h.label.padEnd(Cc)," ",W!==void 0&&h.key===R?fe(Y,{color:"cyan",children:[h.secret?W?`${D} ${W.length} character${W.length===1?"":"s"}`:"":W,"\u258F"]}):q(Y,{dimColor:!h.shown(p,T),children:h.shown(p,T)||"\u2014"})]},h.key))}):null,ye?q(Fe,{marginTop:1,children:fe(Y,{color:"red",children:["\u2717 ",ye]})}):null,O==="deploy"?q(mp,{rows:Sn}):null,O==="deploy"&&I!=="yes"&&!$e&&!g&&p.lastDeploy&&p.lastDeploy.target===(p.target??"local")?q(fp,{last:p.lastDeploy}):null,q(vo,{title:"status",aside:O==="deploy"?void 0:Kt.join(" \xB7 "),children:y.map(h=>fe(Fe,{flexDirection:"column",children:[fe(Fe,{children:[q(Fe,{flexShrink:0,width:Zn+4,children:fe(Y,{children:[q(Y,{color:or[h.mark].color,children:h.mark==="wait"?rc[Se%rc.length]:or[h.mark].char})," ",h.label]})}),fe(Y,{children:[h.detail??"",h.mark==="wait"&&h.progress?fe(Y,{color:"cyan",children:[" ",ip(h.progress)]}):null,h.mark==="wait"&&Date.now()-(Ft.current.get(h.label)??Date.now())>=3e3?fe(Y,{dimColor:!0,children:[" ","\xB7 ",ap(Date.now()-(Ft.current.get(h.label)??Date.now()))]}):null]})]}),h.mark==="fail"&&h.fix?q(Fe,{paddingLeft:Zn+4,children:fe(Y,{color:"red",children:["\u2192 ",h.fix]})}):null]},h.label))}),I==="asking"?q(Fe,{marginTop:1,children:fe(Y,{color:"yellow",bold:!0,children:["Enter again to deploy to ",lp[p.target??"local"],p.answers.domain&&p.target==="cloudflare"?` (${p.answers.domain})`:""," ","\u2014 Esc to cancel"]})}):null,Ke&&g?q(Y,{color:"yellow",children:"still checking \u2014 Enter goes on once every line is \u2713"}):null]}),q(gp,{hints:W!==void 0?["Enter keep","Esc cancel"]:z.current?["Esc stop the login"]:I==="asking"?["Enter deploy now","Esc cancel"]:eo})]})}je();Ze();Qe();io();Qt();import{randomBytes as wp}from"node:crypto";import{rm as ic}from"node:fs/promises";import{join as rr}from"node:path";ct();Lt();Vt();Ln();Xe();ho();pn();var kp="codex/auth.json",ot=(e,t=2)=>e.replace(/\u001B\[[0-9;]*m/g,"").trim().split(`
|
|
214
|
+
`).filter(n=>n.trim()!=="").slice(-t).join(" \xB7 ").slice(0,300),zn=e=>e instanceof Error?e.message:String(e);function rn(e){let t=Pr(e);return t.length>0?t:e.target?[e.target]:[]}var Vn=(e,t)=>({...e,state:it(e.state,t??e.state.target)}),dc=(e,t)=>e.state?{...e,state:it(e.state,t)}:e,So=e=>e==="vercel"?yn:e==="cloudflare"?Wn:Nn;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 Yn=e=>(e({label:"fnMoa",mark:"fail",detail:"moa setup has not deployed it here yet",fix:"run `moa setup`"}),{ok:!1});async function cc(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 yp=15,bp=5,hs=(e,t,n,o={},r=3e4)=>e.fetch(t,{...o,headers:{authorization:`Bearer ${n}`},signal:AbortSignal.timeout(r)});async function sr(e,t,n){let o=(await Oe(e.env)).profiles[So(t)];return o?.url===n?o.token:void 0}async function uc(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,c=await sr(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(!c)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=U({url:t.workspaceUrl,token:c},o.fetch),d=await l.get("/v1/whoami").then(()=>{},u=>u instanceof ue?`${u.status}`:zn(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: ${ot(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 i=await o.fetch(t.consoleUrl,{redirect:"manual",signal:AbortSignal.timeout(t.seconds>0?13e4:5e3)}).then(l=>l.status<500).catch(()=>!1);return n(i?{label:"console",mark:"ok",detail:t.consoleUrl}:{label:"console",mark:"fail",detail:`no answer at ${t.consoleUrl}`,fix:t.fix}),a&&i}async function $p(e,t){let n=Ct(e.state);if(!n)return Yn(t);let o=jn(e.sys,n),r=o.workspacePid,s=o.runner==="compose"?` \u2014 docker compose, project ${be}`:r!==void 0&&Ut(e.sys,r,o.workspaceStarted,Et(o.workspaceUrl))?` (pid ${r})`:"";return{ok:await uc(e,{target:"local",workspaceUrl:o.workspaceUrl,consoleUrl:o.consoleUrl,where:s,fix:"run `moa setup` to start it again",seconds:0},t)}}async function vp(e,t){let n=Ct(e.state);if(!n)return Yn(t);let{sys:o}=e,r=await sr(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 hs(o,`${n.workspaceUrl}/_moa/box`,r).then(async c=>c.ok?await c.json():`${c.status} ${ot(await c.text().catch(()=>""),1)}`.trim()).catch(c=>zn(c));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 ${It}h after the last request`:"was stopped \u2014 this check starts it again, on its data in D1"}),{ok:await uc(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 ${Ye}\`, or run \`moa setup\` to deploy it again`,seconds:600},t)})}var qn={local:"This machine",vercel:"Vercel Sandbox",cloudflare:"Cloudflare"};function Sp(e,t){let n=Pr(e);for(let o of n){let r=fo(e,o);t({label:o,mark:"skip",detail:`${qn[o]} \u2014 ${Ro(o,r)}${Ja(o,r)?"; billed while it stands":""}`})}return n}async function xp(e,t){let n=e.state.target==="vercel"?{ok:await tc(e,t)}:e.state.target==="cloudflare"?await vp(e,t):await $p(e,t);return Ct(e.state)&&Ap(e.state,t),n}function Ap(e,t){let n=e.answers.loggedIn??[];for(let o of we)t(n.includes(o)?{label:o,mark:"ok",detail:`connected${e.answers.agentCli===o?" \u2014 the proof runs on it":""}`}:{label:o,mark:"skip",detail:`not connected \u2014 \`moa setup login ${o}\``})}async function pc(e,t,n){let o=Sp(e.state,t),r=n==="all"?rn(e.state):n?[n]:o.length===1&&o[0]?[o[0]]:[],s=r.length>0?r:[e.state.target??"local"],a=!0;for(let c of s){s.length>1&&t({label:c,mark:"skip",detail:`\u2500\u2500 ${qn[c]}`});let i=await xp(Vn(e,c),t);a=a&&i.ok}return{ok:a}}async function gs(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"?Dt(s,"down","--volumes","--remove-orphans","--rmi","local"):Dt(s,"stop"),{cwd:s,env:ln(r),timeoutMs:3e5});return a.ok?(t({label:"workspace",mark:"ok",detail:o?`compose project ${be} taken down, with any volumes an earlier compose run left \u2014 codex's login among them`:n==="down"?`compose project ${be} removed, with its volumes \u2014 Postgres and codex's login`:`stopped \u2014 Postgres kept in the ${be} volumes`}),!0):(t({label:"workspace",mark:"fail",detail:`docker compose: ${ot(a.stderr||a.stdout)}`,fix:"start Docker, then run this again"}),!1)}async function ar(e,t,n,o){let{sys:r}=e;if(t.runner==="image"){let i=await r.exec("docker",nt(r,...o==="down"?["down","--volumes"]:["stop"]),{timeoutMs:12e4});return n({label:"image",mark:i.ok?"ok":"fail",detail:o==="down"?"Container and data volumes removed":"Container stopped; data kept"}),{ok:i.ok,deployed:t}}let s=!0;t=jn(r,t);let a={...t};if(t.runner==="compose"&&!await gs(e,n,o)&&(s=!1),t.runner==="compose"&&o==="down"){let{people:i,...l}=a;a=l}let c=[["workspace",t.workspacePid,t.workspaceStarted,Et(t.workspaceUrl)],["console",t.consolePid,t.consoleStarted,Et(t.consoleUrl)]];for(let[i,l,d,u]of c){if(l===void 0){if(i==="workspace"&&t.runner==="compose")continue;n({label:i,mark:"ok",detail:"not running"});continue}let f=()=>{let{[i==="workspace"?"workspacePid":"consolePid"]:y,[i==="workspace"?"workspaceStarted":"consoleStarted"]:b,...g}=a;a=g};if(r.alive(l)&&!Ut(r,l,d,u)){n({label:i,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 cc(r,l,yp)){n({label:i,mark:"ok",detail:`stopped (pid ${l})`}),f();continue}let m=r.processOf(l);if(p===void 0||m?.started!==p){let y=p!==void 0&&(m!==void 0||!r.alive(l));y?f():s=!1,n(y?{label:i,mark:"ok",detail:`stopped (pid ${l})`}:{label:i,mark:"fail",detail:`pid ${l} is still running`,fix:`stop it (\`kill -9 ${l}\`), then run this again`});continue}r.stop(l,!0),await cc(r,l,bp)?(n({label:i,mark:"ok",detail:`stopped (pid ${l}) \u2014 killed, it did not stop when asked to`}),f()):(s=!1,n({label:i,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 Ep(e,t){let n=Ct(e.state);if(!n)return Yn(t);let o=await ar(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 Cp(e,t){let n=Ct(e.state);if(!n)return Yn(t);let{sys:o}=e,r=await sr(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 hs(o,`${n.workspaceUrl}/_moa/restart`,r,{method:"POST"}).then(async a=>a.ok?await a.json():`${a.status} ${ot(await a.text().catch(()=>""),1)}`.trim()).catch(a=>zn(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 fc(e,t,n){let o=Vn(e,n),r=o.state.target==="vercel"?{ok:await nc(o,t)}:o.state.target==="cloudflare"?await Cp(o,t):await Ep(o,t);return dc(r,e.state.target)}var Pp=(e,t)=>e.replace(/\n$/,"").split(`
|
|
215
215
|
`).slice(-t).join(`
|
|
216
|
-
`);async function
|
|
217
|
-
${
|
|
218
|
-
${m.stderr}`,p=(m,w,v)=>(t({label:m,mark:"fail",detail:tt(f(w))||"no answer",fix:v}),{ok:!1,removed:s,left:a}),g;if(i.imagesUnlisted){let m=await oo(o,l,d);if(m.ok){let{imagesUnlisted:w,images:v=[],..._}=i,H=[...new Set([...i.unsureImages??[],...m.images.filter(W=>!v.includes(W))])];i={..._,...v.length>0?{images:v}:{},...H.length>0?{unsureImages:H}:{}}}else g=m.why}if(n){let m=await _p(i,n);i=m.made,a.push(...m.declined)}let y=a.length>0||i.d1?.unsure===!0||i.r2?.unsure===!0||(i.unsureImages??[]).length>0||g!==void 0;if(i.worker){let m=await u(["delete",i.worker,"--force"]);if(!m.ok&&!bn.test(f(m)))return p("Worker",m,"check `wrangler whoami`, then run this again");m.ok&&s.push(`Worker ${i.worker}`),t({label:"Worker",mark:"ok",detail:m.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 m=await u(["containers","delete",i.containerId]);if(!m.ok&&!bn.test(f(m)))return p("container",m,"run this again \u2014 or delete it in the dashboard: Workers \u2192 Containers");m.ok&&s.push(`container ${i.containerId}`),t({label:"container",mark:"ok",detail:m.ok?`${i.containerId} deleted`:`${i.containerId} was already gone`})}else t({label:"container",mark:"ok",detail:"setup recorded none"});let b=0;for(let m of i.images??[]){let w=await u(["containers","images","delete",m,"-y"]);if(!w.ok&&!bn.test(f(w)))return p("images",w,"run this again");w.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 m=`${(i.unsureImages??[]).length} image tag(s) of ${Fe} \u2014 setup cannot tell whether it pushed them; left as they were`;n||a.push(m),t({label:"images",mark:"skip",detail:m})}if(g!==void 0){let m=`any image tags of ${Fe} an older moa pushed \u2014 Cloudflare's registry could not be listed; left as they were`;a.push(m),t({label:"images",mark:"skip",detail:`${m}: ${g} \u2014 check the dashboard: Workers \u2192 Containers \u2192 Images`})}if(i.d1?.made){let m=i.d1.id,w=await u(["d1","list","--json"]),v=sn(w.stdout);if(!w.ok||!Array.isArray(v))return p("D1",w,"run this again");let _=v.find(H=>H.uuid===m);if(!_?.name)t({label:"D1",mark:"ok",detail:`${m} was already gone`});else{let H=await u(["d1","delete",_.name,"-y"]);if(!H.ok&&!bn.test(f(H)))return p("D1",H,"run this again");H.ok&&s.push(`D1 ${_.name} (${m})`),t({label:"D1",mark:"ok",detail:H.ok?`${_.name} (${m}) deleted`:`${m} 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}/${pp}`,"--remote"]),i.r2?.made){let m=await u(["r2","bucket","delete",i.r2.name]);if(!m.ok&&!bn.test(f(m)))return p("R2",m,`empty ${i.r2.name} in the dashboard (R2 \u2192 ${i.r2.name}), then run this again`);m.ok&&s.push(`R2 ${i.r2.name}`),t({label:"R2",mark:"ok",detail:m.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:y}}async function gc(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=D=>ic(D,a),{sys:c,state:l}=Vn(e,s),d=on(l).filter(D=>D!==s),u=d.length===0,f=[],p=[],g=!1;if(s==="vercel"||s==="cloudflare"){let D=s==="vercel"?{...await Cp({...e,state:l},t),left:[],unsure:!1}:await Op({...e,state:l},t,o);if(f.push(...D.removed),p.push(...D.left),g=D.unsure===!0,!D.ok)return{ok:!1}}else{let D=Ct(l),B=!1,oe={ok:!1};if(D){let he=await or({...e,state:l},D,t,"down");if(oe=i({ok:!1,state:{...l,deployed:he.deployed}}),!he.ok)return oe;B=D.runner==="compose",f.push(B?`compose project ${we} and its volumes`:"the workspace and console setup started")}else if(l.answers.runner==="compose"){if(!await fs({...e,state:l},t,"down"))return{ok:!1};B=!0,f.push(`compose project ${we} and its volumes`)}if(!B){let he=c.root?await c.exec("docker",["info","--format","{{.ServerVersion}}"]):void 0;if(he?.ok){if(!await fs({...e,state:l},t,"down",!0))return oe;f.push(`compose project ${we}, if an earlier run left one`)}else he&&!he.missing&&(p.push(`any ${we} volumes an earlier compose run left \u2014 Docker was not running`),t({label:"compose",mark:"skip",detail:`Docker is not running \u2014 any ${we} volumes an earlier compose run left are still there: start it, then \`docker compose -p ${we} down --volumes\``}))}}let y=await Ae(c.env),b=bo(s);if(y.profiles[b]){let{[b]:D,...B}=y.profiles,oe=y.current===b?Object.keys(B)[0]??"default":y.current;await kt({current:oe,profiles:B},c.env),f.push(`profile ${b}`),t({label:"owner token",mark:"ok",detail:`profile ${b} removed from ~/.moa/config.json${y.current===b&&B[oe]?`; \`moa\` now uses profile ${oe}`:""}`})}if(u)await rc(tr(pt(c.env),"..","local"),{recursive:!0,force:!0}),f.push("~/.moa/local"),t({label:"this machine",mark:"ok",detail:"~/.moa/local removed"});else{for(let D of Ep(s))await rc(ce(c,D),{recursive:!0,force:!0});f.push("this target's files in ~/.moa/local"),t({label:"this machine",mark:"ok",detail:`${qn[s]}'s files in ~/.moa/local removed; the rest kept \u2014 ${d.map(D=>qn[D]).join(" and ")} still ${d.length===1?"uses":"use"} it`})}let m=l.answers.githubApp;m&&(u||await rs(c,m),p.push(`GitHub App ${m.slug} \u2014 ${m.settingsUrl}/advanced`),t({label:"GitHub App",mark:"skip",detail:`${m.slug} is yours to delete: ${m.settingsUrl}/advanced \u2192 Delete GitHub App (it costs nothing)${u?"":"; its client secret is gone from this machine"}`}));let w=d.map(D=>`${qn[D]} \u2014 \`moa setup down --target ${D}\``).join("; ");t(g?{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: ${w}`}`}:u?{label:"billing",mark:"ok",detail:"nothing setup made is left running or stored"}:{label:"billing",mark:"ok",detail:`nothing ${qn[s]} had is left running or stored; still deployed: ${w}`});let v={target:s,at:n.toISOString(),removed:f,...p.length>0?{left:p}:{}};if(u)return{ok:!0,state:{...Po(),down:v}};let{deployed:_,cloudflare:H,...W}=l,{sandbox:le,githubApp:L,...X}=l.answers;return i({ok:!0,state:{...W,answers:X,...a===s?{screen:"where",passed:W.passed.filter(D=>D!=="deploy")}:{},down:v}})}async function Tp(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: ${tt(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 Ip(e,t,n,o){let r=tr(t,"apps","console",".next");if(e.exists(r)&&!e.exists(tr(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(" ")}: ${tt(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 rr(e,t,n){e=Vn(e,n);let{sys:o,state:r}=e,s=Ct(r);if(!s)return Yn(t);if(s.runner==="image"||!o.root){let c=await vt(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 Tp(o,a,t))return{ok:!1};let i=s;if(r.target==="local"){let c=await or(e,s,t,"stop");if(i=c.deployed,!c.ok)return{ok:!1,state:{...r,deployed:i}};if(!await Ip(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 ${we} 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 hc(e,t,n,o){e=Vn(e,o);let{sys:r,state:s}=e,a=Ct(s),i=s.target;if(!a||!i)return Yn(t);let c={},l=s.answers.githubApp,d=l?nn(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 m=await r.fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${f}`,Accept:"application/vnd.github+json"},signal:AbortSignal.timeout(15e3)}).then(async w=>w.ok?(await w.json()).login:void 0).catch(()=>{});if(!m)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:`${U} \u2014 ${m}'s, new`})}let p=a.memoryKept===!0||Ce(e)!==void 0,g=i==="vercel"||Ce(e)!==void 0;if(p){let m=(await n(g?"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(!m&&!g)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(m){let w=Ce({...e,secrets:{locusgraph:m}});if(!w)return t({label:"LocusGraph",mark:"fail",detail:"setup has no graph ID for this secret",fix:"run `moa setup`, type the graph ID on Memory (g), then `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};let v=s.answers.tenant??Ie(s.answers.owner),_=await Rn(r,w,v);if(!_.read.ok||!_.write.ok)return t({label:"LocusGraph",mark:"fail",detail:`the new secret does not work: ${_.read.why??_.write.why??"no answer"}`,fix:"check it, then run `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};c.locusgraph=m,t({label:"LocusGraph",mark:"ok",detail:`${U} \u2014 new; it reads and writes ${w.graph}`})}}let y=bo(i);await si(r,y,a.workspaceUrl,Ir()),t({label:"owner token",mark:"ok",detail:`${U} \u2014 new; Deploy gives it to the workspace, and profile ${y} takes it once the workspace answers to it`}),l&&d?(await os(r,l,{clientSecret:u||d.clientSecret,authSecret:up(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 m=await or(e,a,t,"stop");if(b=m.deployed,!m.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}}}Dt();qe();mo();ze();Xt();function Rp(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(gn(e))return"the LocusGraph secret (set LOCUSGRAPH_AGENT_SECRET)";if(t.answers.githubApp&&!nn(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 wc(e,t,n){let o=Rp(e);if(o)return t({label:"update",mark:"fail",detail:`Update needs ${o}`}),!1;let r=await rr(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"?Xo:s.target==="cloudflare"?Wo:Go;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 Mp=["status","login","logout","licence","rotate","update","logs","stop","down"],Gp={ok:"\u2713",fail:"\u2717",wait:"\u2026",skip:"\u2013"},jt=e=>{process.stdout.write(`${Gp[e.mark]} ${e.label.padEnd(12)} ${e.detail??""}
|
|
216
|
+
`);async function _p(e,t){let{sys:n}=e;if(e.state.deployed?.runner==="image"){let c=await n.exec("docker",nt(n,"logs","--no-color","--tail",String(t)),{timeoutMs:3e4});return c.ok?{ok:!0,sections:[{name:"image",from:"docker compose",text:c.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=c=>{let i=pe(n,`${c}.log`),l=n.read(i);return{name:c,from:i,text:l===void 0?"":Pp(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",Dt(s,"logs","--no-color","--tail",String(t),"workspace"),{cwd:s,env:ln(n),timeoutMs:6e4});return a.ok?{ok:!0,sections:[{name:"workspace",from:`docker compose -p ${be} logs workspace`,text:a.stdout},r("console")]}:{ok:!1,line:{label:"workspace",mark:"fail",detail:`docker compose: ${ot(a.stderr||a.stdout)}`,fix:"start Docker, then run this again"}}}async function Op(e,t){let n=await tr(e,!1);if(bn(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 He(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: ${ot(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 Tp(e,t){let{sys:n}=e,o=Ct(e.state),r=(i,l,d)=>({ok:!1,line:{label:i,mark:"fail",detail:l,fix:d}});if(!o)return r("fnMoa","not deployed here yet","run `moa setup`");let s=await sr(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 hs(n,`${o.workspaceUrl}/_moa/logs?lines=${t}`,s).catch(i=>zn(i));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} ${ot(await a.text())}`.trim(),"try again");let c=await a.json();return c.running?{ok:!0,sections:[{name:"workspace",from:"the container's /home/moa/logs",text:c.workspace??""},{name:"console",from:"the container's /home/moa/logs",text:c.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 ${Ye} \u2192 Logs`)}function mc(e,t,n){let o=Vn(e,n);return o.state.target==="vercel"?Op(o,t):o.state.target==="cloudflare"?Tp(o,t):_p(o,t)}var Ip=e=>[...e==="local"?["workspace.db","workspace.db-wal","workspace.db-shm","worktrees","workspace.log","console.log"]:[],...e==="cloudflare"?["cloudflare"]:[],rr("rotate",`${So(e)}.json`)],gc="~/.moa/local \u2014 the GitHub Apps' secrets, logs, and what setup kept there";function hc(e,t){if(t==="all")return[...rn(e).map(s=>lc(it(e,s),s,!1)).flat(),gc];let n=t??e.target??"local",o=rn(e).filter(r=>r!==n);return lc(it(e,n),n,o.length===0)}function lc(e,t,n){let o=[n?gc:`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 ${So(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=wc(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 ${an(s).workspace} and ${an(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 ${be} and its volumes \u2014 the database`:"the workspace and console setup started; the database is in ~/.moa/local",...o]}async function Rp(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 Zo(e);if(bn(a))return t({label:a.label,mark:"fail",detail:a.detail,fix:a.fix}),{ok:!1,removed:s};let c=n.vercel;if(!c)return t({label:"sandbox",mark:"fail",detail:"this build of moa cannot reach Vercel",fix:"update moa"}),{ok:!1,removed:s};try{let i=await c.get(a,r.name,!1);i?(await i.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(i){return t({label:"sandbox",mark:"fail",detail:`${r.name} was not deleted: ${ot(zn(i))}`,fix:"check `vercel whoami`, then run this again"}),{ok:!1,removed:s}}if(r.snapshotId)try{await c.deleteSnapshot(a,r.snapshotId),s.push(`snapshot ${r.snapshotId}`),t({label:"snapshot",mark:"ok",detail:`${r.snapshotId} deleted`})}catch(i){return t({label:"snapshot",mark:"fail",detail:`${r.snapshotId} was not deleted: ${ot(zn(i))}`,fix:"run this again"}),{ok:!1,removed:s}}return{ok:!0,removed:s}}var $n=/not found|does not exist|couldn't find|could not find|no such|10007\b/i,wc=e=>e.cloudflare??(e.deployed?.runner==="cloudflare"?e.deployed.cloudflare:void 0);async function Lp(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 c=R=>Je(o,a,R,{account:s,timeoutMs:6e4}),i=R=>`${R.stdout}
|
|
217
|
+
${R.stderr}`,l=[],d=[],u=(R,Q)=>{d.push(`${R}: ${ot(i(Q))||"wrangler did not answer"}`)},f=await c(["deployments","list","--name",Ye,"--json"]);f.ok?l.push({what:`Worker ${Ye}, and its two names`,more:{worker:Ye}}):$n.test(i(f))||u("wrangler deployments list",f);let p=await wr(o,a,s);p.ok||d.push(`wrangler containers list: ${p.why}`);let m=p.ok?kr(p.apps):void 0;m&&l.push({what:`container ${m.id} (${We})`,more:{containerId:m.id}});let y=await so(o,a,s);y.ok||d.push(`wrangler containers images list: ${y.why}`);let b=[...new Set([...y.ok?y.images:[],...m?.image?[m.image]:[]])];for(let R=0;R<b.length;R+=cn){let Q=b.slice(R,R+cn),L=Q.map(re=>re.slice(We.length+1)),K=b.length>cn?` \u2014 ${R+1} to ${R+Q.length} of ${b.length}`:"";l.push({what:`${Q.length} image tag${Q.length===1?"":"s"} of ${We} in Cloudflare's registry${K} (${L.join(", ")})`,more:{images:Q}})}let g=await c(["d1","list","--json"]),w=Jt(g.stdout);(!g.ok||!Array.isArray(w))&&u("wrangler d1 list",g);let v=(Array.isArray(w)?w:[]).filter(R=>R.name===kt),C=v.length===1?v[0]?.uuid:void 0;C&&l.push({what:`D1 database ${kt} (${C}) \u2014 the workspace's data`,more:{d1:{id:C,made:!0}}});let N=await c(["r2","bucket","info",yt,"--json"]);if(N.ok?l.push({what:`R2 bucket ${yt} \u2014 codex's login`,more:{r2:{name:yt,made:!0}}}):$n.test(i(N))||u("wrangler r2 bucket info",N),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 W={account:s},ie=d.map(R=>`not looked for \u2014 ${R}; 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 R of l)await n(`Delete ${R.what}? Only if setup made it [y/N] `)?W={...W,...R.more,...R.more.images?{images:[...W.images??[],...R.more.images]}:{}}:ie.push(`${R.what}: you said no \u2014 left as it was`);return{made:W,declined:ie}}async function Dp(e,t){let n=[],o=e,r=i=>t(`Delete ${i}? 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 i=`D1 database ${kt} (${e.d1.id}) \u2014 the workspace's data`;await r(i)?o={...o,d1:{id:e.d1.id,made:!0}}:n.push(`${i}: you said no \u2014 left as it was`)}if(e.r2?.unsure&&!e.r2.made){let i=`R2 bucket ${e.r2.name} \u2014 codex's login`;await r(i)?o={...o,r2:{name:e.r2.name,made:!0}}:n.push(`${i}: you said no \u2014 left as it was`)}let s=e.unsureImages??[],a=[],c=[];for(let i=0;i<s.length;i+=cn){let l=s.slice(i,i+cn),d=l.map(p=>p.slice(We.length+1)),u=s.length>cn?` \u2014 ${i+1} to ${i+l.length} of ${s.length}`:"",f=`${l.length} image tag${l.length===1?"":"s"} of ${We} in Cloudflare's registry${u} (${d.join(", ")})`;await r(f)?a=[...a,...l]:(c=[...c,...l],n.push(`${f}: you said no \u2014 left as they were`))}if(a.length>0&&(o={...o,images:[...new Set([...o.images??[],...a])],...c.length>0?{unsureImages:c}:{}},c.length===0)){let{unsureImages:i,...l}=o;o=l}return{made:o,declined:n}}async function Up(e,t,n){let{sys:o,state:r}=e,s=[],a=[],c=wc(r),i;if(!c&&n){let g=await Lp(e,t,n);c=g.made,i=g.why,a.push(...g.declined)}if(!c)return t({label:"Cloudflare",mark:"skip",detail:`setup recorded no deploy there${i?`, and ${i}`:""} \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=c.account,u=g=>Je(o,l,g,{account:d,timeoutMs:18e4}),f=g=>`${g.stdout}
|
|
218
|
+
${g.stderr}`,p=(g,w,v)=>(t({label:g,mark:"fail",detail:ot(f(w))||"no answer",fix:v}),{ok:!1,removed:s,left:a}),m;if(c.imagesUnlisted){let g=await so(o,l,d);if(g.ok){let{imagesUnlisted:w,images:v=[],...C}=c,N=[...new Set([...c.unsureImages??[],...g.images.filter(W=>!v.includes(W))])];c={...C,...v.length>0?{images:v}:{},...N.length>0?{unsureImages:N}:{}}}else m=g.why}if(n){let g=await Dp(c,n);c=g.made,a.push(...g.declined)}let y=a.length>0||c.d1?.unsure===!0||c.r2?.unsure===!0||(c.unsureImages??[]).length>0||m!==void 0;if(c.worker){let g=await u(["delete",c.worker,"--force"]);if(!g.ok&&!$n.test(f(g)))return p("Worker",g,"check `wrangler whoami`, then run this again");g.ok&&s.push(`Worker ${c.worker}`),t({label:"Worker",mark:"ok",detail:g.ok?`${c.worker} deleted, and its two names with it`:`${c.worker} was already gone`})}else t({label:"Worker",mark:"ok",detail:"setup recorded none"});if(c.containerId){let g=await u(["containers","delete",c.containerId]);if(!g.ok&&!$n.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 ${c.containerId}`),t({label:"container",mark:"ok",detail:g.ok?`${c.containerId} deleted`:`${c.containerId} was already gone`})}else t({label:"container",mark:"ok",detail:"setup recorded none"});let b=0;for(let g of c.images??[]){let w=await u(["containers","images","delete",g,"-y"]);if(!w.ok&&!$n.test(f(w)))return p("images",w,"run this again");w.ok&&(b+=1)}if(b>0&&s.push(`${b} container image${b===1?"":"s"}`),t({label:"images",mark:"ok",detail:(c.images??[]).length===0?"setup recorded none":b>0?`${b} setup pushed deleted from Cloudflare's registry`:"the ones setup pushed were already gone"}),(c.unsureImages??[]).length>0){let g=`${(c.unsureImages??[]).length} image tag(s) of ${We} \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 ${We} 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(c.d1?.made){let g=c.d1.id,w=await u(["d1","list","--json"]),v=Jt(w.stdout);if(!w.ok||!Array.isArray(v))return p("D1",w,"run this again");let C=v.find(N=>N.uuid===g);if(!C?.name)t({label:"D1",mark:"ok",detail:`${g} was already gone`});else{let N=await u(["d1","delete",C.name,"-y"]);if(!N.ok&&!$n.test(f(N)))return p("D1",N,"run this again");N.ok&&s.push(`D1 ${C.name} (${g})`),t({label:"D1",mark:"ok",detail:N.ok?`${C.name} (${g}) deleted`:`${g} was already gone`})}}else c.d1?.unsure?(n||a.push(`D1 ${c.d1.id} \u2014 setup cannot tell whether it made it; left as it was`),t({label:"D1",mark:"skip",detail:`${c.d1.id} \u2014 setup cannot tell whether it made it; left as it was`})):c.d1&&(a.push(`D1 ${c.d1.id} \u2014 it was there before setup; left as it was`),t({label:"D1",mark:"skip",detail:`${c.d1.id} was there before setup used it \u2014 left as it was`}));if(c.r2&&await u(["r2","object","delete",`${c.r2.name}/${kp}`,"--remote"]),c.r2?.made){let g=await u(["r2","bucket","delete",c.r2.name]);if(!g.ok&&!$n.test(f(g)))return p("R2",g,`empty ${c.r2.name} in the dashboard (R2 \u2192 ${c.r2.name}), then run this again`);g.ok&&s.push(`R2 ${c.r2.name}`),t({label:"R2",mark:"ok",detail:g.ok?`${c.r2.name} deleted`:`${c.r2.name} was already gone`})}else c.r2?.unsure?(n||a.push(`R2 ${c.r2.name} \u2014 setup cannot tell whether it made it; left as it was`),t({label:"R2",mark:"skip",detail:`${c.r2.name} \u2014 setup cannot tell whether it made it; codex's login taken out, the bucket left`})):c.r2&&(a.push(`R2 ${c.r2.name} \u2014 it was there before setup; left as it was`),t({label:"R2",mark:"skip",detail:`${c.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:y}}async function kc(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,c=L=>dc(L,a),{sys:i,state:l}=Vn(e,s),d=rn(l).filter(L=>L!==s),u=d.length===0,f=[],p=[],m=!1;if(s==="vercel"||s==="cloudflare"){let L=s==="vercel"?{...await Rp({...e,state:l},t),left:[],unsure:!1}:await Up({...e,state:l},t,o);if(f.push(...L.removed),p.push(...L.left),m=L.unsure===!0,!L.ok)return{ok:!1}}else{let L=Ct(l),K=!1,re={ok:!1};if(L){let ye=await ar({...e,state:l},L,t,"down");if(re=c({ok:!1,state:{...l,deployed:ye.deployed}}),!ye.ok)return re;K=L.runner==="compose",f.push(K?`compose project ${be} and its volumes`:"the workspace and console setup started")}else if(l.answers.runner==="compose"){if(!await gs({...e,state:l},t,"down"))return{ok:!1};K=!0,f.push(`compose project ${be} and its volumes`)}if(!K){let ye=i.root?await i.exec("docker",["info","--format","{{.ServerVersion}}"]):void 0;if(ye?.ok){if(!await gs({...e,state:l},t,"down",!0))return re;f.push(`compose project ${be}, if an earlier run left one`)}else ye&&!ye.missing&&(p.push(`any ${be} volumes an earlier compose run left \u2014 Docker was not running`),t({label:"compose",mark:"skip",detail:`Docker is not running \u2014 any ${be} volumes an earlier compose run left are still there: start it, then \`docker compose -p ${be} down --volumes\``}))}}let y=await Oe(i.env),b=So(s);if(y.profiles[b]){let{[b]:L,...K}=y.profiles,re=y.current===b?Object.keys(K)[0]??"default":y.current;await wt({current:re,profiles:K},i.env),f.push(`profile ${b}`),t({label:"owner token",mark:"ok",detail:`profile ${b} removed from ~/.moa/config.json${y.current===b&&K[re]?`; \`moa\` now uses profile ${re}`:""}`})}if(u)await ic(rr(pt(i.env),"..","local"),{recursive:!0,force:!0}),f.push("~/.moa/local"),t({label:"this machine",mark:"ok",detail:"~/.moa/local removed"});else{for(let L of Ip(s))await ic(pe(i,L),{recursive:!0,force:!0});f.push("this target's files in ~/.moa/local"),t({label:"this machine",mark:"ok",detail:`${qn[s]}'s files in ~/.moa/local removed; the rest kept \u2014 ${d.map(L=>qn[L]).join(" and ")} still ${d.length===1?"uses":"use"} it`})}let g=l.answers.githubApp;g&&(u||await as(i,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 w=d.map(L=>`${qn[L]} \u2014 \`moa setup down --target ${L}\``).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: ${w}`}`}:u?{label:"billing",mark:"ok",detail:"nothing setup made is left running or stored"}:{label:"billing",mark:"ok",detail:`nothing ${qn[s]} had is left running or stored; still deployed: ${w}`});let v={target:s,at:n.toISOString(),removed:f,...p.length>0?{left:p}:{}};if(u)return{ok:!0,state:{...To(),down:v}};let{deployed:C,cloudflare:N,...W}=l,{sandbox:ie,githubApp:R,...Q}=l.answers;return c({ok:!0,state:{...W,answers:Q,...a===s?{screen:"where",passed:W.passed.filter(L=>L!=="deploy")}:{},down:v}})}async function Np(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"),c=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(!c.ok||!c.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 i=c.stdout.trim(),l=await o("pull","--ff-only");if(!l.ok)return n({label:"fnMoa",mark:"fail",detail:`git pull: ${ot(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 ${i}`:`${s} \u2192 ${d}, from ${i}`}),!0}async function Mp(e,t,n,o){let r=rr(t,"apps","console",".next");if(e.exists(r)&&!e.exists(rr(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 c=await e.exec("pnpm",a,{cwd:t,timeoutMs:9e5});if(!c.ok)return o({label:"build",mark:"fail",detail:`pnpm ${a.join(" ")}: ${ot(c.stderr||c.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 ir(e,t,n){e=Vn(e,n);let{sys:o,state:r}=e,s=Ct(r);if(!s)return Yn(t);if(s.runner==="image"||!o.root){let i=await vt(e);return i.forEach(t),i.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 Np(o,a,t))return{ok:!1};let c=s;if(r.target==="local"){let i=await ar(e,s,t,"stop");if(c=i.deployed,!i.ok)return{ok:!1,state:{...r,deployed:c}};if(!await Mp(o,a,s.runner==="compose",t))return{ok:!1,state:{...r,deployed:c}};t({label:"data",mark:"skip",detail:s.runner==="compose"?`kept in the ${be} 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:c}}}async function yc(e,t,n,o){e=Vn(e,o);let{sys:r,state:s}=e,a=Ct(s),c=s.target;if(!a||!c)return Yn(t);let i={},l=s.answers.githubApp,d=l?on(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(c!=="local"&&(f=(await n(`New GitHub token for the ${c==="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 w=>w.ok?(await w.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};i.github=f,t({label:"GitHub token",mark:"ok",detail:`${D} \u2014 ${g}'s, new`})}let p=a.memoryKept===!0||Ie(e)!==void 0,m=c==="vercel"||Ie(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 w=Ie({...e,secrets:{locusgraph:g}});if(!w)return t({label:"LocusGraph",mark:"fail",detail:"setup has no graph ID for this secret",fix:"run `moa setup`, type the graph ID on Memory (g), then `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};let v=s.answers.tenant??Ne(s.answers.owner),C=await Rn(r,w,v);if(!C.read.ok||!C.write.ok)return t({label:"LocusGraph",mark:"fail",detail:`the new secret does not work: ${C.read.why??C.write.why??"no answer"}`,fix:"check it, then run `moa setup rotate` again \u2014 nothing was changed"}),{ok:!1};i.locusgraph=g,t({label:"LocusGraph",mark:"ok",detail:`${D} \u2014 new; it reads and writes ${w.graph}`})}}let y=So(c);await li(r,y,a.workspaceUrl,Lr()),t({label:"owner token",mark:"ok",detail:`${D} \u2014 new; Deploy gives it to the workspace, and profile ${y} takes it once the workspace answers to it`}),l&&d?(await ss(r,l,{clientSecret:u||d.clientSecret,authSecret:wp(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"}),c==="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?i.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(c==="local"){let g=await ar(e,a,t,"stop");if(b=g.deployed,!g.ok)return{ok:!1,state:{...s,screen:"deploy",deployed:b}}}return{ok:!0,secrets:i,state:{...s,screen:"deploy",answers:{...s.answers,...i.github?{githubToken:"pasted"}:{}},deployed:b}}}Lt();Xe();ho();Qe();Qt();function Gp(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(hn(e))return"the LocusGraph secret (set LOCUSGRAPH_AGENT_SECRET)";if(t.answers.githubApp&&!on(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 bc(e,t,n){let o=Gp(e);if(o)return t({label:"update",mark:"fail",detail:`Update needs ${o}`}),!1;let r=await ir(e,t);if(r.state&&await n(r.state),!r.ok||!r.state)return!1;let s=r.state,a=Promise.resolve(),c=u=>{s=u,a=a.then(()=>n(u))},i=new Map,l=u=>{for(let f of u){let p=JSON.stringify(f);i.get(f.label)!==p&&t(f),i.set(f.label,p)}},d=s.target==="vercel"?er:s.target==="cloudflare"?zo:Fo;try{let u=await d({...e,state:s},l,f=>c({...s,deployed:f}),f=>c({...s,cloudflare:f}));return u.deployed&&c({...s,deployed:u.deployed,...u.ok?{answers:{...s.answers,removed:void 0}}:{}}),u.ok}finally{await a}}var Bp=["status","login","logout","licence","rotate","update","logs","stop","down"],Wp={ok:"\u2713",fail:"\u2717",wait:"\u2026",skip:"\u2013"},Gt=e=>{process.stdout.write(`${Wp[e.mark]} ${e.label.padEnd(12)} ${e.detail??""}
|
|
219
219
|
${e.mark==="fail"&&e.fix?` ${"".padEnd(12)} \u2192 ${e.fix}
|
|
220
|
-
`:""}`)};async function
|
|
221
|
-
`)}}var
|
|
222
|
-
`);;){let
|
|
223
|
-
`)}}async function
|
|
220
|
+
`:""}`)};async function ks(e){let t=ys({input:process.stdin,output:process.stdout});try{return await t.question(e)}finally{t.close()}}async function ws(e){process.stdout.write(e);let t=new Hp({write:(o,r,s)=>s()}),n=ys({input:process.stdin,output:t,terminal:!0});try{return await n.question("")}finally{n.close(),process.stdout.write(`
|
|
221
|
+
`)}}var Jp=e=>{let t=_(e,"lines");if(t===void 0)return 100;let n=Number(t);if(!Number.isInteger(n)||n<=0||n>2e3)throw new A("--lines must be a whole number, 1\u20132000");return n},$c=e=>$t.includes(e);async function Xn(e,t,n){let o=e==="status"||e==="down",r=_(n,"target");if(r!==void 0){if($c(r)||o&&r==="all")return r;throw new A(`--target must be one of ${$t.join(", ")}${o?" or all":""}, not ${r}`)}let s=rn(t);if(s.length<=1)return s[0];let a=`${s.join(", ")}${o?" or all":""}`;if(E(n,"yes")||!process.stdin.isTTY)throw new A(`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(c=>`--target ${c}`).join(", ")}
|
|
222
|
+
`);;){let c=(await ks(`Which one? ${a}: `)).trim().toLowerCase();if($c(c)&&s.includes(c)||o&&c==="all")return c;if(c==="")throw new A(`moa setup ${e} needs a target: ${a}`);process.stdout.write(`One of ${a}.
|
|
223
|
+
`)}}async function qp(e,t,n){let o=await Tn(n),r={state:o,sys:fn(n),secrets:{}},s=await Xn(e,o,t);if(e==="logs"){let i=await mc(r,Jp(t),s==="all"?void 0:s);if(!i.ok)return Gt(i.line),k.failed;for(let l of i.sections)process.stdout.write(`\u2500\u2500 ${l.name} \xB7 ${l.from}
|
|
224
224
|
`),process.stdout.write(l.text.trim()?`${l.text.replace(/\n?$/,`
|
|
225
225
|
`)}`:`(nothing yet)
|
|
226
|
-
`);return k.ok}let a;if(e==="down"&&s&&!E(t,"yes")){if(!process.stdin.isTTY)throw new A("moa setup down asks first, and needs a terminal to ask in \u2014 or --yes");let
|
|
227
|
-
${
|
|
228
|
-
`).join("")}${
|
|
229
|
-
`:""}`),(await
|
|
230
|
-
`),k.ok;a=async l=>/^y(es)?$/i.test((await
|
|
231
|
-
`);let l=await
|
|
226
|
+
`);return k.ok}let a;if(e==="down"&&s&&!E(t,"yes")){if(!process.stdin.isTTY)throw new A("moa setup down asks first, and needs a terminal to ask in \u2014 or --yes");let i=s==="all"?[]:rn(o).filter(l=>l!==s);if(process.stdout.write(`moa setup down deletes, for good:
|
|
227
|
+
${hc(o,s).map(l=>` \xB7 ${l}
|
|
228
|
+
`).join("")}${i.length>0?`and leaves ${i.join(" and ")} deployed, with ${i.length===1?"its":"their"} profile and record.
|
|
229
|
+
`:""}`),(await ks("Type down to delete it all: ")).trim()!=="down")return process.stdout.write(`Nothing was deleted.
|
|
230
|
+
`),k.ok;a=async l=>/^y(es)?$/i.test((await ks(l)).trim())}if(e==="status")return(await pc(r,Gt,s)).ok?k.ok:k.failed;if(e==="stop"){let i=await fc(r,Gt,s==="all"?void 0:s);return i.state&&await Rt(i.state,n),i.ok?k.ok:k.failed}let c=o;for(let i of s==="all"?rn(o):[s]){s==="all"&&process.stdout.write(`\u2500\u2500 ${i}
|
|
231
|
+
`);let l=await kc({...r,state:c},Gt,new Date,a,i);if(l.state&&(c=l.state,await Rt(c,n)),!l.ok)return k.failed}return k.ok}var vc=(e,t)=>{let n=_(e,t);if(n===void 0)return;let o=Number(n);if(!Number.isInteger(o)||o<=0||o>=65536)throw new A(`--${t} must be a port, 1\u201365535`);return o};async function Sc(){process.stdout.write(`
|
|
232
232
|
moa setup: handing the terminal to \`claude setup-token\`.
|
|
233
233
|
|
|
234
|
-
`);let e=
|
|
234
|
+
`);let e=jp("claude",["setup-token"],{stdio:"inherit"}),t=ys({input:process.stdin,output:process.stdout});await t.question(e.error?`
|
|
235
235
|
claude setup-token did not start: ${e.error.message}. Enter to go back. `:`
|
|
236
|
-
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
|
|
237
|
-
Sign in, check the page shows ${
|
|
238
|
-
`),new AbortController().signal);return process.stdout.write("Licence activated. Run `moa setup update` to apply it to the workspace.\n"),k.ok}if(n==="status"||n==="stop"||n==="logs"||n==="down")return
|
|
236
|
+
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 xc(e,t=process.env){let n=e.positional[1];if(n!==void 0&&!Bp.includes(n))throw new A(`moa setup has no ${n}`);if(n==="licence"){let b=await Tn(t),g=await Xn(n,b,e),v={state:it(b,g==="all"?void 0:g),sys:fn(t),secrets:{}};if(t.FNMOA_LICENCE_KEY||E(e,"key")){let C=t.FNMOA_LICENCE_KEY??(process.stdin.isTTY?await ws("Licence key: "):"");if(!C.trim())throw new A("Licence activation needs a key: set FNMOA_LICENCE_KEY or run in a terminal.");await Tr(v,C.trim())}else await Lo(v,C=>process.stdout.write(`Open ${C.url}
|
|
237
|
+
Sign in, check the page shows ${C.code}, and approve it. Waiting\u2026
|
|
238
|
+
`),new AbortController().signal);return process.stdout.write("Licence activated. Run `moa setup update` to apply it to the workspace.\n"),k.ok}if(n==="status"||n==="stop"||n==="logs"||n==="down")return qp(n,e,t);let o=e.positional[2],r;if(n==="login"&&o!==void 0||n==="logout"){if(!Ui(o))throw new A(`moa setup ${n} takes an agent: ${we.join(", ")}`);if(!process.stdin.isTTY&&n==="login")throw new A("moa setup login asks for a login, and needs a terminal to ask in");let b=await Tn(t),g=await Xn(n,b,e),w={state:it(b,g==="all"?void 0:g),sys:fn(t),secrets:{}};if(n==="login"&&w.state.deployed?.runner!=="image")r=o;else{let v=n==="logout"?await Hi(w,o,Gt):await ji(w,o,E(e,"key"),{print:Gt,secret:ws,setupToken:Sc});return v.state&&await Rt(v.state,t),v.ok?k.ok:k.failed}}if(!process.stdin.isTTY){if(n==="update"){let b=await Tn(t),g=await Xn(n,b,e),w=it(b,g==="all"?void 0:g);return await bc({state:w,sys:fn(t),secrets:{}},Gt,C=>Rt(C,t))?k.ok:k.failed}throw new A("moa setup asks questions, and needs a terminal to ask them in")}let s={},a=await Tn(t);if(n==="login"){let b=await Xn(n,a,e);b&&b!=="all"&&b!==a.target&&(a=it(a,b),await Rt(a,t))}if(n==="update"||n==="rotate"){let b={state:a,sys:fn(t),secrets:s},g=await Xn(n,a,e),w=g==="all"?void 0:g,v=n==="update"?await ir(b,Gt,w):await yc(b,Gt,ws,w);if(v.state&&await Rt(v.state,t),!v.ok||!v.state)return k.failed;a=v.state,s=v.secrets??{},process.stdout.write(`
|
|
239
239
|
Deploy runs next: Enter there hands it all to fnMoa.
|
|
240
|
-
`)}let
|
|
241
|
-
`),k.ok}var
|
|
240
|
+
`)}let c=vc(e,"workspace-port"),i=vc(e,"console-port"),{down:l,...d}=a,u={...d,answers:{...a.answers,...c?{workspacePort:c}:{},...i?{consolePort:i}:{}}};(c||i)&&await Rt(u,t);let f={};n==="login"&&(u.screen="logins",r&&(u.answers={...u.answers,agentCli:r}),u.passed.includes("deploy")&&(f={logins:"deploy"}));let p=fn(t),m,y=!1;for(;;){let b;if(await Fp(Kp(ac,{initial:u,sys:p,secrets:s,pasteFirst:y,after:f,save:w=>Rt(w,t),onDone:(w,v)=>{m={state:w,finished:v}},onHandOver:w=>{b=w}})).waitUntilExit(),!b)break;await Sc(),u=b,y=!0}return m&&!m.finished&&process.stdout.write(`Saved. \`moa setup\` picks up at ${po[m.state.screen]}.
|
|
241
|
+
`),k.ok}var Ac=`moa \u2014 talk to a workspace
|
|
242
242
|
|
|
243
243
|
moa setup set fnMoa up \u2014 this machine, Vercel or Cloudflare \u2014
|
|
244
244
|
step by step; resumes where it stopped
|
|
@@ -327,9 +327,9 @@ Flags
|
|
|
327
327
|
|
|
328
328
|
Environment
|
|
329
329
|
MOA_URL, MOA_TOKEN override the saved profile entirely
|
|
330
|
-
`,
|
|
331
|
-
${
|
|
332
|
-
`}var
|
|
330
|
+
`,Xp="Proprietary. Copyright (c) 2026 fnLog0. See LICENSE.";function Qp(){let e=JSON.parse(zp(new URL("../package.json",import.meta.url),"utf8"));return`moa ${typeof e.version=="string"?e.version:"unknown"}
|
|
331
|
+
${Xp}
|
|
332
|
+
`}var Zp=["profile","json","help"],ef={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 tf(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]),[c,i]=a;return a.length===2&&c!==void 0&&i!==void 0&&r[c]===s[i]&&r[i]===s[c]},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,c=0,i=0;for(;a<r.length&&c<s.length;){if(r[a]===s[c]){a+=1,c+=1;continue}if(++i>1)return!1;r.length>s.length?a+=1:(s.length>r.length||(a+=1),c+=1)}return i+(r.length-a)+(s.length-c)<=1};return t.find(r=>o(e,r))}function nf(e,t){let n=ef[e];if(n===void 0)return;let o=[...n,...Zp],r=[...t.flags.keys()].filter(c=>!o.includes(c));if(r.length===0)return;let s=r[0],a=tf(s,o);throw new A(`moa ${e} has no --${s}${a===void 0?"":` \u2014 did you mean --${a}?`}`)}var Ec={login:Rs,whoami:Ls,health:Ds,doctor:Us,profiles:Ms,use:js,logout:Gs,seats:Ns,spend:Hs,agent:Ks,peers:Bs,run:ia,runs:pa,watch:fa,logs:da,ask:ua,mcp:ta,send:Js,inbox:qs,mail:zs,delegate:Vs,delegations:Ys,thread:Xs,reply:Qs,team:na,ship:oa,jobs:ra,stop:sa,manager:ea,setup:xc};async function of(e,t=process.env){let n=As(e),o=n.positional[0];if(!o&&E(n,"version"))return process.stdout.write(Qp()),k.ok;if(!o||E(n,"help")||o==="help")return process.stdout.write(Ac),o?k.ok:k.misuse;if(o==="completion")return process.stdout.write(ma(n.positional[1]??"",Object.keys(Ec))),k.ok;let r=Ec[o];if(!r)throw new A(`unknown command ${o}
|
|
333
333
|
|
|
334
|
-
${
|
|
335
|
-
`),process.exitCode=n}}export{
|
|
334
|
+
${Ac}`);return nf(o,n),r(n,t)}var rf=()=>{let e=process.argv[1];if(!e)return!1;try{return Vp(e)===Yp(import.meta.url)}catch{return!1}};if(rf()){for(let e of[process.stdout,process.stderr])e.on("error",t=>{throw t.code==="EPIPE"&&process.exit(0),t});try{process.exitCode=await of(process.argv.slice(2))}catch(e){let{message:t,code:n}=Os(e);process.stderr.write(`moa: ${t}
|
|
335
|
+
`),process.exitCode=n}}export{Ec as COMMANDS,ef as FLAGS,Xp as NOTICE,nf as checkFlags,tf as nearest,of as run,Qp as versionText};
|