@anyforge/control 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -1
- package/dist/cli.js +190 -110
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`),console.log(
|
|
4
|
-
`),{label:"Claude Code usage telemetry (global)",path:n})}async function
|
|
5
|
-
`),o="";try{o=await
|
|
2
|
+
var Ws=Object.defineProperty;var Gs=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var Hs=(t,e)=>{for(var n in e)Ws(t,n,{get:e[n],enumerable:!0})};var Nn={};Hs(Nn,{default:()=>zl});var zl,qn=Gs(()=>{zl={name:"@anyforge/control",version:"0.8.0",description:"AnyForge CLI \u2014 hosted Oracle conversations, a governed LLM proxy, and a scriptable client for approvals, crews, work items and workflows.",license:"Apache-2.0",homepage:"https://anyforge.ai",repository:{type:"git",url:"git+https://github.com/pootonline/sovereign-engineering.git",directory:"packages/control/cli"},publishConfig:{access:"public"},type:"module",main:"dist/cli.js",bin:{anyforge:"dist/cli.js",control:"dist/cli.js"},files:["dist","README.md"],scripts:{build:"esbuild src/cli.ts --bundle --minify --platform=node --format=esm --outfile=dist/cli.js --packages=external && chmod +x dist/cli.js","type-check":"tsc --noEmit",test:"npm run build && node scripts/test.mjs",dev:"tsx src/cli.ts",prepublishOnly:"npm run build","publish:beta":"npm publish --access public --tag beta","publish:stable":"npm publish --access public"},engines:{node:">=22.19.0"},dependencies:{"@agentclientprotocol/sdk":"^1.9.0","@earendil-works/pi-tui":"^1.0.2",kleur:"^4.1.5",open:"^11.0.4",prompts:"^2.4.2","proper-lockfile":"^4.1.2"},devDependencies:{"@types/node":"^22.20.0","@types/prompts":"^2.4.9","@types/proper-lockfile":"^4.1.4",esbuild:"^0.28.2","node-pty":"^1.1.0",tsx:"^4.7.0",typescript:"^7.0.2"}}});import{red as Rs,yellow as Pd,dim as Xe,bold as W}from"kleur/colors";import{execFileSync as Vn}from"child_process";import ta from"open";import Yn from"prompts";import{green as Et,dim as M,bold as Z,yellow as Ce,cyan as an}from"kleur/colors";import{promises as rn}from"fs";import{homedir as Ks}from"os";import{join as Jr,dirname as Hd}from"path";var Qr=Jr(Ks(),".anyforge"),Hn=Jr(Qr,"credentials");async function Xr(t){await rn.mkdir(Qr,{recursive:!0,mode:448}),await rn.writeFile(Hn,JSON.stringify(t,null,2),{mode:384})}async function it(){try{let t=await rn.readFile(Hn,"utf8");return JSON.parse(t)}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Zr(){try{return await rn.unlink(Hn),!0}catch(t){if(t.code==="ENOENT")return!1;throw t}}import{promises as U}from"fs";import{execFileSync as zs}from"child_process";import{homedir as we}from"os";import{join as Y}from"path";import on from"prompts";import{dim as ce,green as zn,yellow as sn}from"kleur/colors";var Te="# === AnyForge Control \u2014 added by `anyforge init` ===",St="# === end AnyForge Control ===",je=".anyforge-bak",Vs=/claude_(max|pro|team|enterprise)/i,Ys=["CLAUDE_CODE_ENABLE_TELEMETRY","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_EXPORTER_OTLP_HEADERS","OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE"];async function oo(t){let e=[],n=await Js(t);if(n&&e.push(n),t.scope==="repo"){let r=await Qs(t);return r&&e.push(r),e}for(let r of Zs){let o=await r.detect();if(!(!o||!(await on({type:"confirm",name:"go",message:`Patch ${r.label} via shell rc (${ce(o.path)}) ?`,initial:!0})).go))try{await r.patch(o,t),e.push({label:r.label,path:o.path}),console.log(zn(" \u2714")+" "+r.label)}catch(s){console.warn(" \u26A0 Skipped "+r.label+": "+s.message)}}return e}async function Js(t){let e=t.scope==="repo"&&t.repoRoot?{dir:Y(t.repoRoot,".claude"),file:"settings.local.json"}:{dir:Y(we(),".claude"),file:"settings.json"},n=Y(e.dir,e.file);if(!(await on({type:"confirm",name:"go",message:`Patch Claude Code config at ${ce(n)} ?`,initial:!0})).go)return null;await U.mkdir(e.dir,{recursive:!0});let o=await U.readFile(n,"utf8").catch(()=>"{}"),i;try{i=JSON.parse(o)}catch{i={}}let s=i.env??{},a=s.ANTHROPIC_BASE_URL;return a&&!a.includes(t.endpoint)&&(a.includes("localhost")||a.includes("127.0.0.1")||a.includes("0.0.0.0"))&&(console.log(),console.log(sn(" \u26A0 Local LLM proxy detected: ")+ce(a)),console.log(ce(" AnyForge only routes Anthropic Claude models. Local models (Ollama,")),console.log(ce(" LM Studio, etc.) will not work in Claude Code after this patch.")),console.log(ce(" To restore: remove ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN")),console.log(ce(` from ${n}`)),console.log(),!(await on({type:"confirm",name:"go",message:"Overwrite existing local LLM proxy config?",initial:!1})).go)?null:(await pe(n)&&!await pe(n+je)&&await U.copyFile(n,n+je),s.ANTHROPIC_BASE_URL=`${t.endpoint}/v1/anthropic`,s.ANTHROPIC_AUTH_TOKEN=t.apiKey,delete s.ANTHROPIC_API_KEY,i.env=s,await U.writeFile(n,JSON.stringify(i,null,2)+`
|
|
3
|
+
`),console.log(zn(" \u2714")+" Claude Code"),{label:t.scope==="repo"?"Claude Code (repo)":"Claude Code (global)",path:n})}async function io(t){let e=Y(we(),".claude"),n=Y(e,"settings.json");await U.mkdir(e,{recursive:!0});let r=await U.readFile(n,"utf8").catch(()=>"{}"),o;try{o=JSON.parse(r)}catch{o={}}let i=o.env??{},s=i.OTEL_EXPORTER_OTLP_ENDPOINT;return s&&!Kn(s,co(t.endpoint))?(console.log(sn(" \u26A0 An OpenTelemetry endpoint is already configured: ")+ce(s)),console.log(ce(" Leaving it untouched. To send Claude Code usage to AnyForge, point")),console.log(ce(" OTEL_EXPORTER_OTLP_ENDPOINT at "+t.endpoint+"/api/otel")),null):(await pe(n)&&!await pe(n+je)&&await U.copyFile(n,n+je),i.CLAUDE_CODE_ENABLE_TELEMETRY="1",i.OTEL_METRICS_EXPORTER="otlp",i.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",i.OTEL_EXPORTER_OTLP_ENDPOINT=`${t.endpoint}/api/otel`,i.OTEL_EXPORTER_OTLP_HEADERS=`Authorization=Bearer ${t.apiKey}`,i.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE="delta",o.env=i,await U.writeFile(n,JSON.stringify(o,null,2)+`
|
|
4
|
+
`),{label:"Claude Code usage telemetry (global)",path:n})}async function Qs(t){if(!t.repoRoot)return null;let e=Y(t.repoRoot,".env");if(!(await on({type:"confirm",name:"go",message:`Write AnyForge env block to ${ce(e)} ?`,initial:!0})).go)return null;let r=[Te,`ANYFORGE_API_KEY=${t.apiKey}`,`ANTHROPIC_BASE_URL=${t.endpoint}/v1/anthropic`,`ANTHROPIC_AUTH_TOKEN=${t.apiKey}`,`OPENAI_BASE_URL=${t.endpoint}/v1/openai`,`OPENAI_API_KEY=${t.apiKey}`,St,""].join(`
|
|
5
|
+
`),o="";try{o=await U.readFile(e,"utf8")}catch{}let i;return o.includes(Te)?i=o.replace(new RegExp(`${Te}[\\s\\S]*?${St}\\n?`),r):o.length===0?i=r:i=o.endsWith(`
|
|
6
6
|
`)?`${o}${r}`:`${o}
|
|
7
|
-
${r}`,i!==o&&await
|
|
8
|
-
`).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(
|
|
9
|
-
`);for(let n of[".zshrc",".bashrc"]){let r=
|
|
7
|
+
${r}`,i!==o&&await U.writeFile(e,i),await Xs(t.repoRoot),console.log(zn(" \u2714")+" .env"),{label:".env (repo)",path:e}}async function Xs(t){let e=Y(t,".gitignore");try{(await U.readFile(e,"utf8")).split(`
|
|
8
|
+
`).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(sn(" \u26A0 .env is not in .gitignore \u2014 add it before committing."))}catch{console.log(sn(" \u26A0 No .gitignore found \u2014 make sure .env is excluded before committing."))}}var Zs=[{label:"Cursor (shell hint)",async detect(){let t=Y(we(),".cursor");return await pe(t)?{path:".cursor (shell hint)"}:null},async patch(t,e){await eo(e)}},{label:"Cline / Continue (shell hint)",async detect(){let t=await pe(Y(we(),".cline")),e=await pe(Y(we(),".continue"));return t||e?{path:"~/.cline or ~/.continue (shell hint)"}:null},async patch(t,e){await eo(e)}}];async function eo(t){let e=[Te,`export ANYFORGE_API_KEY="${t.apiKey}"`,`export ANTHROPIC_BASE_URL="${t.endpoint}/v1/anthropic"`,`export ANTHROPIC_AUTH_TOKEN="${t.apiKey}"`,`export OPENAI_BASE_URL="${t.endpoint}/v1/openai"`,`export OPENAI_API_KEY="${t.apiKey}"`,"unset ANTHROPIC_API_KEY 2>/dev/null || true",St,""].join(`
|
|
9
|
+
`);for(let n of[".zshrc",".bashrc"]){let r=Y(we(),n);if(!await pe(r))continue;let o=await U.readFile(r,"utf8"),i;o.includes(Te)?i=o.replace(new RegExp(`${Te}[\\s\\S]*?${St}\\n?`),e):i=o.endsWith(`
|
|
10
10
|
`)?`${o}
|
|
11
11
|
${e}`:`${o}
|
|
12
12
|
|
|
13
|
-
${e}`,i!==o&&await
|
|
14
|
-
`),!0):!1}async function
|
|
13
|
+
${e}`,i!==o&&await U.writeFile(r,i)}}async function pe(t){try{return await U.access(t),!0}catch{return!1}}async function so(){try{let t=await U.readFile(Y(we(),".claude.json"),"utf8"),n=JSON.parse(t).oauthAccount,r=n?.organizationType??"";if(n&&Vs.test(r))return{isSubscription:!0,plan:/max/i.test(r)?"Max":/pro/i.test(r)?"Pro":/enterprise/i.test(r)?"Enterprise":/team/i.test(r)?"Team":r,email:n.emailAddress??null}}catch{}return{isSubscription:!1,plan:null,email:null}}async function ao(t={}){let e=t.endpoint?co(t.endpoint):null,n=[],r=Y(we(),".claude","settings.json");await to(r,e)&&n.push(r);for(let i of[".zshrc",".bashrc"]){let s=Y(we(),i);await no(s)&&n.push(s)}let o=t.repoRoot??ea(process.cwd());if(o){let i=Y(o,".claude","settings.local.json");await to(i,e)&&n.push(i);let s=Y(o,".env");await no(s)&&n.push(s)}return{reverted:n}}function Kn(t,e){return t?t.includes("anyforge")?!0:e?t.includes(e):!1:!1}async function to(t,e){if(!await pe(t))return!1;if(await pe(t+je))return await U.copyFile(t+je,t),await U.unlink(t+je).catch(()=>{}),!0;let n;try{n=JSON.parse(await U.readFile(t,"utf8"))}catch{return!1}let r=n.env;if(!r)return!1;let o=!1,i=[["ANTHROPIC_BASE_URL","ANTHROPIC_AUTH_TOKEN"],["OPENAI_BASE_URL","OPENAI_API_KEY"]];for(let[s,a]of i)Kn(r[s],e)&&(delete r[s],delete r[a],o=!0);if(r.ANYFORGE_API_KEY&&(delete r.ANYFORGE_API_KEY,o=!0),Kn(r.OTEL_EXPORTER_OTLP_ENDPOINT,e))for(let s of Ys)s in r&&(delete r[s],o=!0);return o?(Object.keys(r).length===0&&delete n.env,await U.writeFile(t,JSON.stringify(n,null,2)+`
|
|
14
|
+
`),!0):!1}async function no(t){let e;try{e=await U.readFile(t,"utf8")}catch{return!1}if(!e.includes(Te))return!1;let n=e.replace(new RegExp(`\\n?${ro(Te)}[\\s\\S]*?${ro(St)}\\n?`),`
|
|
15
15
|
`).replace(/\n{3,}/g,`
|
|
16
16
|
|
|
17
|
-
`);return await
|
|
18
|
-
`),n=0);let o=await(await fetch(t.pollUrl)).json();if(o.status==="authorized"&&o.data)return process.stdout.write(" "+
|
|
19
|
-
`),{apiKey:o.data.apiKey,companyId:o.data.companyId,endpoint:t.endpoint,savedAt:new Date().toISOString()};if(o.status==="expired"||o.status==="not_found")throw process.stdout.write(`
|
|
20
|
-
`),new Error(`Authorisation ${o.status}. Re-run \`anyforge init\`.`)}throw new Error("Authorisation timed out (15 min). Re-run `anyforge init`.")}function
|
|
21
|
-
${i}`}function
|
|
22
|
-
`)}function
|
|
23
|
-
`))}[n.proxy,n.subscriptionTelemetry].some(i=>i.state==="unavailable")&&(process.exitCode=1)}import{spawn as
|
|
24
|
-
`)),process.exit(1));let n=
|
|
25
|
-
`);let r=await
|
|
17
|
+
`);return await U.writeFile(t,n),!0}function ro(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function co(t){try{return new URL(t).host}catch{return null}}function ea(t){try{return zs("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}var na=2e3,ra=900*1e3;async function uo(t){let{endpoint:e}=t;console.log(Z("AnyForge Control \u2014 device authorisation")),console.log(M(` endpoint: ${e}`));let n=process.cwd(),r=lo(n),o=r?da(r):null,s=await(await fetch(`${e}/api/cli/init`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({repo:o})})).json();if(!s.ok||!s.data)throw new Error(`Could not start the authorisation flow: ${s.error??"unknown error"}`);let{code:a,authUrl:c,pollUrl:d}=s.data;console.log(),console.log("Open this URL in your browser to authorise:"),console.log(` ${an(c)}`),console.log(M(` Code: ${a}`)),console.log();try{await ta(c)}catch{}let{credentials:p,modelRouting:l}=await oa({pollUrl:d,endpoint:e});if(await Xr(p),console.log(Et("\u2714")+" API key saved to "+M("~/.anyforge/credentials")),!l){await aa(e,p.apiKey);return}let A=await so();if(A.isSubscription&&!t.force){await sa(A,e,p.apiKey);return}let k=process.cwd(),x=lo(k),b=await ca(x),g=await oo({endpoint:e,apiKey:p.apiKey,scope:b,repoRoot:x});g.length===0?(console.log(Ce("No agent configs were patched.")),console.log(M(" Set ANTHROPIC_BASE_URL="+e+"/v1/anthropic and")),console.log(M(" ANTHROPIC_AUTH_TOKEN=<your AnyForge key> manually if needed."))):la(g),console.log(),A.isSubscription&&t.force&&(console.log(Ce(` \u26A0 --force: your Claude ${A.plan??""} subscription login is now shadowed`)),console.log(Ce(" by the AnyForge token \u2014 Claude Code will use the BYOK proxy, which")),console.log(Ce(" needs an Anthropic API key on your company. Run ")+Z("anyforge logout")),console.log(Ce(" to revert and restore your subscription."))),b==="repo"&&(console.log(M(" Re-run this command in another repo to patch it too,")),console.log(M(' or run from any directory and choose "global" to install system-wide.'))),console.log(Z("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+an(`${e}/usage`))}async function oa(t){let e=Date.now();process.stdout.write("Waiting for browser authorisation\u2026 ");let n=0;for(;Date.now()-e<ra;){await new Promise(i=>setTimeout(i,na)),process.stdout.write("."),n+=1,n>40&&(process.stdout.write(`
|
|
18
|
+
`),n=0);let o=await(await fetch(t.pollUrl)).json();if(o.status==="authorized"&&o.data)return process.stdout.write(" "+Et("done")+`
|
|
19
|
+
`),{credentials:{apiKey:o.data.apiKey,companyId:o.data.companyId,endpoint:t.endpoint,savedAt:new Date().toISOString()},modelRouting:o.data.modelRouting!==!1};if(o.status==="expired"||o.status==="not_found")throw process.stdout.write(`
|
|
20
|
+
`),new Error(`Authorisation ${o.status}. Re-run \`anyforge init\`.`)}throw new Error("Authorisation timed out (15 min). Re-run `anyforge init`.")}function ia(t,e){return["mcp","add","anyforge","--env",`ANYFORGE_API_TOKEN=${e}`,"--env",`ANYFORGE_API_URL=${t}`,"--","npx","-y","@anyforge/mcp-server"]}async function sa(t,e,n){let r=t.email?` (${t.email})`:"";if(console.log(),console.log(Ce(` \u26A0 Claude ${t.plan??""} subscription login detected${r}.`)),console.log(),console.log(" AnyForge Control\u2019s inference proxy is "+Z("BYOK-only")+" \u2014 it routes"),console.log(" Claude Code through your own Anthropic API key for metering and"),console.log(" governance. Pointing Claude Code at the proxy would "+Z("override and")),console.log(" "+Z("disable your subscription login")+", so we did NOT patch your config."),console.log(),console.log(" Instead, use your subscription for inference and add the "+Z("AnyForge")),console.log(" "+Z("MCP server")+" for control (HIL approvals, work items, crews):"),console.log(),await po(e,n),console.log(),(await Yn({type:"confirm",name:"go",message:"Send Claude Code usage to your AnyForge dashboard? (token counts only \u2014 no prompt content)",initial:!0})).go){let i=await io({endpoint:e,apiKey:n});i&&(console.log(Et(" \u2714")+" Usage telemetry enabled \u2014 view it at "+an(`${e}/usage`)),console.log(M(" (written to "+i.path+"; `anyforge logout` removes it)")))}else console.log(M(" Skipped \u2014 your subscription usage stays local to Claude Code."));console.log(),console.log(M(" Want the metered BYOK proxy instead? Re-run with ")+Z("--force")+M(" using an")),console.log(M(" Anthropic API key on your company (this disables your subscription in")),console.log(M(" Claude Code; ")+Z("anyforge logout")+M(" reverts it).")),console.log(),console.log(Z("Done.")+" Run `anyforge status` to check identity, local setup and recorded activity."),console.log(" MCP access, BYOK inference and optional subscription telemetry are separate. Usage: "+an(`${e}/usage`))}async function aa(t,e){console.log(),console.log(Ce(" This key is read-only")+M(" (Viewer role).")),console.log(" It reads programs, initiatives, work items and the strategy through the"),console.log(" "+Z("AnyForge MCP server")+". Routing model calls through the company's keys"),console.log(" needs the Editor role, so your Claude Code config was "+Z("not")+" patched."),console.log(),await po(t,e),console.log(),console.log(Z("Done.")+" Run `anyforge status` to check identity and local setup.")}async function po(t,e){let n=ia(t,e),r="claude mcp add anyforge -- npx -y @anyforge/mcp-server";if((await Yn({type:"confirm",name:"go",message:"Add the AnyForge MCP server to Claude Code now?",initial:!0})).go)try{Vn("claude",n,{stdio:"inherit"}),console.log(Et(" \u2714")+" AnyForge MCP server added to Claude Code.")}catch{console.log(Ce(" Could not run `claude mcp add` automatically (is the `claude` CLI on PATH?).")),console.log(M(" Add it manually:")),console.log(" "+r)}else console.log(M(" Add it later with:")),console.log(" "+r)}function lo(t){try{return Vn("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}async function ca(t){return t?(await Yn({type:"select",name:"scope",message:"Where should AnyForge patch Claude Code?",choices:[{title:`This repo only (${t}/.claude/settings.local.json + .env)`,value:"repo"},{title:"Globally (~/.claude/settings.json)",value:"global"}],initial:0})).scope??"repo":(console.log(M(" Not a git repo root \u2014 installing globally.")),console.log(M(" Run this from a repo root if you want a per-repo install instead.")),"global")}function la(t){console.log(Et("\u2714")+" Patched "+t.length+" agent config"+(t.length===1?"":"s")+":");for(let e of t)console.log(` ${M("\u2022")} ${e.label} ${M("("+e.path+")")}`)}function da(t){try{let e=Vn("git",["remote","get-url","origin"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n=e.match(/^git@[\w.-]+:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);if(n)return n[1];let r=e.match(/^https?:\/\/[\w.-]+\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);return r?r[1]:null}catch{return null}}import{dim as Xn,yellow as ya,bold as wa}from"kleur/colors";var m={OK:0,FAILURE:1,USAGE:2,NO_CREDENTIALS:3,API_ERROR:4,VERIFICATION_FAILED:5,INTERRUPTED:130},f=class extends Error{exitCode;hint;constructor(e,n=m.FAILURE,r){super(e),this.name="CliError",this.exitCode=n,this.hint=r}},y=class extends f{constructor(e,n){super(e,m.USAGE,n),this.name="UsageError"}},fe=class extends f{constructor(){super("Not authenticated \u2014 no ANYFORGE_API_TOKEN and no saved credential.",m.NO_CREDENTIALS,"Run `npx @anyforge/control init`, or set ANYFORGE_API_TOKEN for unattended use."),this.name="NotAuthenticatedError"}},fo=4e3,le=class extends f{code;status;details;constructor(e,n,r,o,i){super(ua(e,r,o),m.API_ERROR,pa(e,n,i)),this.name="ControlApiError",this.code=e,this.status=n,o&&(this.details=o)}};function ua(t,e,n){let r=e?`${t}: ${e}`:t;if(!n)return r;let o=JSON.stringify(n),i=o.length>fo?`${o.slice(0,fo)}\u2026(truncated)`:o;return`${r}
|
|
21
|
+
${i}`}function pa(t,e,n){if(t==="api_key_expired")return`This API key has expired. Mint a new key in Settings \u2192 API keys, then re-run \`npx @anyforge/control init\` or update ANYFORGE_API_TOKEN. See ${n?`${n.replace(/\/$/,"")}/docs?page=service-accounts-and-key-expiry`:'the "Service accounts and API key expiry" guide'}.`;if(t==="missing_api_key"||t==="invalid_api_key"||t==="invalid_token"||e===401)return"The key was rejected. Re-run `npx @anyforge/control init`.";if(t==="insufficient_scope"||e===403)return"This key lacks the scope for that command. Run `anyforge whoami` to see its scopes.";if(e===404)return"No such id in this company. Check it with the matching list command.";if(e===429)return"Rate limited. Retry after a short pause."}var mo="https://crew.anyforge.ai";async function $e(t){let e=process.env.ANYFORGE_API_TOKEN,n=t||process.env.ANYFORGE_API_URL;if(e)return{apiKey:e,endpoint:n||mo};let r=await it();if(r?.apiKey)return{apiKey:r.apiKey,endpoint:n||r.endpoint||mo};throw new fe}var Jn=null;function go(t){Jn=t&&/^[a-z][a-z0-9-]*$/.test(t)?`anyforge ${t}`:null}async function S(t,e,n,r){let o=`${t.endpoint.replace(/\/$/,"")}/api/v1/control${n}`,i;try{i=await fetch(o,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.apiKey}`,"X-AnyForge-Client":"anyforge-cli",...Jn?{"X-AnyForge-Tool":Jn}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}})}catch(a){throw new f(`Cannot reach ${t.endpoint}: ${a instanceof Error?a.message:String(a)}`,m.FAILURE,"Check your network, or pass --endpoint if this workspace is self-hosted.")}let s;try{s=await i.json()}catch{throw new f(`Non-JSON response from ${o} (HTTP ${i.status}).`,m.FAILURE)}if(!i.ok||s.ok===!1){let a=typeof s.error=="string"?s.error:`http_${i.status}`,c=typeof s.message=="string"?s.message:typeof s.detail=="string"?s.detail:void 0;throw new le(a,i.status,c,ma(s),t.endpoint)}return s.data}var fa=new Set(["ok","error","message","detail"]);function ma(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=Object.entries(t).filter(([n,r])=>!fa.has(n)&&r!==void 0);return e.length?Object.fromEntries(e):void 0}function H(t){let e=Object.entries(t).filter(([,n])=>n!==void 0&&n!=="").map(([n,r])=>`${encodeURIComponent(n)}=${encodeURIComponent(String(r))}`);return e.length?`?${e.join("&")}`:""}async function Tt(t){return S(t,"GET","/me")}function Qn(t,e,n,r){let o=t?.entityLabels?.[e]?.[n];return typeof o=="string"&&o.trim()?o:r}async function ho(t){try{return await Tt(t)}catch{return null}}function yo(t){return[...t].length}function st(t,e){if(e<=0)return"";let n=[...t];return n.length<=e?t:e===1?"\u2026":`${n.slice(0,e-1).join("")}\u2026`}function D(t,e){if(t.length===0)return"";let n=t.map(i=>e.map(s=>{let a=s.value(i)??"";return s.max?st(a,s.max):a})),r=e.map((i,s)=>Math.max(yo(i.header),...n.map(a=>yo(a[s])))),o=i=>i.map((s,a)=>a===i.length-1?s:s.padEnd(r[a]+2)).join("").replace(/\s+$/,"");return[o(e.map(i=>i.header)),...n.map(o)].join(`
|
|
22
|
+
`)}function _(t){return JSON.stringify(t,null,2)}function wo(t,e=Date.now()){let n=ga(t);if(n===null)return"\u2014";let r=Math.max(0,Math.floor((e-n)/1e3));if(r<60)return`${r}s`;let o=Math.floor(r/60);if(o<60)return`${o}m`;let i=Math.floor(o/60);return i<48?`${i}h`:`${Math.floor(i/24)}d`}function ga(t){if(typeof t=="number")return Number.isFinite(t)?t:null;if(typeof t=="string"){let e=Date.parse(t);return Number.isNaN(e)?null:e}return null}function E(t,e,n="\u2014"){let r=t[e];return typeof r=="string"&&r.trim()?r:typeof r=="number"&&Number.isFinite(r)?String(r):n}function X(t,e,n="\u2014"){for(let r of e){let o=t[r];if(typeof o=="string"&&o.trim())return o}return n}function se(t){let e=Array.isArray(t)?t:t&&typeof t=="object"?ha(t):null;return e?e.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)):[]}function ha(t){for(let e of["items","results","rows","data"])if(Array.isArray(t[e]))return t[e];return null}function cn(t,e,n){let r=t?`last ${t}d`:"all time";return!e||!n?r:`last ${Math.max(1,Math.round((Date.now()-n)/864e5))}d of ${t?`${t}d`:"all time"} \u2014 read cap`}async function vo(t){let e=await $e(t.endpoint),n=await Tt(e);if(t.json){console.log(_(n));return}console.log(`Company: ${wa(n.companyName??n.companyId)}${n.companyName?Xn(` (${n.companyId})`):""}`),console.log(`Endpoint: ${e.endpoint}`),console.log(`Scopes: ${n.scopes.length?n.scopes.join(", "):ya("none")}`),n.userId&&console.log(`User: ${n.userId}`);let r=await it();console.log(r?Xn(`Key tail: \u2026${r.apiKey.slice(-4)} \xB7 saved ${new Date(r.savedAt).toLocaleString()}`):Xn("Key from: ANYFORGE_API_TOKEN (no saved credential file)"))}import{green as bo,dim as ln,yellow as va}from"kleur/colors";async function Oo(){let t=await it(),e=await Zr(),{reverted:n}=await ao({endpoint:t?.endpoint??null});if(console.log(e?bo("\u2714")+" Local credentials cleared.":va("No credentials were saved on this machine.")),n.length){console.log(bo("\u2714")+" Reverted patched configs:");for(let r of n)console.log(` ${ln("\u2022")} ${r}`);console.log(ln(" Your previous Claude Code login (if any) is restored."))}else console.log(ln(" No AnyForge config patches found to revert."));console.log(ln(" Revoke the key server-side at /settings \u2192 API keys."))}import{readFile as dn}from"node:fs/promises";import{homedir as ba}from"node:os";import{dirname as Zn,join as ve}from"node:path";function Pe(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)?t:{}}async function ko(t,e={home:ba(),cwd:process.cwd(),env:process.env}){let n=t.endpoint.replace(/\/$/,""),r=[],o={};try{o=Pe(JSON.parse(await dn(ve(e.home,".anyforge","credentials"),"utf8")))}catch{}function i(a,c){let d=Pe(c),p=Pe(d.env),l=Object.values(Pe(d.mcpServers));r.push({source:a,readable:!0,mcp:l.some(A=>{let k=Pe(A),x=Pe(k.env),b=Array.isArray(k.args)?k.args:[],g=x.ANYFORGE_API_TOKEN??e.env.ANYFORGE_API_TOKEN,R=x.ANYFORGE_API_URL??e.env.ANYFORGE_API_URL,P=g??o.apiKey,j=R??(g?"https://crew.anyforge.ai":o.endpoint)??"https://crew.anyforge.ai";return b.includes("@anyforge/mcp-server")&&P===t.apiKey&&j===n}),proxy:p.ANTHROPIC_BASE_URL===`${n}/v1/anthropic`&&p.ANTHROPIC_AUTH_TOKEN===t.apiKey,subscriptionTelemetry:String(p.CLAUDE_CODE_ENABLE_TELEMETRY)==="1"&&(p.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL??p.OTEL_EXPORTER_OTLP_PROTOCOL)==="http/json"&&(p.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===void 0||p.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT===`${n}/api/otel/v1/metrics`)&&p.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE==="delta"&&p.OTEL_METRICS_EXPORTER==="otlp"&&p.OTEL_EXPORTER_OTLP_ENDPOINT===`${n}/api/otel`&&(p.OTEL_EXPORTER_OTLP_METRICS_HEADERS??p.OTEL_EXPORTER_OTLP_HEADERS)===`Authorization=Bearer ${t.apiKey}`})}i("Current shell",{env:e.env});let s=e.cwd;for(;Zn(s)!==s;){try{await dn(ve(s,".git","HEAD"));break}catch{}try{await dn(ve(s,".git"));break}catch{}s=Zn(s)}Zn(s)===s&&(s=e.cwd);for(let a of new Set([ve(e.home,".claude","settings.json"),ve(s,".claude","settings.json"),ve(s,".claude","settings.local.json"),ve(s,".mcp.json"),ve(e.home,".claude.json")]))try{let c=JSON.parse(await dn(a,"utf8"));if(i(a,c),a===ve(e.home,".claude.json")){let d=Pe(Pe(c).projects)[s];d&&i(`${a} (project)`,d)}}catch(c){c.code!=="ENOENT"&&r.push({source:a,mcp:!1,proxy:!1,subscriptionTelemetry:!1,readable:!1})}return r}var Oa=/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\|[@-_])/g,ka=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g;function u(t){return t.replace(Oa,"").replace(ka,"")}function er(t){try{let e=new URL(t);return e.protocol!=="https:"&&e.protocol!=="http:"||!e.hostname||e.username||e.password?null:e.toString()}catch{return null}}function Ao(t){return t.replace(/\x1B\]8;;([^\x1B\x07]*)(?:\x07|\x1B\\)/g,(e,n)=>er(n)?e:"\x1B]8;;\x07")}function Ro(t,e){if(!e||!t.startsWith("/")||t.startsWith("//"))return t;try{return new URL(t,e).toString()}catch{return t}}function xo(t,e){return e?t.replace(/(\]\()(<?)(\/(?!\/)[^\s)>]*)/g,(n,r,o,i)=>`${r}${o}${Ro(i,e)}`):t}function tr(t,e){let r=u(t).replace(/(!?)\[([^\]\n]+)\]\((<[^>\n]*>|[^\s)]+)(?:\s+[^)]*)?\)/g,(s,a,c,d)=>{let p=Ro(d.startsWith("<")&&d.endsWith(">")?d.slice(1,-1):d,e),l=er(p);return l?`${a}[${c}](${l})`:c}),o=new Map,i=r.replace(/^(\s{0,3}\[([^\]]+)\]:\s*)(\S+)(.*)$/gm,(s,a,c,d,p)=>{let l=er(d);return o.set(c.trim().toLowerCase(),l),l?`${a}${l}${p}`:""});return o.size===0?i:i.replace(/(!?)\[([^\]\n]+)\](?:\[([^\]\n]*)\])?/g,(s,a,c,d)=>{let p=(d||c).trim().toLowerCase();return!o.has(p)||o.get(p)?s:c})}function be(t){return typeof t=="string"?u(t):Array.isArray(t)?t.map(e=>be(e)):t&&typeof t=="object"?Object.fromEntries(Object.entries(t).map(([e,n])=>[u(e),be(n)])):t}function Io(t){return t.state==="received"?t.lastRecordedAt??"Timestamp unavailable":t.state==="unattributed"?"Unknown: this credential has no user attribution":t.state==="unavailable"?"Unavailable: retry the check":"No recorded activity for this user in this company"}async function _o(t){let e=await $e(t.endpoint),n=await S(e,"GET","/connection"),r=await ko(e),o={...n,endpoint:e.endpoint,localConfiguration:r};if(t.json)console.log(_(o));else{let i=[`Endpoint: ${e.endpoint}`,`Company: ${n.identity.companyName??n.identity.companyId} (${n.identity.companyId})`,`User: ${n.identity.userId??"Unattributed credential"}`,`Granted scopes: ${n.identity.scopes.join(", ")||"none"} (individual actions also enforce role permissions)`,`Control API: authenticated at ${n.checkedAt}`,"MCP: shared Control API verified; confirm the MCP connection in your agent client."];for(let[s,a]of[["mcp","MCP"],["proxy","Inference proxy"],["subscriptionTelemetry","Subscription telemetry"]]){let c=r.filter(d=>d[s]).map(d=>d.source);i.push(`${a} matching local settings: ${c.join(", ")||"not detected"}`)}for(let s of r.filter(a=>!a.readable))i.push(`Could not inspect: ${s.source}`);i.push("Settings are observations, not proof of effective client configuration; managed settings and other clients are not inspected.",`Latest recorded proxy usage: ${Io(n.proxy)}`,`Latest recorded subscription telemetry: ${Io(n.subscriptionTelemetry)}`,"Activity is for this user across devices, not proof that this installation is working now.","Inference forwarding was not tested. The proxy needs a company BYOK provider key.","Subscription inference stays with your provider. Optional telemetry records token counts and estimated cost, not prompts or full traces.",`Usage: ${e.endpoint.replace(/\/$/,"")}/usage`,`Traces: ${e.endpoint.replace(/\/$/,"")}/traces (MCP access alone does not create inference traces)`),console.log(i.map(u).join(`
|
|
23
|
+
`))}[n.proxy,n.subscriptionTelemetry].some(i=>i.state==="unavailable")&&(process.exitCode=1)}import{spawn as Aa}from"node:child_process";import{readFile as Ra}from"node:fs/promises";import{red as Ct,green as nr,yellow as xa,dim as $t,bold as So}from"kleur/colors";async function Eo(t){let e=await Ia(t.patchFile);e.trim()||(process.stderr.write(Ct(`Empty patch \u2014 nothing to apply.
|
|
24
|
+
`)),process.exit(1));let n=_a(e);process.stderr.write($t(`Patch: ${n.files} ${n.files===1?"file":"files"}, `)+nr(`+${n.added}`)+" "+Ct(`-${n.removed}`)+`
|
|
25
|
+
`);let r=await rr(["apply","--check"],e);if(r.code!==0){if(t.threeWay){let i=await rr(["apply","--3way"],e);i.code!==0&&(process.stderr.write(Ct(`3-way apply failed:
|
|
26
26
|
`)+i.stderr+`
|
|
27
|
-
`),process.exit(1)),process.stderr.write(
|
|
28
|
-
`));return}process.stderr.write(
|
|
27
|
+
`),process.exit(1)),process.stderr.write(xa(`\u2713 Applied via 3-way merge \u2014 review conflicts before committing.
|
|
28
|
+
`));return}process.stderr.write(Ct(`Patch does not apply cleanly:
|
|
29
29
|
`)+r.stderr+`
|
|
30
|
-
`),process.stderr.write(
|
|
31
|
-
`)),process.exit(1)}if(t.dryRun){process.stderr.write(
|
|
32
|
-
`));return}let o=await
|
|
30
|
+
`),process.stderr.write($t("Try ")+So("--3way")+$t(` to attempt a 3-way merge against the index.
|
|
31
|
+
`)),process.exit(1)}if(t.dryRun){process.stderr.write(nr(`\u2713 Patch would apply cleanly.
|
|
32
|
+
`));return}let o=await rr(["apply"],e);o.code!==0&&(process.stderr.write(Ct(`git apply failed:
|
|
33
33
|
`)+o.stderr+`
|
|
34
|
-
`),process.exit(1)),process.stderr.write(
|
|
35
|
-
`)),n.files>0&&process.stderr.write(
|
|
36
|
-
`))}async function
|
|
37
|
-
`))if(o.startsWith("diff --git"))e+=1;else{if(o.startsWith("+++")||o.startsWith("---"))continue;o.startsWith("+")?n+=1:o.startsWith("-")&&(r+=1)}return e===0&&(n>0||r>0)&&(e=(t.match(/^\+\+\+ /gm)??[]).length),{files:e,added:n,removed:r}}import{bold as
|
|
38
|
-
${n.length} pending \xB7 approve with `)+
|
|
39
|
-
Last ${i.length} messages:`));for(let s of i){let a=
|
|
40
|
-
${c} \u2014 this crew is waiting on a person.`));let
|
|
41
|
-
${c}`;console.log(c==="COMPLETE"?
|
|
42
|
-
${s}`)}${a}`)}import{bold as
|
|
43
|
-
Dispatch with `)+
|
|
44
|
-
`));let n=await
|
|
45
|
-
Biggest fixable gap: `)+
|
|
46
|
-
Read cap reached \u2014 these are the most recent reviews${i?` (back to ${i})`:""}, not the whole window. Every count is a lower bound.`))}e.reviews&&e.skippedShare!=null&&e.skippedShare>0&&r(
|
|
47
|
-
${e.reviews.skipped} of ${e.reviews.total} reviews were skipped (${
|
|
48
|
-
${i}`))}import{bold as
|
|
49
|
-
Feedback reached the author in ${
|
|
34
|
+
`),process.exit(1)),process.stderr.write(nr(`\u2713 Applied.
|
|
35
|
+
`)),n.files>0&&process.stderr.write($t(" Run ")+So("git diff --stat")+$t(` to review.
|
|
36
|
+
`))}async function Ia(t){if(t)return await Ra(t,"utf8");let e=[];for await(let n of process.stdin)e.push(n);return Buffer.concat(e).toString("utf8")}function rr(t,e){return new Promise((n,r)=>{let o=Aa("git",t,{stdio:["pipe","pipe","pipe"]}),i="",s="";o.stdout.on("data",a=>{i+=a.toString()}),o.stderr.on("data",a=>{s+=a.toString()}),o.on("error",r),o.on("close",a=>n({code:a??0,stdout:i,stderr:s})),o.stdin.write(e),o.stdin.end()})}function _a(t){let e=0,n=0,r=0;for(let o of t.split(`
|
|
37
|
+
`))if(o.startsWith("diff --git"))e+=1;else{if(o.startsWith("+++")||o.startsWith("---"))continue;o.startsWith("+")?n+=1:o.startsWith("-")&&(r+=1)}return e===0&&(n>0||r>0)&&(e=(t.match(/^\+\+\+ /gm)??[]).length),{files:e,added:n,removed:r}}import{bold as Sa,dim as or,green as Ea,red as Ta}from"kleur/colors";async function To(t){let e=await S(t.creds,"GET",`/hil${H({limit:t.limit})}`);if(t.json){console.log(_(e));return}let n=se(e);if(n.length===0){console.log(or("No approvals pending."));return}console.log(D(n,[{header:"ID",value:r=>E(r,"id")},{header:"CREW",value:r=>E(r,"crewId"),max:24},{header:"AGE",value:r=>wo(r.requestedAt)},{header:"SUMMARY",value:r=>X(r,["planSummary","summary","title","description"]),max:60}])),console.log(or(`
|
|
38
|
+
${n.length} pending \xB7 approve with `)+Sa("anyforge approve <id>"))}async function ir(t){if(!t.approvalId)throw new y(`${t.decision==="APPROVED"?"approve":"reject"} requires an approval id.`,"List them with `anyforge approvals`.");let e=await S(t.creds,"POST",`/hil/${encodeURIComponent(t.approvalId)}`,{decision:t.decision,...t.notes?{notes:t.notes}:{}});if(t.json){console.log(_(e));return}let n=t.decision==="APPROVED"?Ea("Approved"):Ta("Rejected");console.log(`${n} ${t.approvalId}`);let r=e&&typeof e=="object"?e.signature:void 0;typeof r=="string"&&r&&console.log(or(`Audit signature: ${r}`))}import{bold as Ca,dim as sr}from"kleur/colors";async function Co(t){let e=await S(t.creds,"GET",`/crews${H({status:t.status,activeOnly:t.activeOnly,limit:t.limit})}`);if(t.json){console.log(_(e));return}let n=se(e);if(n.length===0){console.log(sr(t.activeOnly?"No active crews.":"No crews."));return}console.log(D(n,[{header:"ID",value:r=>E(r,"id")},{header:"STATUS",value:r=>E(r,"status")},{header:"TASK",value:r=>X(r,["task"],"\u2014"),max:52},{header:"NOTE",value:r=>X(r,["errorMessage"],""),max:40}]))}async function $o(t){if(!t.crewId)throw new y("crew requires a crew id.","List them with `anyforge crews`.");let e=await S(t.creds,"GET",`/crews/${encodeURIComponent(t.crewId)}${H({messages:t.messages})}`);if(t.json){console.log(_(e));return}let n=e?.crew&&typeof e.crew=="object"?e.crew:e;console.log(`${Ca($a(X(n,["taskDescription"],E(n,"id"))))}`),console.log(`Status: ${E(n,"status")}`),console.log(`Repo: ${X(n,["githubRepo"])}`);let r=E(n,"prUrl","");r&&console.log(`PR: ${r}`);let o=E(n,"url","");o&&console.log(`Console: ${o}`);let i=se(e?.chat);if(i.length>0){console.log(sr(`
|
|
39
|
+
Last ${i.length} messages:`));for(let s of i){let a=X(s,["agentRole","role","author","agent","type"],"?"),c=X(s,["content","body","text","summary"],"");c&&console.log(`${sr(`[${a}]`)} ${st(c.replace(/\s+/g," "),160)}`)}}}async function ar(t){if(!t.crewId)throw new y(`${t.action} requires a crew id.`,"List them with `anyforge crews`.");let e=await S(t.creds,"POST",`/crews/${encodeURIComponent(t.crewId)}/${t.action}`);if(t.json){console.log(_(e));return}console.log(`${t.action==="halt"?"Halted":"Resumed"} ${t.crewId}`)}function $a(t){return st(t.split(/\r?\n/).find(e=>e.trim())?.trim()??t,80)}import{bold as Pa,dim as at,red as Po,yellow as No}from"kleur/colors";var La=new Set(["COMPLETE","HALTED","REJECTED","CANCELLED"]),Na=new Set(["HIL_PENDING","HIL_SHIP","PERMISSION_PENDING","PAUSED"]),qa={HIL_PENDING:"Decide it with `anyforge approvals`, or from the Slack alert.",HIL_SHIP:"Decide it with `anyforge approvals`, or from the Slack alert.",PERMISSION_PENDING:"The agent asked for a tool outside its profile. Allow or deny it in the Console.",PAUSED:"Restart it with `anyforge resume <id>`."};async function qo(t){if(!t.workItemId)throw new y("crew start requires a work item id.","Find one with `anyforge work --status READY`.");let e=await S(t.creds,"PATCH",`/work/${encodeURIComponent(t.workItemId)}`,{action:"dispatch",...t.profileId?{runProfileId:t.profileId}:{}});if(t.json&&!t.follow){console.log(_(e));return}let n=typeof e?.crewId=="string"?e.crewId:"";if(!n)throw new f("Dispatch was accepted but returned no crew id.",m.FAILURE,"Check `anyforge crews --active` to see whether one started.");for(let r of e?.warnings??[])console.error(No(`warning: ${r}`));if(!t.follow){console.log(`Started ${Pa(n)} on ${t.workItemId}`),console.log(at(`Follow it with \`anyforge crew follow ${n}\``));return}await cr({creds:t.creds,json:t.json,crewId:n,intervalSeconds:t.intervalSeconds})}var Fa=5,Ua=10,Ma=120,Lo=30;async function cr(t){if(!t.crewId)throw new y("crew follow requires a crew id.","List them with `anyforge crews --active`.");let e=Math.min(Math.max(t.intervalSeconds??Ua,Fa),Ma)*1e3,n=Date.now()+Lo*6e4,r=new Set,o="",i=!1;for(;;){let s=await S(t.creds,"GET",`/crews/${encodeURIComponent(t.crewId)}?messages=50`),a=s?.crew??{},c=E(a,"status","");for(let d of s?.chat??[]){let p=E(d,"id","");!p||r.has(p)||(r.add(p),i&&!t.json&&ja(d))}if(i=!0,c&&c!==o&&(t.json||console.log(at(`\u2500\u2500 ${c}`)),o=c),Na.has(c)){if(t.json)console.log(_({crewId:t.crewId,status:c,waiting:!0,url:E(a,"url","")}));else{console.log(No(`
|
|
40
|
+
${c} \u2014 this crew is waiting on a person.`));let d=qa[c];d&&console.log(at(d));let p=E(a,"url","");p&&console.log(at(p))}return}if(La.has(c)){if(t.json)console.log(_({crewId:t.crewId,status:c,waiting:!1,url:E(a,"url","")}));else{let d=`
|
|
41
|
+
${c}`;console.log(c==="COMPLETE"?d:Po(d));let p=E(a,"prUrl","");p&&console.log(at(p));let l=E(a,"errorMessage","");l&&console.log(Po(l))}if(c!=="COMPLETE")throw new f(`Crew ${t.crewId} ended ${c}.`,m.FAILURE);return}if(Date.now()>=n)throw new f(`Still ${c||"running"} after ${Lo} minutes \u2014 giving up watching.`,m.FAILURE,`The crew is unaffected. Check it with \`anyforge crew ${t.crewId}\`.`);await Ba(e)}}function ja(t){let e=E(t,"agentRole","")||E(t,"type","?"),n=Da(t,["content","text","summary"]);n&&console.log(`${at(`[${e}]`)} ${st(n.replace(/\s+/g," "),160)}`)}function Da(t,e){for(let n of e){let r=t[n];if(typeof r=="string"&&r.trim())return r.trim()}return""}function Ba(t){return new Promise(e=>{setTimeout(e,t)})}import{dim as un}from"kleur/colors";var Uo=100;function Fo(t){let e=t&&typeof t=="object"?t:{};return{total:typeof e.total=="number"?e.total:null,hasMore:e.hasMore===!0,cursor:typeof e.nextCursor=="string"?e.nextCursor:null}}async function Wa(t){let e=s=>`/work${H({status:t.status,type:t.type,priority:t.priority,initiative:t.initiative,program:t.program,search:t.search,assignee:t.assignee,includeArchived:t.includeArchived,limit:t.limit,cursor:s})}`,n=await S(t.creds,"GET",e()),r=se(n),o=Fo(n),i=1;if(!t.all)return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i};for(;o.hasMore&&o.cursor&&i<Uo;){let s=await S(t.creds,"GET",e(o.cursor));r.push(...se(s)),o=Fo(s),i+=1}return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i}}async function Mo(t){let e=await Wa(t);if(t.json){console.log(_(t.all?{items:e.rows,total:e.total,hasMore:e.hasMore,nextCursor:null}:e.raw));return}let n=e.rows,r=await ho(t.creds),o=Qn(r,"work_item","plural","Work items");if(n.length===0){console.log(un(`No ${o.toLowerCase()} match that filter.`));return}console.log(D(n,[{header:"ID",value:c=>E(c,"id")},{header:"PRI",value:c=>E(c,"priority","")},{header:"TYPE",value:c=>E(c,"type","")},{header:"STATUS",value:c=>E(c,"status")},{header:"TITLE",value:c=>X(c,["title","name","summary"]),max:58}]));let i=(n.length===1?Qn(r,"work_item","singular","work item"):o).toLowerCase(),s=e.total!==null&&e.total!==n.length?`${n.length} of ${e.total} ${o.toLowerCase()}`:`${n.length} ${i}`,a=e.hasMore?t.all?un(` \u2014 stopped after ${Uo} pages; narrow the filter`):un(" \u2014 pass --all for the rest"):"";console.log(`${un(`
|
|
42
|
+
${s}`)}${a}`)}import{bold as Ga,dim as pn}from"kleur/colors";async function jo(t){let e=await S(t.creds,"GET","/workflows");if(t.json){console.log(_(e));return}let n=se(e);if(n.length===0){console.log(pn("No workflows defined."));return}console.log(D(n,[{header:"ID",value:r=>E(r,"id")},{header:"STATUS",value:r=>X(r,["status","state"],"")},{header:"NAME",value:r=>X(r,["name","title"]),max:58}])),console.log(pn(`
|
|
43
|
+
Dispatch with `)+Ga("anyforge run <id>"))}async function Do(t){if(!t.workflowId)throw new y("run requires a workflow id.","List them with `anyforge workflows`.");let e;if(t.input!==void 0){try{e=JSON.parse(t.input)}catch(o){throw new y(`--input is not valid JSON: ${o instanceof Error?o.message:String(o)}`,`Example: --input '{"repo":"acme/web"}'`)}if(!e||typeof e!="object"||Array.isArray(e))throw new y("--input must be a JSON object.",`Example: --input '{"repo":"acme/web"}'`)}let n=await S(t.creds,"POST",`/workflows/${encodeURIComponent(t.workflowId)}/run`,e===void 0?void 0:{inputs:e});if(t.json){console.log(_(n));return}let r=X(n??{},["id","runId"],"");console.log(`Queued run ${r||pn("(id not returned)")}`),console.log(pn("Runs pause at gate steps for an operator decision."))}import{dim as Ha}from"kleur/colors";async function Bo(t){let e=t.question?.trim();if(!e)throw new y("ask requires a question.",'Example: anyforge ask "which initiatives slipped this cycle?"');!t.json&&process.stderr.isTTY&&process.stderr.write(Ha(`Asking the Oracle (this can take a few minutes)\u2026
|
|
44
|
+
`));let n=await S(t.creds,"POST","/oracle",{question:e,...t.context?{context:t.context}:{}});if(t.json){console.log(_(n));return}let r=n&&typeof n=="object"?["answer","text","content","response"].map(o=>n[o]).find(o=>typeof o=="string"&&o.trim()):void 0;console.log(typeof r=="string"?r:_(n))}import{bold as lr,dim as Wo,green as Ka,yellow as za}from"kleur/colors";function Va(t){return typeof t=="number"?`${Math.round(t*100)}%`:"\u2014"}async function Go(t){let e=await S(t.creds,"GET",`/backtest-corpus${H({days:t.days})}`);if(t.json){console.log(_(e));return}let n=e.incidentsInWindow??0,r=e.backTestable??0,o=e.windowDays?`last ${e.windowDays}d`:"all time";console.log(`${lr(String(r))} of ${lr(String(n))} incidents are back-testable `+Wo(`(${Va(e.coverage)} \xB7 ${o})`)),e.viable?console.log(Ka("Enough to run a back-test.")):console.log(za(`Not yet viable \u2014 ${e.shortfall??0} more needed.`));let i=(e.excluded??[]).filter(s=>s.count>0);i.length>0&&console.log(D(i,[{header:"EXCLUDED",value:s=>String(s.count)},{header:"REASON",value:s=>s.label,max:56}])),e.biggestGap&&console.log(Wo(`
|
|
45
|
+
Biggest fixable gap: `)+lr(`${e.biggestGap.label} (${e.biggestGap.count})`))}import{bold as fn,dim as Pt,green as Ya,yellow as Ho}from"kleur/colors";var dr=t=>typeof t=="number"?`${Math.round(t*1e3)/10}%`:"\u2014";async function Ko(t){let e=await S(t.creds,"GET",`/review-interception${H({days:t.days})}`);if(t.json){console.log(_(e));return}let n=cn(e.windowDays,e.truncated,e.oldestReviewMs),r=i=>{console.log(i)};if(r(`${fn(String(e.seriousRaised??0))} serious findings raised across ${fn(String(e.prsReviewed??0))} pull requests ${Pt(`(${n})`)}`),e.verdict==="measured")r(`${Ya(String(e.disappearedSerious??0))} no longer raised by the next round \u2014 ${fn(dr(e.interceptionFloor))} to ${fn(dr(e.interceptionCeiling))}`),r(Pt(`floor counts every unmeasured finding as unresolved \xB7 ceiling is over the ${e.measurableSerious??0} that had a later round`));else{r(Ho("Not enough findings had a later round to measure a rate."));for(let i of e.inconclusiveReasons??[])r(Pt(` ${i}`))}let o=(e.byLens??[]).filter(i=>i.serious>0);if(o.length>0&&r(D(o,[{header:"LENS",value:i=>i.lens,max:16},{header:"RAISED",value:i=>String(i.raised)},{header:"SERIOUS",value:i=>String(i.serious)},{header:"CHECKED",value:i=>String(i.measurable)},{header:"GONE",value:i=>String(i.disappeared)}])),e.truncated){let i=e.oldestReviewMs?new Date(e.oldestReviewMs).toISOString().slice(0,10):null;r(Ho(`
|
|
46
|
+
Read cap reached \u2014 these are the most recent reviews${i?` (back to ${i})`:""}, not the whole window. Every count is a lower bound.`))}e.reviews&&e.skippedShare!=null&&e.skippedShare>0&&r(Pt(`
|
|
47
|
+
${e.reviews.skipped} of ${e.reviews.total} reviews were skipped (${dr(e.skippedShare)}) \u2014 drafts, size caps or path policy.`));for(let i of e.caveats??[])r(Pt(`
|
|
48
|
+
${i}`))}import{bold as Lt,dim as De,green as Ja,red as Qa,yellow as ur}from"kleur/colors";var Xa=t=>typeof t=="number"?`${Math.round(t*1e3)/10}%`:"\u2014";function pr(t){if(typeof t!="number")return"\u2014";if(t<60)return`${t}m`;if(t<1440){let n=Math.floor(t/60);return`${n}h ${Math.round(t-n*60)}m`}let e=Math.floor(t/1440);return`${e}d ${Math.round((t-e*1440)/60)}h`}var Za={capacity:"a limit fired",policy:"configured off",by_design:"correctly declined",unclassified:"reason not recognised"};async function zo(t){let e=await S(t.creds,"GET",`/review-coverage${H({days:t.days})}`);if(t.json){console.log(_(e));return}let n=s=>{console.log(s)},r=cn(e.windowDays,e.truncated,e.oldestRecordMs);n(`${Lt(String(e.pullRequestsReviewed??0))} of ${Lt(String(e.pullRequestsSeen??0))} recorded pull requests reviewed ${e.coverageRate!=null?Lt(`(${Xa(e.coverageRate)})`):""} ${De(`(${r})`)}`),e.repositoriesReviewed&&n(`across ${Lt(String(e.repositoriesReviewed))} repositories`),n(De("recorded = pull requests AnyForge was enabled to watch, not every pull request you opened")),e.medianLatencyMinutes!=null?(n(`
|
|
49
|
+
Feedback reached the author in ${Ja(pr(e.medianLatencyMinutes))} median, ${Lt(pr(e.p90LatencyMinutes))} at p90 ${De(`(${e.latencySamples??0} reviews)`)}`),e.medianOnOpenLatencyMinutes!=null&&n(De(` ${pr(e.medianOnOpenLatencyMinutes)} median on pull-request open (${e.onOpenSamples??0} reviews, no quiet period in the way)`))):n(ur(`
|
|
50
50
|
Not enough timed reviews to report a latency figure.`));let o=e.capacityGapPullRequests??0;o>0&&n(`
|
|
51
|
-
${
|
|
52
|
-
${s}`));if(e.truncated){let s=e.oldestRecordMs?new Date(e.oldestRecordMs).toISOString().slice(0,10):null;n(
|
|
53
|
-
Read cap reached \u2014 these are the most recent records${s?` (back to ${s})`:""}, not the whole window. Every count is a lower bound.`))}for(let s of e.caveats??[])n(
|
|
54
|
-
${s}`))}import{promises as
|
|
51
|
+
${Qa(String(o))} pull requests went unreviewed because a limit fired `+De("\u2014 a cap or a diff over the size ceiling, not a decision anyone made"));let i=e.bySkipClass??[];i.length>0&&n(D(i,[{header:"WHY UNREVIEWED",value:s=>Za[s.skipClass]??s.skipClass,max:24},{header:"PRS",value:s=>String(s.pullRequests)},{header:"REASONS",value:s=>s.reasons.join(", "),max:46}])),e.failedOnlyPullRequests&&n(De(`${e.failedOnlyPullRequests} pull requests had every review attempt error out.`));for(let s of e.inconclusiveReasons??[])n(ur(`
|
|
52
|
+
${s}`));if(e.truncated){let s=e.oldestRecordMs?new Date(e.oldestRecordMs).toISOString().slice(0,10):null;n(ur(`
|
|
53
|
+
Read cap reached \u2014 these are the most recent records${s?` (back to ${s})`:""}, not the whole window. Every count is a lower bound.`))}for(let s of e.caveats??[])n(De(`
|
|
54
|
+
${s}`))}import{createHash as sc}from"node:crypto";import{dim as Zo,green as ac,red as cc,yellow as lc}from"kleur/colors";function Yo(t){return ec({eventType:t.eventType,actor:t.actor,payload:t.payload,timestamp:t.timestampMs})}function ec(t){return JSON.stringify(fr(t))}function fr(t){if(Array.isArray(t))return t.map(fr);if(t===null||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)return t;let e=t,n={};for(let r of Object.keys(e).sort())n[r]=fr(e[r]);return n}var tc="";function Jo(t,e,n){return t(`${n??""}${tc}${e}`)}function nc(t){let e=t.findIndex(n=>(n.sealVersion??1)>=2);return e===-1?t.length:e}function Qo(t,e){let n=nc(t),r=n;for(let o=n;o<t.length;o++){let i=t[o];if(typeof i.hash!="string"||!i.hash)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"missing_hash",detail:`Row ${i.id} carries seal version ${i.sealVersion??1} but no hash.`};if(o>n){let s=t[o-1].hash??null;if((i.previousHash??null)!==s)return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"link_mismatch",detail:`Row ${i.id} links to ${Vo(i.previousHash)}, but the prior row hashes to ${Vo(s)}.`}}if(i.hash!==e(i))return{valid:!1,verified:o-n,unverifiable:r,brokenAt:o,brokenId:i.id,reason:"content_mismatch",detail:`Row ${i.id} does not match its own contents.`}}return{valid:!0,verified:t.length-n,unverifiable:r}}function Vo(t){return t?`${t.slice(0,12)}\u2026`:"nothing"}function oc(t){return t?JSON.parse(JSON.stringify(t)):{}}function ic(t){return{id:t.id,eventType:t.eventType,actor:t.actor,payload:oc(t.payload),timestampMs:t.timestampMs,hash:t.hash,previousHash:t.prevHash,...typeof t.sealVersion=="number"?{sealVersion:t.sealVersion}:{}}}function Xo(t,e,n={}){let r=t.map(ic),o=t.length>0?t[t.length-1].hash??null:null,i=n.anchorHash;return i&&r.length>0&&(r[0].sealVersion??1)>=2&&(r[0].previousHash??null)!==i?{valid:!1,verified:0,unverifiable:0,brokenAt:0,brokenId:r[0].id,reason:"link_mismatch",detail:`Entry ${r[0].id} does not link onto the previous page's last hash.`,lastHash:o}:{...Qo(r,a=>Jo(e,Yo({eventType:a.eventType,actor:a.actor,payload:a.payload,timestampMs:a.timestampMs}),a.previousHash??null)),lastHash:o}}var dc=200,uc=t=>sc("sha256").update(t).digest("hex");function pc(t,e=uc){let n=null,r=0,o=0;for(let i of t){if(i.length===0)continue;let s=Xo(i,e,{anchorHash:n});if(r+=s.verified,o+=s.unverifiable,!s.valid)return{valid:!1,verified:r,unverifiable:o,brokenId:s.brokenId,reason:s.reason,detail:s.detail};n=s.lastHash}return{valid:!0,verified:r,unverifiable:o}}async function ei(t){if(t.verify&&(t.eventType||t.actor))throw new y("--verify needs an unfiltered read.","Drop --event and --actor: a filtered page skips rows, so its chain links cannot meet. --from and --to are fine.");let e=c=>`/audit${H({eventType:t.eventType,actor:t.actor,from:t.from,to:t.to,limit:t.limit,cursor:c})}`,n=[],r=t.cursor;do{let c=await S(t.creds,"GET",e(r));n.push(c),r=c.hasMore&&c.nextCursor?c.nextCursor:void 0}while(t.all&&r&&n.length<dc);let o=n.flatMap(c=>c.entries),i=n[n.length-1],s=t.verify?pc(n.map(c=>c.entries)):null,a=c=>{console.log(c)};if(t.json)a(_(n.length===1&&!s?i:{entries:o,nextCursor:i.nextCursor,hasMore:i.hasMore,chainContiguous:i.chainContiguous,pages:n.length,...s?{verification:s}:{}}));else{o.length===0?a("No audit entries match."):a(D(o,[{header:"SEQUENCE",value:d=>String(d.sequence)},{header:"TIME",value:d=>d.timestamp},{header:"EVENT",value:d=>d.eventType,max:34},{header:"ACTOR",value:d=>d.actor,max:28},{header:"HASH",value:d=>d.hash?`${d.hash.slice(0,12)}\u2026`:"\u2014"}]));let c=n.length===1?"page":"pages";a(Zo(`${o.length} entries from ${n.length} ${c}.`)),i.hasMore&&i.nextCursor&&a(lc(`More entries exist. Continue with --cursor ${i.nextCursor}${t.all?"":" or pass --all"}.`)),s&&(s.valid?a(ac(`Chain verified: ${s.verified} sealed entries link and recompute.`)+(s.unverifiable?Zo(` ${s.unverifiable} earlier entries predate sealing and cannot be verified.`):"")):a(cc(`Chain break at ${s.brokenId}: ${s.reason}.`)+(s.detail?` ${s.detail}`:"")))}if(s&&!s.valid)throw new f(`Audit chain verification failed at ${s.brokenId??"an entry"} (${s.reason??"unknown"}).`,m.VERIFICATION_FAILED)}import{dim as ti}from"kleur/colors";var fc=["invite","invitations","withdraw"];function ni(t,e){if(!t.email)throw new y(`people ${e} requires an email address.`,`Example: anyforge people ${e} person@example.com`);return t.email}function mc(t){if(t===void 0)return;let e=t.split(",").map(n=>n.trim()).filter(Boolean);return e.length?e:void 0}function Be(t){console.log(t)}async function ri(t){switch(t.subcommand){case"invite":{let e=ni(t,"invite");if(!t.role)throw new y("people invite requires --role.","One of operator, editor, viewer or filer.");let n=mc(t.team),r=await S(t.creds,"POST","/people/invitations",{email:e,role:t.role,...n?{teamIds:n}:{},...t.notify?{}:{notify:!1}});if(t.json){Be(_(r));return}let o=r.created?"Invited":"Refreshed the pending invitation for",i=r.emailSent?"Invitation email sent.":"No invitation email sent.";Be(`${o} ${r.email} as ${r.role}. ${ti(i)}`);return}case"invitations":{let e=await S(t.creds,"GET","/people/invitations");if(t.json){Be(_(e));return}let n=se(e);if(n.length===0){Be(ti("No invitations."));return}Be(D(n,[{header:"EMAIL",value:r=>E(r,"email"),max:48},{header:"ROLE",value:r=>E(r,"role")},{header:"STATUS",value:r=>E(r,"status")},{header:"TEAMS",value:r=>Array.isArray(r.teamIds)&&r.teamIds.length?r.teamIds.join(","):"\u2014",max:32}]));return}case"withdraw":{let e=ni(t,"withdraw"),n=await S(t.creds,"DELETE",`/people/invitations${H({email:e})}`);if(t.json){Be(_(n));return}Be(`Withdrew the invitation for ${n.email}.`);return}default:throw new y(t.subcommand?`Unknown people command: ${t.subcommand}`:"people needs a command.",`One of: ${fc.join(", ")}.`)}}import{promises as Nt}from"node:fs";import{homedir as wc}from"node:os";import{dirname as ii,join as We}from"node:path";import{dim as si,green as vc,yellow as bc}from"kleur/colors";var me="anyforge-platform",gc="Orient an agent to AnyForge's structure, delivery model, surfaces, live control tools, and safety boundaries.",oi="anyforge://guide/platform",hc="text/markdown";var mn={"SKILL.md":`---
|
|
55
55
|
name: anyforge-platform
|
|
56
56
|
description: >
|
|
57
57
|
Orient an agent to AnyForge's structure, delivery model, surfaces, live control
|
|
@@ -219,7 +219,7 @@ allow it.
|
|
|
219
219
|
- **Skills** are instruction text. Company skills, bundled platform skills,
|
|
220
220
|
and repository skills are different sources with different trust and scope;
|
|
221
221
|
none of them grants authority.
|
|
222
|
-
`,"references/tool-map.md":
|
|
222
|
+
`,"references/tool-map.md":'# AnyForge MCP tool map\n\nTool names can grow as the platform grows. Use `tools/list` and the current\ntool description as the contract. The names below are representative entry\npoints in the public MCP server.\n\n## Discover and inspect\n\n- `anyforge_list_repo_index` and `anyforge_get_repo_analysis` discover connected\n repositories and their analysis.\n- `anyforge_list_programs` and `anyforge_get_program` inspect program records.\n- `anyforge_list_initiatives` and `anyforge_get_initiative` inspect scoped work.\n- `anyforge_list_work_items` and `anyforge_get_work_item` inspect delivery\n units.\n- `anyforge_list_crews` and `anyforge_get_crew` inspect Crew runs and output.\n- `anyforge_list_pending_approvals` and `anyforge_get_approval_detail` inspect\n human gates.\n- `anyforge_ask_oracle` asks a read-oriented cross-workspace question. A long investigation returns `running` with an `askId`; collect it with `anyforge_get_oracle_answer` rather than asking again. A turn that runs out of time is continued server-side under the same `askId` (`maxContinuations`, default 2), so a long answer arrives whole without a "continue" ask.\n\n### Find a work item by code\n\nFor a known full code, call `anyforge_list_work_items` with `itemId`:\n\n```json\n{ "itemId": "WBA-004", "limit": 5 }\n```\n\nUse the returned item\'s `id` with `anyforge_get_work_item` for its full details.\nFor a partial code or title, use `search` instead, for example\n`{ "search": "WBA-", "limit": 20 }`. Search is case-insensitive and matches\nsubstrings, so check the returned code and title before selecting a record.\nArchived items are excluded by default, including code lookups; add\n`"includeArchived": true` when looking for archived work. Check `hasMore` and\nfollow `nextCursor` with unchanged filters when there are more results.\n\n## Plan and write\n\n- `anyforge_create_program` and `anyforge_update_program` manage program\n records.\n- `anyforge_create_initiative`, `anyforge_update_initiative`, and\n `anyforge_dispatch_architect` manage initiative planning and dispatch.\n- `anyforge_create_work_item` and `anyforge_update_work_item` manage concrete\n delivery units.\n- Program Genesis also exposes tools for the structured brief and reconciling\n program delivery. Read their schemas before use.\n\n## Approve and control\n\n- `anyforge_approve_backlog` and `anyforge_approve_task_proposal` advance\n approval-gated planning.\n- `anyforge_submit_approval` records a human decision when the caller has the\n required approval scope.\n- `anyforge_resume_crew`, `anyforge_halt_crew`,\n `anyforge_message_crew`, and `anyforge_amend_crew` control a Crew run.\n Explain the effect before calling them.\n\n## Selection rules\n\n1. Prefer the narrowest read tool that answers the question.\n2. Use a detail tool after a list result when a decision depends on fields not\n present in the list.\n3. Filter and page large lists. Do not request an unbounded workspace dump.\n4. Treat names and descriptions as hints; use the tool schema for required\n fields and allowed values.\n5. Before a mutation, restate the target, change, and likely side effect.\n6. After a mutation, read the target again and report the resulting state.\n',"references/operating-boundaries.md":`# AnyForge operating boundaries
|
|
223
223
|
|
|
224
224
|
## Identity and scope
|
|
225
225
|
|
|
@@ -258,72 +258,125 @@ these sources can grant network, filesystem, tool, or approval authority.
|
|
|
258
258
|
Do not place API keys, tokens, credentials, private tenant records, or customer
|
|
259
259
|
data in a reusable skill, prompt, log, or generated example. Use the client's
|
|
260
260
|
credential mechanism and let the server enforce access.
|
|
261
|
-
`},
|
|
262
|
-
`);return}console.log(Ea("Installed ")+`${r.name} ${Fo(`(${r.scope}, ${r.client})`)}`),console.log(` ${r.directory}`),console.log(Fo(` ${r.files.length} files installed. Re-run with --force to refresh them.`)),r.scope==="project"&&r.client==="agents"&&console.log(Ta(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as nl}from"node:crypto";import{open as rl,realpath as ol,stat as il}from"node:fs/promises";import{basename as sl,join as al,resolve as cl}from"node:path";import{bold as Er,cyan as Hi,dim as be,yellow as xn}from"kleur/colors";import{ProcessTerminal as ll,SelectList as dl,Text as ul,TuiAltScreen as pl}from"@earendil-works/pi-tui";var Pa=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function La(t){return Pa.has(t)?"querying the code graph":t==="search_code_semantic"?"searching the code index":t==="search_codebase_docs"?"searching codebase docs":null}function sn(t){let e=La(t);if(e)return e;let n=/^mcp__(.+?)__(.+)$/.exec(t);return n?`${n[1].replace(/[_-]+/g," ")} \xB7 ${n[2].replace(/_/g," ")}`:t.replace(/_/g," ")}var Na=new Set(["ask_user","present_catch_up_decision","present_memory_candidates"]);function tt(t){return typeof t=="string"&&Na.has(t)}import{truncateToWidth as Lt,wrapTextWithAnsi as ui}from"@earendil-works/pi-tui";import{cyan as pr,dim as Be,green as pi,italic as fc,red as fr,yellow as mr}from"kleur/colors";import{truncateToWidth as xt,visibleWidth as Zn,wrapTextWithAnsi as Fa}from"@earendil-works/pi-tui";import{bold as qa,dim as ja}from"kleur/colors";function D(t){return t<1024?`${t} B`:t<1024*1024?`${St(t/1024)} KiB`:`${St(t/(1024*1024))} MiB`}function Et(t){let e=Math.max(0,t);if(e<1e4)return`${St(e/1e3)}s`;let n=Math.round(e/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/3600),o=Math.floor(n%3600/60),i=n%60;return r?o?`${r}h ${o}m`:`${r}h`:i?`${o}m ${i}s`:`${o}m`}function er(t){let e=Math.floor(Math.max(0,t)/1e3);return e<60?`${e}s`:`${Math.floor(e/60)}m ${String(e%60).padStart(2,"0")}s`}function ie(t){return Math.abs(t)<1e3?String(Math.round(t)):Math.abs(t)<1e6?`${St(t/1e3,t<1e5?1:0)}k`:`${St(t/1e6)}M`}function St(t,e=1){return t.toFixed(e).replace(/\.0+$/,"")}function Ma(t){return l(t).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function Ua(t,e){return typeof e=="boolean"?e?"on":"off":typeof e=="string"?l(e):/Ms$/.test(t)?Et(e):/Bytes$/.test(t)?D(e):/tokens?/i.test(t)?ie(e):String(e)}function Mo(t){return t?Object.entries(t).filter(e=>e[1]!==null).map(([e,n])=>`${Ma(e)} ${Ua(e,n)}`):[]}function tr(t,e,n=2){let r=Math.min(16,Math.max(...t.map(([s])=>Zn(s)),0)+2),o=Math.max(10,e-n-r),i=[];for(let[s,a]of t)Fa(a,o).forEach((u,d)=>{let p=d===0?ja(xt(s,r-1).padEnd(r)):" ".repeat(r);i.push(xt(`${" ".repeat(n)}${p}${u}`,e))});return i}function Uo(t,e,n){return[xt(qa(t),n),...tr(e,n)]}function Do(t,e,n){let r=Zn(n),o=e.filter(Boolean).join(" \xB7 ");if(r+4>t)return xt(o||n,t);let i=t-r-2,s=xt(o,i),a=Math.max(2,t-Zn(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as rt,cyan as nt,dim as Ee,green as Da,italic as Bo,strikethrough as Ba,underline as Wo,yellow as Wa}from"kleur/colors";import{Markdown as Ga}from"@earendil-works/pi-tui";var Ka={heading:t=>rt(nt(t)),link:t=>Wo(nt(t)),linkUrl:t=>Ee(t),code:t=>Wa(t),codeBlock:t=>t,codeBlockBorder:t=>Ee(t),quote:t=>Bo(t),quoteBorder:t=>Ee(t),hr:t=>Ee(t),listBullet:t=>nt(t),bold:t=>rt(t),italic:t=>Bo(t),strikethrough:t=>Ba(t),underline:t=>Wo(t)},it={selectedPrefix:t=>nt(t),selectedText:t=>rt(t),description:t=>Ee(t),scrollInfo:t=>Ee(t),noMatch:t=>Ee(t)},Go={borderColor:t=>Ee(t),selectList:it};var ot=class{constructor(e,n){this.linkOrigin=n;this.content=l(e),this.markdown=new Ga(Un(this.content,n),0,0,Ka)}content;markdown;cache;getText(){return this.content}setText(e){let n=l(e);n!==this.content&&(this.content=n,this.markdown.setText(Un(n,this.linkOrigin)),this.cache=void 0)}invalidate(){this.markdown.invalidate(),this.cache=void 0}render(e){let n=Math.max(12,e);if(this.cache?.width===n)return this.cache.lines;let r=this.content?this.markdown.render(n).map(co):[];return this.cache={width:n,lines:r},r}};function Tt(t,e){return t==="user"?`${rt(Da(e?l(e):"You"))}`:`${rt(nt("Oracle"))}`}function Ko(t,e,n){let r=nt("\u2502"),o=Math.max(1,e-4);return["",`${r} ${Tt("user",n)}`,...t.render(o).map(i=>`${r} ${rt(i)}`),""]}function he(t){return t.shared||t.visibility==="company"?`Shared with the company${t.entityKey?` \xB7 ${l(t.entityKey)}`:""}`:"Private to you"}function zo(t){return t.shared||t.visibility==="company"?"shared":"private"}import{createReadStream as sc}from"node:fs";import{stat as ac}from"node:fs/promises";import{basename as cc}from"node:path";import{setTimeout as ai}from"node:timers/promises";var Ho={txt:"text/plain",log:"text/plain",md:"text/markdown",markdown:"text/markdown",csv:"text/csv",json:"application/json",pdf:"application/pdf",ts:"text/plain",tsx:"text/plain",js:"text/plain",jsx:"text/plain",mjs:"text/plain",cjs:"text/plain",py:"text/plain",go:"text/plain",rs:"text/plain",yaml:"text/plain",yml:"text/plain",toml:"text/plain",xml:"text/plain",html:"text/plain",css:"text/plain",sh:"text/plain",sql:"text/plain",java:"text/plain",kt:"text/plain",swift:"text/plain",c:"text/plain",h:"text/plain",cpp:"text/plain",rb:"text/plain",php:"text/plain",tf:"text/plain",diff:"text/plain",patch:"text/plain",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"};function Yo(t){let e=t.slice(t.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(Ho,e)?Ho[e]:null}var nr="/api/v1/oracle";var Jo="workspace";function Te(t){let e;try{e=new URL(t)}catch{throw new f(`Invalid Oracle endpoint: ${t}`,m.USAGE)}if(!["http:","https:"].includes(e.protocol)||e.username||e.password)throw new f("Oracle endpoint must be an http or https URL without embedded credentials.",m.USAGE);if(e.pathname!=="/"||e.search||e.hash)throw new f("Oracle endpoint must be the workspace origin, without a path, query or fragment.",m.USAGE);return e.origin}function we(t,e){return`${Te(t)}${nr}${e}`}function za(t,e,n){let r;try{r=new URL(t)}catch{throw new f(`Oracle server returned an invalid ${n} endpoint.`,m.API_ERROR)}let o=new URL(Te(e));if(r.origin!==o.origin||!r.pathname.startsWith("/api/oauth/")||r.search||r.hash||r.username||r.password)throw new f(`Oracle server returned an unsafe ${n} endpoint.`,m.API_ERROR);return r.toString()}function Ha(t,e){if(!t||typeof t!="object")throw new f("Oracle server omitted OAuth endpoints.",m.API_ERROR);let n=t,r=["registration","authorization","token","revocation"],o={};for(let i of r){if(typeof n[i]!="string")throw new f(`Oracle server omitted its OAuth ${i} endpoint.`,m.API_ERROR);o[i]=za(n[i],e,i)}return o}async function qe(t,e=fetch,n){let r=Te(t),o;try{o=await e(we(r,"/config"),{headers:{Accept:"application/json",...n?{Authorization:`Bearer ${n}`}:{}},redirect:"error",signal:AbortSignal.timeout(2e4)})}catch(d){throw new f(`Cannot reach ${r}: ${d instanceof Error?d.message:String(d)}`,m.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}let i=await ct(o,we(r,"/config")),s=L(at(i,o.status)),a=`${r}${nr}`;if(s.protocolVersion!==1)throw new f(`Oracle protocol mismatch: this CLI supports ${1}, and the server reports ${String(s.protocolVersion)}.`,m.API_ERROR,"Update @anyforge/control to a version supported by this workspace.");if(s.resource!==a)throw new f("Oracle server resource does not match this workspace origin.",m.API_ERROR);let c=Array.isArray(s.scopes)?s.scopes:null;if(!c||!c.every(d=>d==="read"||d==="write"||d==="approve"))throw new f("Oracle server returned an invalid scope list.",m.API_ERROR);let u={protocolVersion:1,resource:a,scopes:c,oauth:Ha(s.oauth,r)};if(s.identity!==void 0){let d=L(s.identity);if(typeof d.uid!="string"||typeof d.companyId!="string")throw new f("Oracle server returned an invalid identity.",m.API_ERROR);let p=typeof d.companyName=="string"?l(d.companyName).trim().slice(0,120):"";u.identity={uid:l(d.uid),companyId:l(d.companyId),...p?{companyName:p}:{}}}if(s.capabilities!==void 0){let d=L(s.capabilities);if(![d.read,d.write,d.approve].every(p=>typeof p=="boolean"))throw new f("Oracle server returned invalid capabilities.",m.API_ERROR);u.capabilities={read:d.read,write:d.write,approve:d.approve}}if(s.model!==void 0){let d=L(s.model);if(typeof d.id!="string"||typeof d.provider!="string")throw new f("Oracle server returned invalid model metadata.",m.API_ERROR);u.model={id:l(d.id),provider:l(d.provider)}}if(s.limits!==void 0){let d=L(s.limits);if(Object.values(d).some(p=>!["string","number","boolean"].includes(typeof p)&&p!==null))throw new f("Oracle server returned invalid limits metadata.",m.API_ERROR);u.limits=Object.fromEntries(Object.entries(d).map(([p,R])=>[l(p),typeof R=="string"?l(R):R]))}return Array.isArray(s.commands)&&(u.commands=Ya(s.commands)),u}function Ya(t){let e=[];for(let n of t.slice(0,50)){let r=L(n);if(typeof r.name!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(r.name)||typeof r.description!="string"||e.some(i=>i.name===r.name))continue;let o=typeof r.args=="string"?l(r.args).slice(0,60).trim():"";e.push({name:r.name,description:l(r.description).slice(0,200),...o?{args:o}:{}})}return e}var je=class{endpoint;scopes;source;#e;#n;#t;cachedConfig;constructor(e,n=fetch){this.endpoint=Te(e.endpoint),this.#e=e.getAccessToken??(async()=>e.accessToken),this.#n=e.onIdentity,this.#t=n,this.scopes=new Set(e.scopes),this.source=e.source}async config(){if(!this.cachedConfig){let e=await this.#e();try{this.cachedConfig=await qe(this.endpoint,this.#t,e)}catch(r){if(this.source!=="oauth"||!(r instanceof Ie)||r.status!==401)throw r;let o=await this.#e(e);if(o===e)throw r;this.cachedConfig=await qe(this.endpoint,this.#t,o)}let n=this.cachedConfig.identity;n&&this.#n&&await this.#n(n).catch(()=>{})}return this.cachedConfig}async listSessions(){let e=await this.json("GET","/sessions"),n=Array.isArray(e)?e:L(e).sessions;if(!Array.isArray(n))throw new f("Oracle server returned an invalid sessions list.",m.API_ERROR);return n.map(Xo)}async createSession(e={}){let n=await this.json("POST","/sessions",e),r=L(n);return!Array.isArray(r.messages)&&typeof r.sessionId=="string"?this.getSession(r.sessionId):Qo(n)}async getSession(e){return Qo(await this.json("GET",`/sessions/${encodeURIComponent(e)}`))}async submitTurn(e,n,r,o){let i,s=we(this.endpoint,`/sessions/${encodeURIComponent(e)}/turns`),a=async u=>this.#t(s,{method:"POST",headers:{Authorization:`Bearer ${u}`,Accept:"text/event-stream, application/json","Content-Type":"application/json"},redirect:"error",body:JSON.stringify(n),...r?{signal:r}:{}});try{let u=await this.#e();if(i=await a(u),i.status===401&&this.source==="oauth"){let d=await this.#e(u);d!==u&&(i=await a(d))}}catch(u){throw u instanceof f||r?.aborted?u:new f(`Oracle connection was interrupted: ${u instanceof Error?u.message:String(u)}`,m.FAILURE,"The server may still be working. Resume this session to replay its saved stream.")}if(o?.(i),i.ok||await st(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let u=await ct(i,we(this.endpoint,`/sessions/${e}/turns`)),d=at(u,i.status);throw new f(`Oracle server did not start a stream (${Ja(d)}).`,m.API_ERROR)}return i}async replayEvents(e,n,r){let o=[];for await(let i of this.streamReplayEvents(e,n,r))o.push(i);return o}async*streamReplayEvents(e,n,r,o){let i=`/sessions/${encodeURIComponent(e)}/events?streamId=${encodeURIComponent(n)}&from=${Math.max(0,r)}`,s=await this.authenticatedFetch("GET",i,{headers:{Accept:"text/event-stream, application/json"}},0);if(s.ok||await st(s),(s.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Ct(s,o);return}let c=at(await ct(s,we(this.endpoint,i)),s.status),u=Array.isArray(c)?c:L(c).events;if(!Array.isArray(u))throw new f("Oracle replay response did not contain events.",m.API_ERROR);for(let d of u)yield rr(d)}async*streamRequestEvents(e,n,r=-1,o){let i=`/sessions/${encodeURIComponent(e)}/events?requestId=${encodeURIComponent(n)}&from=${r}`,s=await this.authenticatedFetch("GET",i,{headers:{Accept:"text/event-stream, application/json"}},0);if(s.ok||await st(s),(s.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Ct(s,o);return}let c=at(await ct(s,we(this.endpoint,i)),s.status),u=Array.isArray(c)?c:L(c).events;if(!Array.isArray(u))throw new f("Oracle request replay response did not contain events.",m.API_ERROR);for(let d of u)yield rr(d)}async cancelTurn(e,n){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{streamId:n})}async cancelRequest(e,n){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{requestId:n})}async upload(e,n){let r=new FormData;r.set("sessionId",e),r.set("file",n,n.name);let o=await this.authenticatedFetch("POST","/uploads",{method:"POST",body:r,headers:{Accept:"application/json"}});return o.ok||await st(o),Qa(at(await ct(o,we(this.endpoint,"/uploads")),o.status))}async download(e,n){let r=await this.authenticatedFetch("GET",`/sessions/${encodeURIComponent(e)}/files/${encodeURIComponent(n)}`,{headers:{Accept:"application/octet-stream, application/json"}});return r.ok||await st(r),r}async json(e,n,r){let o=await this.authenticatedFetch(e,n,{headers:{Accept:"application/json",...r!==void 0?{"Content-Type":"application/json"}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}});return o.ok||await st(o),at(await ct(o,we(this.endpoint,n)),o.status)}async authenticatedFetch(e,n,r={},o=2e4){try{let i=we(this.endpoint,n),s=Object.fromEntries(new Headers(r.headers).entries()),a=r.signal||o<=0?void 0:AbortSignal.timeout(o),c=R=>this.#t(i,{...r,method:e,headers:{...s,Authorization:`Bearer ${R}`},redirect:"error",...a?{signal:a}:{}}),u=await this.#e(),d=await c(u),p=r.body===void 0||typeof r.body=="string"||r.body===null;if(d.status===401&&this.source==="oauth"){let R=await this.#e(u);R!==u&&p&&(d=await c(R))}return d}catch(i){throw i instanceof f?i:new f(`Cannot reach ${this.endpoint}: ${i instanceof Error?i.message:String(i)}`,m.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}}};async function*Ct(t,e){if(!t.body)throw new f("Oracle stream has no response body.",m.FAILURE);let n=t.body.getReader(),r=new TextDecoder,o="",i=0,s=[],a=0,c=-1,u=()=>{if(s.length===0)return null;let d=s.join(`
|
|
263
|
-
`);if(s=[],a=0,
|
|
264
|
-
`,x);for(;
|
|
265
|
-
`,x)}if(o=o.slice(x),R)break}if(o.trim()){let p=o.replace(/\r$/,"");if(p.startsWith("data:")&&(s.push(p.slice(5).replace(/^ /,"")),a+=an(s[s.length-1]??"")+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR)}let d=u();d&&(yield d)}catch(d){throw await n.cancel("Invalid or oversized Oracle event stream.").catch(()=>{}),d}finally{n.releaseLock()}}function an(t){return Buffer.byteLength(t,"utf8")}function or(t){let e=["program","initiative","codebase"].filter(o=>typeof t[o]=="string");if(e.length>1)throw new f("Choose only one of --program, --initiative or --codebase.",m.USAGE);let n=e[0];if(!n)return;let r=t[n];if(typeof r!="string"||!r.trim())throw new f(`--${n} needs an id.`,m.USAGE);return{kind:n,id:r.trim()}}function L(t){return!t||typeof t!="object"||Array.isArray(t)?{}:t}function Ja(t){if(typeof t=="string")return l(t);try{return l(JSON.stringify(t))}catch{return"unexpected response"}}function at(t,e){let n=L(t);if(n.ok===!1){let r=Vo(n.error,e),o=typeof n.message=="string"?l(n.message).slice(0,2e3):void 0;throw new Ie(r,e,o)}return n.ok===!0&&"data"in n?n.data:t}async function ct(t,e){try{return await t.json()}catch{throw new f(`Non-JSON response from ${e} (HTTP ${t.status}).`,m.FAILURE)}}async function st(t){let e;try{e=await t.clone().json()}catch{e=null}let n=L(e),r=Vo(n.error??n.error_description,t.status),o=typeof n.message=="string"?n.message:typeof n.error_description=="string"?n.error_description:typeof n.detail=="string"?n.detail:void 0;throw new Ie(r,t.status,typeof o=="string"?l(o).slice(0,2e3):void 0)}function Vo(t,e){if(typeof t!="string")return`http_${e}`;let n=l(t).slice(0,120);return/^[A-Za-z0-9_.-]+$/.test(n)?n:`http_${e}`}function Xo(t){let e=L(t);if(typeof e.sessionId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(e.sessionId)||typeof e.title!="string"||e.visibility!=="private"&&e.visibility!=="company"||typeof e.shared!="boolean")throw new f("Oracle server returned an invalid session summary.",m.API_ERROR);return{sessionId:e.sessionId,title:l(e.title),visibility:e.visibility,shared:e.shared,...typeof e.entityKey=="string"?{entityKey:l(e.entityKey)}:{},...typeof e.updatedAt=="number"||e.updatedAt===null?{updatedAt:e.updatedAt}:{}}}function Qo(t){let e=L(t),n=Xo(e);if(!Array.isArray(e.messages))throw new f("Oracle server returned an invalid session transcript.",m.API_ERROR);let r=e.messages.map((o,i)=>{let s=L(o);if(s.role!=="user"&&s.role!=="assistant"||typeof s.content!="string")throw new f(`Oracle session message ${i+1} is invalid.`,m.API_ERROR);let a=Array.isArray(s.attachments)?s.attachments.map(ec):void 0,c=Array.isArray(s.files)?s.files.map(tc):void 0;return{role:s.role,content:l(s.content),...typeof s.id=="string"?{id:s.id}:{},...typeof s.authorName=="string"?{authorName:l(s.authorName)}:{},...s.question&&typeof s.question=="object"?{question:Va(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:Xa(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof e.activeStreamId=="string"?{activeStreamId:e.activeStreamId}:{}}}function rr(t){let e=L(t);if(e.protocolVersion!==1||typeof e.idx!="number"||!Number.isSafeInteger(e.idx)||e.idx<0||typeof e.type!="string")throw new f("Oracle stream event does not match protocol version 1.",m.API_ERROR);return ge(e)}function Qa(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle upload response is missing file metadata.",m.API_ERROR);return{fileId:e.fileId,name:l(e.name),mediaType:l(e.mediaType),...typeof e.sizeBytes=="number"?{sizeBytes:e.sizeBytes}:{}}}function Va(t){let e=L(t);if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",m.API_ERROR);return{id:l(e.id),questions:e.questions.map((n,r)=>{let o=L(n);if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,m.API_ERROR);return{id:l(o.id),header:l(o.header),question:l(o.question),multiSelect:o.multiSelect,options:o.options.map((i,s)=>{let a=L(i);if(typeof a.id!="string"||typeof a.label!="string"||typeof a.description!="string")throw new f(`Oracle question option ${s+1} is invalid.`,m.API_ERROR);return{id:l(a.id),label:l(a.label),description:l(a.description),...typeof a.recommended=="boolean"?{recommended:a.recommended}:{},...typeof a.confirms=="boolean"?{confirms:a.confirms}:{}}})}}),optional:e.optional===!0}}function Xa(t){let e=L(t);if(typeof e.questionId!="string"||!Array.isArray(e.answers))throw new f("Oracle returned an invalid question response.",m.API_ERROR);return{questionId:l(e.questionId),answers:e.answers.map(Za)}}function Za(t){let e=L(t);if(typeof e.questionId!="string"||!Array.isArray(e.optionIds)||!e.optionIds.every(n=>typeof n=="string"))throw new f("Oracle returned an invalid question answer.",m.API_ERROR);return{questionId:l(e.questionId),optionIds:e.optionIds.map(n=>l(n)),...typeof e.customText=="string"?{customText:l(e.customText).slice(0,8e3)}:{}}}function ec(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle session attachment metadata is invalid.",m.API_ERROR);return{fileId:l(e.fileId),name:l(e.name),mediaType:l(e.mediaType),...typeof e.sizeBytes=="number"&&Number.isFinite(e.sizeBytes)?{sizeBytes:e.sizeBytes}:{}}}function tc(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.filename!="string"||typeof e.mediaType!="string"||typeof e.sizeBytes!="number"||!Number.isFinite(e.sizeBytes))throw new f("Oracle session file metadata is invalid.",m.API_ERROR);return{fileId:l(e.fileId),filename:l(e.filename),mediaType:l(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:l(e.format)}:{}}}import{randomUUID as nc}from"node:crypto";import{promises as ye}from"node:fs";import{homedir as rc}from"node:os";import{join as ir}from"node:path";import*as ei from"proper-lockfile";function dn(){return ir(rc(),".anyforge")}function ln(){return ir(dn(),"oracle")}function ti(){return ir(dn(),"oracle.last-session")}function oc(){return{version:2,active:{},logins:[]}}function cn(t){return t===void 0||typeof t=="string"}function Zo(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.endpoint!="string"||typeof e.clientId!="string"||typeof e.redirectUri!="string"||typeof e.accessToken!="string"||typeof e.refreshToken!="string"||typeof e.expiresAt!="number"||!Number.isFinite(e.expiresAt)||!Array.isArray(e.scopes)||!e.scopes.every(n=>typeof n=="string")||!cn(e.companyId)||!cn(e.companyName)||!cn(e.uid)?null:{endpoint:e.endpoint,...e.companyId?{companyId:e.companyId}:{},...e.companyName?{companyName:e.companyName}:{},...e.uid?{uid:e.uid}:{},clientId:e.clientId,redirectUri:e.redirectUri,accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt,scopes:e.scopes}}function ic(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;if(e.version===1){let o=Zo(e);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(e.version!==2||!Array.isArray(e.logins)||!cn(e.endpoint)||!e.active||typeof e.active!="object"||Array.isArray(e.active))return null;let n=e.logins.map(Zo);if(n.some(o=>o===null))return null;let r={};for(let[o,i]of Object.entries(e.active)){if(typeof i!="string")return null;r[o]=i}return{version:2,...e.endpoint?{endpoint:e.endpoint}:{},active:r,logins:n}}async function Z(){let t=ln(),e;try{e=await ye.readFile(t,"utf8")}catch(o){if(o.code==="ENOENT")return oc();throw o}let n;try{n=JSON.parse(e)}catch{throw new Error(`Oracle credentials at ${t} are invalid JSON; run \`anyforge oracle logout --all\` and sign in again.`)}let r=ic(n);if(!r)throw new Error(`Oracle credentials at ${t} have an unsupported format; run \`anyforge oracle logout --all\` and sign in again.`);return r}async function _t(t){if(!t.logins.length){await ye.rm(ln(),{force:!0});return}await si(ln(),t)}async function un(t){return mn(async()=>{let e=await Z(),n=await t(e);return await _t(e),n})}function Oe(t,e){return t.logins.filter(n=>n.endpoint===e)}function pn(t,e){let n=Oe(t,e);return n.find(r=>r.clientId===t.active[e])??n[0]}function ee(t){return t.companyName||t.companyId||"workspace not known yet"}function ni(t,e){let n=e.companyId?t.logins.find(r=>r.endpoint===e.endpoint&&r.companyId===e.companyId&&r.clientId!==e.clientId):void 0;return t.logins=[...t.logins.filter(r=>r!==n&&r.clientId!==e.clientId),e],t.active[e.endpoint]=e.clientId,t.endpoint=e.endpoint,n}function sr(t,e){t.logins=t.logins.filter(o=>!(o.endpoint===e.endpoint&&o.clientId===e.clientId));let n=Oe(t,e.endpoint);if(!n.length){if(delete t.active[e.endpoint],t.endpoint===e.endpoint){let o=t.logins[0]?.endpoint;o?t.endpoint=o:delete t.endpoint}return}if(t.active[e.endpoint]!==e.clientId&&n.some(o=>o.clientId===t.active[e.endpoint]))return;let r=n[0];return t.active[e.endpoint]=r.clientId,r}function ri(t,e,n,r){let o=t.logins.find(a=>a.endpoint===e&&a.clientId===n);if(!o)return{changed:!1};let i=o.companyId!==r.companyId||r.companyName!==void 0&&o.companyName!==r.companyName||r.uid!==void 0&&o.uid!==r.uid;o.companyId=r.companyId,r.companyName!==void 0&&(o.companyName=r.companyName),r.uid!==void 0&&(o.uid=r.uid);let s=t.logins.find(a=>a!==o&&a.endpoint===e&&a.companyId===r.companyId);return s&&(t.logins=t.logins.filter(a=>a!==s),t.active[e]===s.clientId&&(t.active[e]=o.clientId)),{changed:i||!!s,...s?{duplicate:s}:{}}}function Me(t,e){let n=e.trim(),r=t.find(a=>a.companyId===n);if(r)return r;let o=t.filter(a=>a.companyName?.toLowerCase()===n.toLowerCase());if(o.length===1)return o[0];let i=t.map(a=>`${ee(a)}${a.companyId&&a.companyName?` (${a.companyId})`:""}`),s=i.length?`Saved workspaces: ${i.join(", ")}.`:"No Oracle logins are saved for this endpoint.";throw o.length>1?new y(`More than one saved workspace is named "${n}". Use its id instead.`,s):new y(`No saved Oracle login for workspace "${n}".`,`${s} Add one with \`anyforge oracle login --workspace "${n}"\`.`)}async function oi(){let t;try{t=await ye.readFile(ti(),"utf8")}catch(r){if(r.code==="ENOENT")return[];throw r}let e;try{e=JSON.parse(t)}catch{return[]}if(!e||typeof e!="object"||Array.isArray(e))return[];let n=e;return n.version!==2||!Array.isArray(n.sessions)?[]:n.sessions.filter(r=>{if(!r||typeof r!="object")return!1;let o=r;return typeof o.endpoint=="string"&&typeof o.companyId=="string"&&typeof o.sessionId=="string"})}async function ii(t,e,n=""){let r=(await oi()).filter(o=>!(o.endpoint===e&&o.companyId===n));await si(ti(),{version:2,sessions:[...r,{endpoint:e,companyId:n,sessionId:t}]})}async function fn(t,e=""){return(await oi()).find(n=>n.endpoint===t&&n.companyId===e)?.sessionId}async function si(t,e){await ye.mkdir(dn(),{recursive:!0,mode:448});let n=`${t}.${nc()}.tmp`;try{await ye.writeFile(n,`${JSON.stringify(e,null,2)}
|
|
266
|
-
`,{mode:384,flag:"wx"}),await ye.rename(n,t),await ye.chmod(t,384)}finally{await ye.rm(n,{force:!0}).catch(()=>{})}}async function mn(t){await ye.mkdir(dn(),{recursive:!0,mode:448});let e=ln(),n=await ei.lock(e,{realpath:!1,lockfilePath:`${e}.refresh-lock`,stale:6e4,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}});try{return await t()}finally{await n()}}var ar=8192,ci=10*1048576,lc=20*1048576,li=10,di=3;function cr(t,e){if(t.type!=="tool_call"&&t.type!=="tool_call_start"||typeof t.spawnId=="string")return!1;let n=typeof t.id=="string"?t.id:void 0;if(n){if(e.has(n))return!1;e.add(n)}return!tt(t.name)}var Ue=class{lastIdx=-1;answer="";question;error;done;streamId;files=[];toolIds=new Set;accept(e){return e.type==="finalizing"?!0:e.idx<=this.lastIdx?!1:(this.lastIdx=e.idx,e.type==="stream_start"&&typeof e.streamId=="string"?this.streamId=l(e.streamId):e.type==="delta"&&typeof e.content=="string"?this.answer+=e.content:cr(e,this.toolIds)?this.answer="":e.type==="question"?this.question=ur(e):e.type==="error"?this.error=dr(e):e.type==="done"?this.done=e:e.type==="navigator_file_created"&&dc(this.files,e.file),!0)}get settled(){return!!(this.done||this.error)}outcome(){return{answer:this.answer,...this.question?{question:this.question}:{},...this.error?{error:this.error}:{},...this.done?{done:this.done}:{},...this.streamId?{streamId:this.streamId}:{},...this.files.length?{files:[...this.files]}:{}}}};async function gn(t,e,n,r,o,i=new Ue){let s=async p=>{let R=i.streamId;i.accept(p)&&(i.streamId&&i.streamId!==R&&r.onStream?.(i.streamId),await r.onEvent(p))},a=()=>lr(t,e,i,s,o,r.onActivity),c=async()=>{try{for await(let p of t.streamRequestEvents(e,n.requestId,-1,r.onActivity))await s(p)}catch(p){throw o?.aborted?p:new f("Oracle connection failed and the submitted request could not be recovered by its request id. It was not resubmitted with a new id.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)}if(i.settled)return i.outcome();if(i.streamId)return a();throw new f("Oracle request lookup returned no stream events. It was not resubmitted with a new id.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)},u=async()=>{try{let p=await t.submitTurn(e,n,o);for await(let R of Ct(p,r.onActivity))await s(R)}catch(p){if(o?.aborted||p instanceof f&&p.exitCode===m.API_ERROR)throw p;return i.streamId?a():c()}return i.settled?i.outcome():i.streamId?a():c()},d;try{d=await t.submitTurn(e,n,o)}catch(p){if(o?.aborted||p instanceof f&&p.exitCode!==m.FAILURE)throw p;return u()}if(!d)throw new f("Oracle did not return a stream response.",m.FAILURE);r.onResponse?.();try{for await(let p of Ct(d,r.onActivity))await s(p)}catch(p){if(o?.aborted||p instanceof f)throw p;return i.streamId?a():u()}return i.settled?i.outcome():i.streamId?a():u()}async function lr(t,e,n,r,o,i){let s=n.streamId;if(!s)throw new f("Oracle stream replay needs a stream id.",m.FAILURE);let a=0;for(;!n.settled&&a<di;){if(o?.aborted)throw o.reason;try{for await(let c of t.streamReplayEvents(e,s,n.lastIdx+1,i))await r(c);if(!n.settled){if((await t.getSession(e)).activeStreamId!==s)break;await ai(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof f&&c.exitCode===m.API_ERROR||(a+=1,a>=di))throw c;await ai(300,void 0,o?{signal:o}:void 0)}}if(!n.settled)throw new f("Oracle stream could not be fully replayed; the turn was not submitted again.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the session.`);return n.outcome()}function $t(t,e){let n=e.done,r=n?.servedBy,o=De(n?.tokens),i=G(n?.rounds),s=typeof n?.exitReason=="string"?l(n.exitReason):void 0,a=n?.exitReason==="completed"&&!e.question,c=e.files??(Array.isArray(n?.files)?n.files.map(dt):void 0),u=e.error?"error":e.question?"awaiting_input":a?"complete":"interrupted";return{protocolVersion:1,sessionId:t,status:u,answer:l(e.answer),partial:!a,...typeof n?.messageId=="string"?{messageId:l(n.messageId)}:typeof n?.answerId=="string"?{messageId:l(n.answerId)}:{},...e.question?{question:e.question}:{},...e.error?{error:l(e.error)}:{},...e.streamId?{streamId:l(e.streamId)}:{},...r?{servedBy:ge(r)}:{},...o?{usage:{...o,...i!==void 0?{rounds:i}:{}}}:{},...i!==void 0?{rounds:i}:{},...s?{exitReason:s}:{},...c?.length?{files:ge(c)}:{}}}function E(t,e){return typeof t=="string"?l(t).slice(0,e):""}function dr(t){return typeof t.message=="string"?l(t.message).slice(0,2e3):"Oracle turn failed."}function Q(t){return l(t instanceof Error?t.message:String(t)).slice(0,2e3)}function ur(t){let e=t.question;if(!e||typeof e!="object")throw new f("Oracle question event omitted its question card.",m.API_ERROR);return uc(e)}function dt(t){let e=t&&typeof t=="object"&&!Array.isArray(t)?t:{};if(typeof e.fileId!="string"||!e.fileId||typeof e.filename!="string"||!e.filename||typeof e.mediaType!="string"||!e.mediaType||typeof e.sizeBytes!="number"||!Number.isSafeInteger(e.sizeBytes)||e.sizeBytes<0)throw new f("Oracle generated-file event omitted safe file metadata.",m.API_ERROR);return{fileId:l(e.fileId),filename:l(e.filename),mediaType:l(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:l(e.format)}:{}}}function dc(t,e){let n=dt(e);t.some(r=>r.fileId===n.fileId)||t.push(n)}function uc(t){let e=t;if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",m.API_ERROR);return{id:l(e.id),optional:e.optional===!0,questions:e.questions.map((n,r)=>{let o=n&&typeof n=="object"?n:{};if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,m.API_ERROR);return{id:l(o.id),header:l(o.header),question:l(o.question),multiSelect:o.multiSelect,options:o.options.map(i=>{let s=i&&typeof i=="object"?i:{};if(typeof s.id!="string"||typeof s.label!="string"||typeof s.description!="string")throw new f("Oracle question option is invalid.",m.API_ERROR);return{id:l(s.id),label:l(s.label),description:l(s.description),...typeof s.recommended=="boolean"?{recommended:s.recommended}:{},...typeof s.confirms=="boolean"?{confirms:s.confirms}:{}}})}})}}function G(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function De(t){if(typeof t=="number"&&Number.isFinite(t))return{totalTokens:t};if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,n=G(e.in)??G(e.input)??G(e.inputTokens),r=G(e.cacheRead),o=G(e.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=G(e.out)??G(e.output)??G(e.outputTokens),a=G(e.total)??G(e.totalTokens)??(i!==void 0||s!==void 0?(i??0)+(s??0):void 0);if(!(i===void 0&&s===void 0&&a===void 0))return{...i!==void 0?{inputTokens:i}:{},...r?{cachedInputTokens:r}:{},...s!==void 0?{outputTokens:s}:{},...a!==void 0?{totalTokens:a}:{}}}function ut(t){if(t&&typeof t=="object"){let e=t;if(typeof e.accepted=="boolean")return e.accepted?"Oracle server accepted the cancellation request.":"Oracle server did not accept the cancellation request.";if(typeof e.cancelled=="boolean")return e.cancelled?"Oracle server cancelled the active turn.":"Oracle server did not cancel the active turn."}return"Oracle server acknowledged the cancellation request."}function pt(t,e){let n=e&&t&&(t.kind==="program"||t.kind==="initiative")?{kind:t.kind,id:t.id}:void 0;return{...!e&&t?{context:t}:{},...n?{sharedEntity:n}:{},visibility:e?"company":"private"}}async function Pt(t,e,n){await ii(t,e,n).catch(()=>{})}async function hn(t,e,n,r){if(!n.length)return[];if(n.length>li)throw new y(`Attach no more than ${li} files per turn.`);let o=0,i=[];for(let a of n){let c=await ac(a);if(!c.isFile())throw new y(`Attachment is not a file: ${a}`);if(c.size>ci)throw new y(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let u=await pc(a,ci);if(o+=u.byteLength,o>lc)throw new y("Attachments exceed the 20 MiB total CLI limit.");let d=cc(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",p=Yo(d)??"";if(!p)throw new y(`Unsupported attachment type: ${a}`);i.push({content:u,name:d,mediaType:p})}let s=[];for(let{content:a,name:c,mediaType:u}of i){await r(l(c),a.byteLength);let d=new ArrayBuffer(a.byteLength);new Uint8Array(d).set(a);let p=new File([d],c,{type:u});s.push(await t.upload(e,p))}return s}async function pc(t,e){let n=[],r=0;for await(let o of sc(t)){let i=Buffer.isBuffer(o)?o:Buffer.from(o);if(r+=i.byteLength,r>e)throw new y(`Attachment exceeds the ${D(e)} limit: ${t}`);n.push(i)}return Buffer.concat(n)}var We=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],mc=2e4,gc=6,hc={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},wc={max_rounds:"reached the step limit",max_tokens:"reached the output limit",token_budget:"reached the token budget",deadline:"ran out of time",interrupted:"interrupted",loop_detected:"stopped a repeating loop",cancelled:"cancelled"},wn=class{constructor(e,n){this.startedAt=e;this.sessionId=n;this.lastSignalAt=e}phase="sending";answer="";tools=[];notices=[];files=[];lastSignalAt;endedAt;end;done;upload;round;maxRounds;serverPhase;agents=new Map;toolIds=new Set;get toolCount(){return this.tools.length}get failedCount(){return this.tools.filter(e=>e.status==="failed").length}get finished(){return this.end!==void 0}setUploading(e,n,r){this.phase="uploading",this.upload=`${e} (${D(n)})`,this.lastSignalAt=r}setPhase(e,n){this.phase=e,this.lastSignalAt=n}touch(e){this.lastSignalAt=e}apply(e,n){if(this.lastSignalAt=n,this.phase==="cancelling")return this.record(e);switch(e.type){case"stream_start":case"thinking":this.phase!=="finishing"&&(this.phase="thinking");break;case"round_start":this.round=G(e.round),this.maxRounds=G(e.maxRounds)??this.maxRounds;for(let r=this.tools.length-1;r>=0;r--)this.tools[r]?.started&&this.tools.splice(r,1);this.phase!=="finishing"&&(this.phase="thinking");break;case"phase":this.serverPhase=typeof e.phase=="string"?e.phase:void 0;break;case"delta":this.phase!=="finishing"&&(this.phase="writing");break;case"tool_result":this.phase!=="finishing"&&(this.phase="thinking");break;case"finalizing":this.phase="finishing";break}this.record(e)}record(e){switch(e.type){case"delta":typeof e.content=="string"&&(this.answer+=e.content);break;case"tool_call_start":case"tool_call":this.recordToolCall(e);break;case"tool_result":{let n=this.tools.find(r=>r.id!==void 0&&r.id===e.toolUseId);n&&(n.status=e.ok===!1?"failed":"ok");break}case"subagent_start":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"running");break;case"subagent_done":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"done");break;case"warning":this.notices.push({tone:"warning",text:`Warning: ${E(e.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${E(e.filename,200)}: ${E(e.error,500)}`});break;case"attachment_notice":if(Array.isArray(e.notices))for(let n of e.notices){let r=n&&typeof n=="object"?n:{};this.notices.push({tone:"warning",text:`${E(r.name,160)}: ${E(r.message,500)}`})}break;case"provider_fallback":{let n=E(e.from,120),r=E(e.to,120),o=E(e.reason,40).replaceAll("_"," ");this.notices.push({tone:"info",text:`Switched model${n?` from ${n}`:""}${r?` to ${r}`:""}${o?` (${o})`:""}.`});break}case"navigator_file_created":{let n=dt(e.file);if(this.files.some(r=>r.fileId===n.fileId))break;this.files.push(n),this.notices.push({tone:"info",text:`Generated ${n.filename} (${D(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`});break}case"error":this.notices.push({tone:"error",text:dr(e)});break;case"done":this.done=e;break}}recordToolCall(e){let n=typeof e.id=="string"?e.id:void 0,r=cr(e,this.toolIds),o=typeof e.name=="string"?e.name:"";if(tt(o))return;let i=E(typeof e.label=="string"?e.label:sn(o),120)||"tool",s=n?this.tools.find(c=>c.id===n):void 0;if(s){s.label=i,e.type==="tool_call"&&delete s.started;return}let a={label:i,status:"running",...n?{id:n}:{},...typeof e.spawnId=="string"?{spawnId:e.spawnId}:{},...e.type==="tool_call_start"?{started:!0}:{}};r&&this.answer.trim()&&(a.note=l(this.answer.trim()),this.answer=""),this.tools.push(a),this.phase!=="finishing"&&(this.phase="thinking")}note(e,n){this.notices.push({tone:e,text:l(n)})}finish(e,n){this.end=e,this.endedAt=n}label(){if(this.phase==="cancelling")return"Cancelling";if(this.phase==="uploading")return`Uploading ${this.upload??"attachment"}`;if(this.phase==="sending")return"Sending";if(this.phase==="preparing")return"Preparing context";if(this.phase==="finishing")return"Finishing up";let e=[...this.agents.values()].filter(r=>r==="running").length;if(e)return`Researching (${e} agent${e===1?"":"s"})`;let n=this.tools.filter(r=>r.status==="running"&&!r.spawnId);return n.length===1?`Running ${n[0]?.label}`:n.length>1?`Running ${n.length} tools`:this.phase==="writing"?"Writing":this.serverPhase&&hc[this.serverPhase]||"Thinking"}statusLine(e,n){let r=[this.label(),er(e-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=e-this.lastSignalAt;return o>=mc&&this.phase!=="cancelling"&&r.push(`no update for ${er(o)}`),n&&e-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let e=[],n=Et((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=pi("\u2713"):this.end==="awaiting_input"?o=pr("?")+" Waiting for your answer":this.end==="cancelled"?o=mr("\u2717")+" Interrupted":this.end==="error"?o=fr("\u2717")+" Error":o=mr("\u2717")+` Stopped: ${r&&wc[r]||"no final answer"}`,e.push(n),this.tools.length&&e.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=De(this.done?.tokens);(i?.inputTokens!==void 0||i?.outputTokens!==void 0)&&e.push(`${ie(i.inputTokens??0)} in${i.cachedInputTokens?` (${ie(i.cachedInputTokens)} cached)`:""} / ${ie(i.outputTokens??0)} out`);let s=Nt(this.done?.servedBy);return s&&e.push(s),`${o} ${Be(e.join(" \xB7 "))}`}};function Nt(t){if(!t||typeof t!="object")return"";let e=t,n=E(e.model??e.id,100);if(!n)return"";let r=e.fallback&&typeof e.fallback=="object"?e.fallback:void 0;if(r&&typeof r.from=="string"){let o=typeof r.reason=="string"?`: ${E(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${E(r.from,100)}${o})`}return n}var yn=class{constructor(e,n){this.activity=e;this.view=n;this.answer=new ot("",n.linkOrigin)}answer;getText(){return this.answer.getText()}invalidate(){this.answer.invalidate()}render(e){let n=this.activity;this.answer.setText(n.answer);let r=[Tt("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=pr(We[this.view.frame()%We.length]??"*");r.push(Lt(` ${o} ${Be(n.label())}`,e))}r.push(...this.renderTools(e)),r.push(...this.answer.render(e));for(let o of n.notices){let i=o.tone==="error"?fr:o.tone==="warning"?mr:Be;r.push(...ui(i(o.text),e))}return n.finished&&r.push(Lt(n.summaryLine(),e)),r}renderTools(e){let{tools:n}=this.activity;if(!n.length)return[];if(this.activity.finished&&!this.view.expanded()){let s=this.activity.failedCount,a=`Used ${n.length} tool${n.length===1?"":"s"}${s?` \xB7 ${s} failed`:""} (ctrl+o to expand)`;return[Lt(Be(` \u23BF ${a}`),e)]}let r=this.activity.finished?n:n.slice(-gc),o=n.length-r.length,i=[];return o&&i.push(Lt(Be(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),e)),r.forEach((s,a)=>{if(s.note)for(let p of ui(s.note,Math.max(10,e-4)).slice(0,3))i.push(` ${Be(fc(p))}`);let c=s.status==="running"?pr(We[this.view.frame()%We.length]??"*"):s.status==="ok"?pi("\u2713"):fr("\u2717"),u=a===0&&!o?" \u23BF ":" ",d=s.spawnId?` ${Be("agent \xB7")} `:"";i.push(Lt(`${u}${d}${c} ${s.label}`,e))}),i}};import{truncateToWidth as bc,visibleWidth as Rc,wrapTextWithAnsi as mi}from"@earendil-works/pi-tui";import{bold as gi,cyan as kc,dim as ve}from"kleur/colors";import{CombinedAutocompleteProvider as yc}from"@earendil-works/pi-tui";var Ft=[{name:"help",description:"Show commands and keyboard shortcuts"},{name:"status",description:"Show session, access, model, usage and limits"},{name:"model",description:"Show the workspace model and the model that answered last"},{name:"usage",description:"Show turns and tokens used in this terminal"},{name:"new",args:"[shared]",description:"Start a new conversation",idleOnly:!0,completions:["shared"]},{name:"clear",description:"Clear the screen and start a new private conversation",idleOnly:!0},{name:"resume",args:"[session-id]",description:"Pick or open a saved conversation",idleOnly:!0},{name:"workspace",args:"[name|id]",description:"Switch to another saved workspace and start a new conversation there",idleOnly:!0},{name:"context",args:"[clear|program|initiative|codebase <id>]",description:"Show or change the context for the next message",completions:["clear","program ","initiative ","codebase "]},{name:"attach",args:"<path>",description:"Attach a file to your next message"},{name:"copy",description:"Copy the last answer as Markdown"},{name:"files",description:"List files Oracle generated in this conversation"},{name:"exit",description:"Leave the Oracle",aliases:["quit"]}],Oc=[{name:"design",args:"[topic]",description:"Talk through a technical design before anything is created"},{name:"catchup",description:"Catch up on your work: what needs you, what landed, what is running"}];function gr(t){let e=new Set(Ft.flatMap(n=>[n.name,...n.aliases??[]]));return(t??Oc).filter(n=>!e.has(n.name))}function fi(t,e){let n=/^\/([A-Za-z][\w-]*)(?:\s+([\s\S]*))?$/.exec(t.trim());if(!n)return null;let r=(n[1]??"").toLowerCase(),o=(n[2]??"").trim(),i=Ft.find(a=>a.name===r||a.aliases?.includes(r));if(i)return{kind:"local",command:i,arg:o,args:o?o.split(/\s+/):[]};let s=e.find(a=>a.name===r);return s?{kind:"server",command:s}:{kind:"unknown",name:r}}function hr(t,e){let n=[...Ft.flatMap(i=>[i,...(i.aliases??[]).map(s=>({...i,name:s}))]).map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{},...i.completions?{getArgumentCompletions:s=>vc(i.completions??[],s)}:{}})),...t.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new yc(n,e),o=/^\s*\/attach\s+/;return{async getSuggestions(i,s,a,c){let u=(i[s]??"").slice(0,a);return s!==0||!u.trimStart().startsWith("/")?null:o.test(u)?r.getSuggestions(i,s,a,{...c,force:!0}):r.getSuggestions(i,s,a,c)},applyCompletion(i,s,a,c,u){return r.applyCompletion(i,s,a,c,u)},shouldTriggerFileCompletion(i,s,a){return s===0&&o.test((i[s]??"").slice(0,a))}}}function vc(t,e){let n=t.filter(r=>r.startsWith(e.toLowerCase())&&r!==e).map(r=>({value:r,label:r.trim()}));return n.length?n:null}function qt(t,e){return t?e?"write + approve":"write":"read only"}function ft(t){return t?`${t.kind} ${l(t.id)}`:"no context"}function Ce(t){return t?l(t.companyName||t.companyId):""}function hi(t,e){let n=[`${gi(kc("\u26A1\uFE0E AnyForge Oracle"))}${ve(` \xB7 v${l(t.version)}`)}`,`${t.workspace?`${gi(t.workspace)} \xB7 `:""}${he(t.session)} \xB7 ${t.access} \xB7 ${ft(t.context)}`,...t.session.messages.length?[`Resumed: ${l(t.session.title)}`]:[],ve("Only your messages and the files you attach leave this terminal."),ve("/help commands \xB7 /status details")];if(e<24)return n.flatMap(s=>mi(s,Math.max(1,e)));let r=Math.min(e,76),o=r-4,i=[ve(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>mi(a,o))){let a=bc(s,o);i.push(`${ve("\u2502")} ${a}${" ".repeat(Math.max(0,o-Rc(a)))} ${ve("\u2502")}`)}return i.push(ve(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function wi(t){let e=(r,o)=>r?`${ve(r)} ${o}`:o,n=[{title:"Commands",rows:Ft.map(r=>[`/${r.name}`,e(r.args,r.aliases?.length?`${r.description} (also /${r.aliases.join(", /")})`:r.description)])}];if(t.server.length){let r=t.server.map(o=>[`/${o.name}`,e(o.args,l(o.description))]);t.server.some(o=>o.name==="catchup")&&!t.canApprove&&r.push(["",ve("Acting on /catchup decisions needs a session started with `anyforge oracle --write --approve`.")]),n.push({title:"Workspace commands (answered by Oracle)",rows:r})}return n.push({title:"Keys",rows:[["Enter","Send. While Oracle works, queues one message for when it finishes"],["Shift+Enter","New line (Ctrl+J also works)"],["Esc","Interrupt the current answer"],["Ctrl+C","Interrupt the answer, or clear the input; press twice to exit"],["Ctrl+D","Exit when the input is empty"],["Ctrl+O","Show or hide tool activity for finished answers"],["Ctrl+Shift+F","Search the conversation"],["Up / Down","Recall messages sent in this terminal"],["Tab","Complete a command, or a file path after /attach"],["PgUp / PgDn","Scroll; Home and End jump to the top and bottom"]]}),n}function wr(t){let n=[["Model",`workspace default ${t.model?`${l(t.model.provider)}/${l(t.model.id)}`:"chosen by the server"}`]],r=Nt(t.lastServedBy);if(r){let o=t.lastServedBy,i=E(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function yr(t){return[["This run",t.turns?`${t.turns} turn${t.turns===1?"":"s"} \xB7 ${t.tools} tool${t.tools===1?"":"s"} \xB7 ${ie(t.inputTokens)} in / ${ie(t.outputTokens)} out tokens`:"no turns yet"]]}function Or(t){if(!t?.finished)return[];let e=[Et((t.endedAt??t.startedAt)-t.startedAt)];e.push(`${t.toolCount} tool${t.toolCount===1?"":"s"}${t.failedCount?` (${t.failedCount} failed)`:""}`);let n=G(t.done?.rounds);n!==void 0&&e.push(`${n} round${n===1?"":"s"}`);let r=De(t.done?.tokens);(r?.inputTokens!==void 0||r?.outputTokens!==void 0)&&e.push(`${ie(r.inputTokens??0)} in${r.cachedInputTokens?` (${ie(r.cachedInputTokens)} cached)`:""} / ${ie(r.outputTokens??0)} out tokens`);let o=E(t.done?.exitReason,40);return e.push(o?o.replaceAll("_"," "):t.end??""),[["Last turn",e.filter(Boolean).join(" \xB7 ")]]}function yi(t){let e=t.capabilities?["read","write","approve"].filter(o=>t.capabilities?.[o]).join(", ")||"none":"not reported",n=t.contextText?` \xB7 note ${D(Buffer.byteLength(t.contextText,"utf8"))}`:"",r=Mo(t.limits);return[["Session",`${t.session.sessionId} \xB7 ${l(t.session.title)||"Untitled"}`],["Audience",he(t.session)],["Access",`this session ${qt(t.write,t.approve)} \xB7 grant ${t.grantScopes.join(", ")||"none"} \xB7 workspace role ${e}`],["Context",`${ft(t.context)}${n}`],...wr(t),...Or(t.lastTurn),...yr(t.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...t.identity?[["Workspace",Ac(t.identity,t.savedWorkspaces)]]:[],["Endpoint",t.endpoint],["Version",`CLI ${l(t.version)} \xB7 protocol ${1}`],["Attached",t.pendingFiles.length?`${t.pendingFiles.map(l).join(", ")} (uploads with your next message)`:"nothing waiting"]]}function Ac(t,e){let n=t.companyName?`${l(t.companyName)} (${l(t.companyId)})`:`company ${l(t.companyId)}`,r=e!==void 0&&e>1?` \xB7 ${e} saved, /workspace to switch`:"";return`${n} \xB7 user ${l(t.uid)}${r}`}function Oi(t,e){return e.length?e.map((n,r)=>[`${r+1}.`,`${l(n.filename)} (${D(n.sizeBytes)}) \xB7 anyforge oracle download ${t} ${l(n.fileId)} --output .`]):[["Files","Oracle has not generated files in this conversation."]]}import{randomUUID as Ec}from"node:crypto";import{stat as Tc}from"node:fs/promises";import{basename as vi}from"node:path";import{bold as Cc,cyan as bi,dim as Ri,red as gt,yellow as _c}from"kleur/colors";import{Container as $c,Editor as Pc,ProcessTerminal as Lc,ScrollView as Nc,SelectList as ki,TuiAltScreen as Fc,VStack as qc,getNativeClipboard as jc,matchesKey as bn,truncateToWidth as Ai,wrapTextWithAnsi as Mc}from"@earendil-works/pi-tui";import{bold as Ic,cyan as vr,dim as On,yellow as xc}from"kleur/colors";import{Input as Sc,Text as Ge,matchesKey as mt}from"@earendil-works/pi-tui";var vn=class{constructor(e,n,r){this.tui=e;this.question=n;this.onComplete=r;(n.questions[0]?.options.length??0)===0&&(this.mode="custom"),this.customInput.onSubmit=o=>{let i=o.trim().slice(0,8e3);if(!i){this.errorMessage="Enter a short answer before continuing.",this.tui.requestRender();return}this.recordCurrent(i)},this.customInput.onEscape=()=>{if(!this.question.questions[this.questionIndex]?.options.length){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.setValue(""),this.mode="select",this.tui.setFocus(this),this.tui.requestRender()}}focused=!1;questionIndex=0;optionIndex=0;mode="select";errorMessage="";selected=new Map;answers=[];customInput=new Sc({prompt:`${vr("Your answer: ")} `,placeholder:"Write a short answer and press Enter",placeholderStyle:On});invalidate(){}render(e){let n=this.question.questions[this.questionIndex];if(!n)return[];this.customInput.focused=this.focused&&this.mode==="custom";let r=Math.max(12,e-2),o=this.question.questions.length>1?` \xB7 ${this.questionIndex+1}/${this.question.questions.length}`:"",i=[...new Ge(`${Ic(vr(l(n.header)))}${On(o)}`,0,0).render(r),...new Ge(l(n.question),0,0).render(r),...this.renderOptions(n,r)];return this.mode==="custom"?(i.push(...this.customInput.render(r)),i.push(...new Ge(On(`Enter submits your text${n.options.length?" \xB7 Esc back to the choices":this.question.optional?" \xB7 Esc skip":""}`),0,0).render(r))):i.push(...new Ge(On(this.instructions(n)),0,0).render(r)),this.errorMessage&&i.push(...new Ge(xc(this.errorMessage),0,0).render(r)),i.map(s=>`${vr("\u2502")} ${s}`)}handleInput(e){if(this.mode==="custom"){this.customInput.handleInput(e);return}let n=this.question.questions[this.questionIndex];if(!n)return;let r=this.selectedFor(n);if(n.options.length===0){if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(mt(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(e);return}if(mt(e,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(mt(e,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(mt(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(e.toLowerCase()==="o"){this.mode="custom",this.tui.setFocus(this),this.tui.requestRender();return}let o=Number(e);if(Number.isInteger(o)&&o>=1&&o<=n.options.length){let i=n.options[o-1]?.id;i&&(this.optionIndex=o-1,n.multiSelect?r.has(i)?r.delete(i):r.add(i):(r.clear(),r.add(i)),this.tui.requestRender());return}if(mt(e,"space")&&n.multiSelect){let i=n.options[this.optionIndex];i&&(r.has(i.id)?r.delete(i.id):r.add(i.id),this.tui.requestRender());return}if(mt(e,"enter")){if(r.size===0){this.errorMessage="Select an option explicitly, type a custom answer, or skip if optional.",this.tui.requestRender();return}this.recordCurrent()}}renderOptions(e,n){let r=this.selectedFor(e),o=[];return e.options.forEach((i,s)=>{let a=r.has(i.id),c=e.multiSelect?a?"[x]":"[ ]":a?"(x)":"( )",u=s===this.optionIndex?"\u203A":" ",d=i.recommended?" \xB7 recommended":"",p=i.confirms?" \xB7 confirms the shown action":"",R=`${u} ${s+1}. ${c} ${l(i.label)}${d}${p}`;o.push(...new Ge(R,0,0).render(n)),o.push(...new Ge(` ${l(i.description)}`,0,0).render(n))}),o}instructions(e){return e.options.length===0?`Write a short answer${this.question.optional?" \xB7 s or Esc skip card":""}`:`\u2191/\u2193 move \xB7 1\u2013${e.options.length} ${e.multiSelect?"toggle":"select"}${e.multiSelect?" \xB7 Space toggle":""} \xB7 Enter continue \xB7 o write your own${this.question.optional?" \xB7 s or Esc skip card":""}`}requireAnswer(){let e=this.question.questions[this.questionIndex];this.errorMessage=e?.options.length?"Oracle needs an answer to continue: pick an option, or press o to write your own.":"Oracle needs an answer to continue: type it and press Enter.",this.tui.requestRender()}selectedFor(e){let n=this.selected.get(e.id);return n||(n=new Set,this.selected.set(e.id,n)),n}recordCurrent(e){let n=this.question.questions[this.questionIndex];if(!n)return;e&&this.selectedFor(n).clear();let r=[...this.selectedFor(n)],o={questionId:n.id,optionIds:r,...e?{customText:e}:{}},i=this.answers.findIndex(s=>s.questionId===n.id);if(i>=0?this.answers.splice(i,1,o):this.answers.push(o),this.customInput.setValue(""),this.mode="select",this.questionIndex<this.question.questions.length-1){this.questionIndex+=1,this.optionIndex=0,this.mode=this.question.questions[this.questionIndex]?.options.length===0?"custom":"select",this.errorMessage="",this.tui.setFocus(this),this.tui.requestRender();return}this.finish({questionId:this.question.id,answers:this.answers.map(s=>({...s}))})}finish(e){this.onComplete(e)}};var Ii=1500,Uc=100,Dc="\x1B]133;A\x07";function ht(t){return{render:t,invalidate(){}}}function xi(t,e,n,r){let o=new ot(e,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>t==="user"?Ko(o,i,n).map((s,a)=>a===1?`${Dc}${s}`:s):[Tt("assistant"),...o.render(i)]}}async function Si(t){let{client:e,config:n}=t,r=new Lc,o=new Fc(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new $c,s=new Nc(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new Pc(o,Go),c=gr(n.commands),u=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,d=t.session,p=t.initialContext,R=t.contextText,I=t.canWrite,x=t.canApprove,O,v,k,P,j=[...t.initialFiles],U=!1,ue=!1,ze=!1,Sn=0,He,Cr=0,Dt=!1,_e="",wt,Bt,Ye=[],Je={turns:0,inputTokens:0,outputTokens:0,tools:0},Wt=new Map,pe,Xi=ht(g=>{if(O){let h=bi(We[Sn%We.length]??"*");return[Ai(`${h} ${O.activity.statusLine(Date.now(),u())}`,g)]}return pe?[Ai(`${bi("?")} Waiting for your answer to the question above`,g)]:[]}),Zi=ht(g=>{let h=[Ce(n.identity),qt(I,x),ft(p),zo(d),j.length?`${j.length} file${j.length===1?"":"s"} attached`:"",v!==void 0?"1 message queued":""];return[Ri(Do(g,h,O?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new qc([{component:s,basis:0,grow:1,minSize:1},{component:Xi,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:Zi,basis:"auto",shrink:0,minSize:1}]));let te=()=>o.requestRender(),N=g=>o.flash(l(g),3e3),ke=g=>{i.addChild(g),te()},B=(g,h=Ri)=>{let w=l(g);ke(ht(b=>Mc(h(w),Math.max(1,b))))},Gt=(g,h)=>{ke(ht(w=>["",...Uo(g,h,w)]))},es=()=>{He||(He=setInterval(()=>{Sn+=1,te()},Uc),He.unref())},En=()=>{He&&clearInterval(He),He=void 0},Qe=()=>{U||o.hasOverlay()||o.setFocus(pe??a)},_r=g=>{k=g;let h=Wt.get(g.id);h&&i.removeChild(h);let w=new vn(o,g,b=>{Wt.delete(g.id),pe===w&&(pe=void 0),i.removeChild(w),rs(b??{questionId:g.id,skipped:!0,answers:[]}),Qe()});Wt.set(g.id,w),pe=w,ke(w)},Kt=g=>{i.clear(),Wt.clear(),pe=void 0,d=g,k=void 0,P=void 0,Ye=[],_e="";let h={version:t.version,workspace:Ce(n.identity),session:g,access:qt(I,x),context:p};ke(ht(b=>hi(h,b)));let w;for(let b of g.messages){ke(xi(b.role,b.content,b.authorName,e.endpoint)),b.role==="assistant"&&(_e=b.content),b.question&&(w=b.question),b.questionResponse&&(B("A stored response was recorded for this question."),w?.id===b.questionResponse.questionId&&(w=void 0));for(let A of b.files??[])Ye.some(_=>_.fileId===A.fileId)||Ye.push(A),B(`File: ${A.filename} (${A.mediaType}, ${D(A.sizeBytes)}) \xB7 anyforge oracle download ${g.sessionId} ${A.fileId} --output .`)}w&&_r(w),Qe(),Pt(g.sessionId,e.endpoint,n.identity?.companyId)},$r,ts=new Promise(g=>{$r=g}),zt=()=>{U||(U=!0,En(),O?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),$r())},Tn=async()=>{let g=O;if(!g||g.cancelling)return;g.cancelling=!0,g.activity.setPhase("cancelling",Date.now()),te();let{streamId:h,requestId:w}=g;g.controller.abort(new Error("Turn cancelled by user."));let b;if(h)try{b=ut(await e.cancelTurn(d.sessionId,h))}catch(A){b=`Could not confirm server cancellation: ${Q(A)}`}else if(w)try{b=ut(await e.cancelRequest(d.sessionId,w))}catch(A){b=`Could not confirm server cancellation by request id: ${Q(A)}`}else b="The turn had not reached the server; nothing was sent.";g.activity.note("info",b),te()},Ve=async g=>{if(O||U)return;if(g.kind==="submit"&&g.response){let $=k;if(!$||$.id!==g.response.questionId){N("That question is no longer active. Resume the session to refresh it.");return}B(Bc($,g.response)),k=void 0}else g.kind==="submit"&&ke(xi("user",g.text));let h=d.sessionId,w=new wn(Date.now(),h),b=new yn(w,{expanded:()=>ze,frame:()=>Sn,linkOrigin:e.endpoint}),A={activity:w,view:b,controller:new AbortController,cancelling:!1};O=A,ke(b),es();let _=new Ue,H=$=>{w.apply($,Date.now()),$.type==="question"&&(_r(ur($)),Qe()),te()},$e={onEvent:H,onStream:$=>{A.streamId=$},onResponse:()=>{w.phase==="sending"&&w.setPhase("preparing",Date.now())},onActivity:()=>w.touch(Date.now())},ne="error";try{if(g.kind==="replay"){_.streamId=g.streamId,A.streamId=g.streamId,w.setPhase("thinking",Date.now());let $=await lr(e,h,_,async se=>{_.accept(se)&&H(se)},A.controller.signal,$e.onActivity);ne=$t(h,$).status}else{let $=g.response?[]:j.splice(0),se=await hn(e,h,$,(Cn,fs)=>{w.setUploading(Cn,fs,Date.now()),te()});A.controller.signal.throwIfAborted(),w.setPhase("sending",Date.now());let Ht=Ec();A.requestId=Ht;let us={requestId:Ht,text:g.response?"":g.text,readOnly:!I,...x?{allowApprove:!0}:{},...p?{context:p}:{},...R?{contextText:R}:{},...g.response?{questionResponse:g.response}:{},...se.length?{attachments:se.map(Cn=>({fileId:Cn.fileId}))}:{}},ps=await gn(e,h,us,$e,A.controller.signal,_);ne=$t(h,ps).status}}catch($){if(A.controller.signal.aborted)ne="cancelled";else{ne="error";let se=$ instanceof f&&$.hint?` ${l($.hint)}`:"";w.note("error",`${Q($)}${se}`)}}finally{w.finish(ne,Date.now()),O=void 0,En(),U||(ue=ne==="cancelled"),wt=w,w.answer.trim()&&(_e=w.answer),w.done?.servedBy&&(Bt=w.done.servedBy);let $=De(w.done?.tokens);Je.turns+=1,Je.tools+=w.toolCount,Je.inputTokens+=$?.inputTokens??0,Je.outputTokens+=$?.outputTokens??0;for(let se of w.files)Ye.some(Ht=>Ht.fileId===se.fileId)||Ye.push(se);te()}if(!U){if(A.streamId&&ne!=="cancelled"){let $=await e.getSession(h).catch(()=>{});$&&$.sessionId===d.sessionId&&(d=$)}ns(ne)}},ns=g=>{if(!(U||O)){if(P){let h=P;P=void 0,Ve({kind:"submit",text:"",response:h});return}if(v!==void 0){let h=v;if(v=void 0,g==="complete"&&!pe){Ve({kind:"submit",text:h});return}a.getText()||a.setText(h),N("Your queued message is back in the input; press Enter to send it.")}Qe(),te()}},rs=g=>{if(O){P=g,N("Answer saved; Oracle continues once the current turn settles.");return}Ve({kind:"submit",text:"",response:g})},Pr=async g=>{try{let h=await e.getSession(g);Kt(h),h.activeStreamId&&Ve({kind:"replay",streamId:h.activeStreamId}),N(`Resumed ${h.title||h.sessionId}.`)}catch(h){B(Q(h),gt)}},os=async()=>{try{let g=await e.listSessions();if(!g.length){N("No saved conversations to resume.");return}let h=new ki(g.map(_=>({value:_.sessionId,label:_.title,description:he(_)})),Math.min(12,g.length),it),w=await fn(e.endpoint,n.identity?.companyId),b=w?g.findIndex(_=>_.sessionId===w):-1;b>=0&&h.setSelectedIndex(b);let A=o.showOverlay(h,{width:"85%",maxHeight:"80%",anchor:"center"});h.onSelect=_=>{A.hide(),Pr(_.value)},h.onCancel=()=>{A.hide(),Qe()},A.focus()}catch(g){B(Q(g),gt)}},Lr=async g=>{if(g&&(!p||p.kind!=="program"&&p.kind!=="initiative")){N("Use /context program <id> or /context initiative <id> before /new shared.");return}try{let h=await e.createSession(pt(p,g));Kt(h),N(`Started a new conversation \xB7 ${he(h)}.`)}catch(h){B(Q(h),gt)}},Nr=async(g,h)=>{let w=l(ee(h));if(h.companyId&&h.companyId===n.identity?.companyId){N(`Already in ${w}.`);return}Dt=!0;try{N(`Connecting to ${w}...`);let b=await g.connect(h),A=await b.client.createSession(pt(void 0,!1));e=b.client,n=b.config,c=gr(n.commands),a.setAutocompleteProvider(hr(c,process.cwd()));let _=e.scopes.has("write")&&n.capabilities?.write===!0,H=e.scopes.has("approve")&&n.capabilities?.approve===!0,$e=I&&!_||x&&!H;I&&!_&&(I=!1),(!I||!H)&&(x=!1);let ne=p!==void 0;p=void 0,Bt=void 0,wt=void 0,Kt(A),B(`Switched to ${Ce(n.identity)||w}. This is a new conversation in that workspace.${ne?" Context was cleared because its id belongs to the previous workspace.":""}`),$e&&B(`Access here is ${qt(I,x)}: this workspace's login or your role there does not allow more.`,_c)}catch(b){let A=b instanceof f&&b.hint?` ${l(b.hint)}`:"";B(`Could not switch to ${w}: ${Q(b)}${A}`,gt)}finally{Dt=!1}},is=async(g,h)=>{let w=n.identity?.companyId,b=new ki(h.map(H=>({value:H.clientId,label:ee(H),description:[H.companyId,H.scopes.join(", "),H.companyId===w?"current":""].filter(Boolean).join(" \xB7 ")})),Math.min(12,h.length),it),A=h.findIndex(H=>H.companyId===w);A>=0&&b.setSelectedIndex(A);let _=o.showOverlay(b,{width:"85%",maxHeight:"80%",anchor:"center"});b.onSelect=H=>{_.hide();let $e=h.find(ne=>ne.clientId===H.value);$e&&Nr(g,$e)},b.onCancel=()=>{_.hide(),Qe()},_.focus()},ss=async g=>{let h=t.workspaces;if(!h){B(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${Ce(n.identity)||"one workspace"}. To switch workspaces, unset it and sign in with \`anyforge oracle login --workspace <name>\`.`);return}let w=await h.list();if(g){let b;try{b=Me(w,g)}catch(A){let _=A instanceof f&&A.hint?` ${l(A.hint)}`:"";B(`${Q(A)}${_}`,gt);return}await Nr(h,b);return}if(w.length<=1){B(`${Ce(n.identity)||"This workspace"} is the only saved workspace. To add another, run \`anyforge oracle login --workspace <name>\` in another terminal, then use /workspace here.`);return}await is(h,w)},as=g=>({session:d,write:I,approve:x,grantScopes:[...e.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...p?{context:p}:{},...R?{contextText:R}:{},...n.model?{model:n.model}:{},lastServedBy:Bt,...wt?{lastTurn:wt}:{},totals:{...Je},...n.limits?{limits:n.limits}:{},...n.identity?{identity:n.identity}:{},...g!==void 0?{savedWorkspaces:g}:{},endpoint:e.endpoint,version:t.version,pendingFiles:j.map(h=>vi(h))}),cs=async()=>{if(!_e.trim()){N("There is no answer to copy yet.");return}try{let g=jc();g?.setText?await g.setText(_e):r.write(`\x1B]52;c;${Buffer.from(_e,"utf8").toString("base64")}\x07`),N(`Copied the last answer (${_e.length} characters).`)}catch(g){N(`Could not copy: ${Q(g)}`)}},ls=async(g,h,w)=>{if(g.idleOnly&&Dt){N("Wait for the workspace switch to finish.");return}if(g.idleOnly&&O){N(`Wait for Oracle to finish, or press Esc, before /${g.name}.`);return}switch(g.name){case"help":ke(ht(b=>["",...wi({server:c,canApprove:x}).flatMap(A=>[Cc(A.title),...tr(A.rows,b)])]));return;case"status":{let b=await t.workspaces?.list().then(A=>A.length).catch(()=>{});Gt("Status",yi(as(b)));return}case"workspace":await ss(w);return;case"model":Gt("Model",wr({...n.model?{model:n.model}:{},lastServedBy:Bt}));return;case"usage":Gt("Usage",[...Or(wt),...yr(Je)]);return;case"files":Gt("Generated files",Oi(d.sessionId,Ye));return;case"copy":await cs();return;case"exit":zt();return;case"new":if(w&&w!=="shared"){N("Usage: /new or /new shared");return}await Lr(w==="shared");return;case"clear":await Lr(!1);return;case"resume":w?await Pr(w):await os();return;case"context":{if(!w){B(`Context: ${ft(p)}${R?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(w==="clear")p=void 0;else{let[b,A,..._]=h;if(!["program","initiative","codebase"].includes(b??"")||!A||_.length){N("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}p={kind:b,id:A}}B(`Context for your next message: ${ft(p)}.`);return}case"attach":{if(!w){N("Usage: /attach <file path>");return}let b=await Tc(w).catch(()=>{});if(!b?.isFile()){N(`Not a readable file: ${w}`);return}j.push(w),B(`Attached ${vi(w)} (${D(b.size)}). It uploads with your next message.`);return}}},Fr=g=>{let h=g.trim();if(!h)return;a.addToHistory(h);let w=fi(h,c);if(w?.kind==="local"){ls(w.command,w.args,w.arg).catch(b=>B(Q(b),gt));return}if(w?.kind==="unknown"){a.setText(h),N(`Unknown command /${w.name}. Type /help to see the commands.`);return}if(Dt){a.setText(h),N("Switching workspace; send your message once it has opened.");return}if(O){let b=v!==void 0;v=h,N(b?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),te();return}Ve({kind:"submit",text:h})},ds=o.addInputListener(g=>{if(bn(g,"ctrl+c")){if(O)return Tn(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),te(),{consume:!0};let h=Date.now();return h-Cr<=Ii?zt():(Cr=h,o.flash("Press Ctrl+C again to exit",Ii)),{consume:!0}}if(bn(g,"escape")){let h=pe!==void 0&&o.getFocusedComponent()===pe;return O&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!h?(Tn(),{consume:!0}):void 0}if(bn(g,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(zt(),{consume:!0}):void 0;if(bn(g,"ctrl+o"))return ze=!ze,N(ze?"Showing tool activity":"Hiding tool activity"),te(),{consume:!0}}),qr=()=>{let g=!!O;Tn().finally(()=>{g&&(ue=!0),zt()})};process.on("SIGINT",qr),a.onSubmit=Fr,a.onChange=()=>te(),a.setAutocompleteProvider(hr(c,process.cwd())),Kt(t.session),o.start(),d.activeStreamId&&Ve({kind:"replay",streamId:d.activeStreamId}),t.initialPrompt&&Fr(t.initialPrompt),await ts,ds(),process.off("SIGINT",qr),En(),O?.controller.abort(),await r.drainInput().catch(()=>{}),ue&&(process.exitCode=130)}function Bc(t,e){if(e.skipped)return"Skipped the optional Oracle question.";let n=[];for(let r of e.answers){let o=t.questions.find(i=>i.id===r.questionId);for(let i of r.optionIds){let s=o?.options.find(a=>a.id===i);s&&n.push(s.label)}r.customText&&n.push(r.customText)}return`You answered: ${n.map(l).join("; ")||"no options selected"}`}import{createHash as Wc,randomBytes as Ei,timingSafeEqual as Gc}from"node:crypto";import{createServer as Kc}from"node:http";import zc from"open";var Hc=6e4,Yc=5*6e4,Rn=new Map;async function br(t){let e=Te(t.endpoint),n=await qe(e),r=["read"];t.write&&r.push("write"),t.approve&&(r.includes("write")||r.push("write"),r.push("approve"));let o=Ei(32).toString("base64url"),i=await Xc(o);try{let s={client_name:"AnyForge CLI",redirect_uris:[i.redirectUri],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"},a=await Zc(n.oauth.registration,s);if(typeof a.client_id!="string"||!a.client_id)throw new f("OAuth registration did not return a client id.",m.API_ERROR);let c=Ei(32).toString("base64url"),u=Wc("sha256").update(c).digest("base64url"),d={client_id:a.client_id,redirect_uri:i.redirectUri,response_type:"code",scope:r.join(" "),state:o,code_challenge:u,code_challenge_method:"S256",resource:n.resource},p=new URL(n.oauth.authorization);for(let[U,ue]of Object.entries(d))p.searchParams.set(U,String(ue));let R=t.workspace?.trim();R&&p.searchParams.set(Jo,R),t.onAuthorizationUrl?.(p.toString());try{t.openBrowser?await t.openBrowser(p.toString()):await zc(p.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
|
|
267
|
-
${
|
|
268
|
-
`)}let
|
|
269
|
-
${e}`,i=
|
|
270
|
-
`),
|
|
271
|
-
`),await
|
|
272
|
-
`);return}let
|
|
273
|
-
`),
|
|
274
|
-
`);return}if(e==="workspaces"){if(n.length)throw new y("oracle workspaces does not accept positional arguments.");await
|
|
275
|
-
`);return}let a;try{a=await
|
|
276
|
-
`),a=await
|
|
277
|
-
`)),
|
|
278
|
-
`);let r=
|
|
279
|
-
`)}function
|
|
280
|
-
`)}async function
|
|
281
|
-
`);else if(!r.length)process.stdout.write("No saved Oracle logins. Run `anyforge oracle login` to add one.\n");else{process.stdout.write(`${
|
|
282
|
-
`);for(let o of n.logins){let i=o.clientId===n.active?.clientId,s=
|
|
261
|
+
`},Gp=Object.entries(mn).map(([t,e])=>({uri:t==="SKILL.md"?oi:`${oi}/${t}`,name:t==="SKILL.md"?me:`${me}/${t.replace(/^references\//,"")}`,title:t==="SKILL.md"?"AnyForge platform guide":t,description:t==="SKILL.md"?gc:`Reference for the ${me} skill: ${t}`,mimeType:hc,size:new TextEncoder().encode(e).byteLength}));var yc=mn["SKILL.md"],Hp=yc.replace(/^---[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/,"");var ai={agents:".agents/skills",claude:".claude/skills",codex:".codex/skills"};function Oc(t){let e=t.scope==="user"?t.homeDir??wc():t.cwd??process.cwd();if(t.scope==="user"&&t.client==="codex"){let n=t.codexHome??process.env.CODEX_HOME??We(e,".codex");return We(n,"skills",me)}return We(e,ai[t.client],me)}function kc(t){if(t.scope!=="project"&&t.scope!=="user")throw new y(`--scope must be project or user, got "${t.scope}".`);if(!Object.hasOwn(ai,t.client))throw new y(`--client must be agents, claude, or codex, got "${t.client}".`)}async function mr(t){try{return(await Nt.lstat(t)).isSymbolicLink()}catch(e){if(e.code==="ENOENT")return!1;throw e}}async function Ac(t){kc(t);let e=Oc(t),n=Object.entries(mn),r=n.map(([s])=>We(e,s));if(await mr(e))throw new y(`Refusing to install through the symbolic link ${e}.`);let o=new Set(n.map(([s])=>ii(s)).filter(s=>s!=="."));for(let s of o)if(await mr(We(e,s)))throw new y(`Refusing to install through the symbolic link ${We(e,s)}.`);if(!t.force){let s=[];for(let a of r)try{await Nt.access(a),s.push(a)}catch(c){if(c.code!=="ENOENT")throw c}if(s.length>0)throw new y(`The ${me} skill already exists at ${e}.`,"Use --force only if you want to replace AnyForge-managed files in that directory.")}await Nt.mkdir(e,{recursive:!0});let i=[];for(let[s,a]of n){let c=We(e,s);if(await Nt.mkdir(ii(c),{recursive:!0}),await mr(c))throw new y(`Refusing to replace the symbolic link ${c}.`);await Nt.writeFile(c,a,{encoding:"utf8",mode:420}),i.push(s)}return{name:me,scope:t.scope,client:t.client,directory:e,files:i}}async function ci(t){if(t.subcommand!=="install")throw new y(`Unknown skill command${t.subcommand?`: ${t.subcommand}`:""}.`,"Use `anyforge skill install anyforge-platform`.");if(t.name!==me)throw new y(`Unknown skill${t.name?`: ${t.name}`:""}.`,`The only portable platform skill is ${me}.`);let e=t.scope??"project",n=t.client??"agents";if(!["project","user"].includes(e))throw new y(`--scope must be project or user, got "${e}".`);if(!["agents","claude","codex"].includes(n))throw new y(`--client must be agents, claude, or codex, got "${n}".`);let r=await Ac({scope:e,client:n,force:t.force});if(t.json){process.stdout.write(JSON.stringify(r)+`
|
|
262
|
+
`);return}console.log(vc("Installed ")+`${r.name} ${si(`(${r.scope}, ${r.client})`)}`),console.log(` ${r.directory}`),console.log(si(` ${r.files.length} files installed. Re-run with --force to refresh them.`)),r.scope==="project"&&r.client==="agents"&&console.log(bc(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as Vl}from"node:crypto";import{open as Yl,realpath as Jl,stat as Ql}from"node:fs/promises";import{basename as Xl,join as Zl,resolve as ed}from"node:path";import{bold as jr,cyan as gs,dim as Se,yellow as Fn}from"kleur/colors";import{ProcessTerminal as td,SelectList as nd,Text as rd,TuiAltScreen as od}from"@earendil-works/pi-tui";var Rc=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function xc(t){return Rc.has(t)?"querying the code graph":t==="search_code_semantic"?"searching the code index":t==="search_codebase_docs"?"searching codebase docs":null}function ct(t){let e=xc(t);if(e)return e;let n=/^mcp__(.+?)__(.+)$/.exec(t);return n?`${n[1].replace(/[_-]+/g," ")} \xB7 ${n[2].replace(/_/g," ")}`:t.replace(/_/g," ")}var Ic=new Set(["ask_user","present_catch_up_decision","present_memory_candidates"]);function lt(t){return typeof t=="string"&&Ic.has(t)}import{truncateToWidth as Ht,wrapTextWithAnsi as Ni}from"@earendil-works/pi-tui";import{cyan as _r,dim as ze,green as qi,italic as il,red as Sr,yellow as Er}from"kleur/colors";import{truncateToWidth as qt,visibleWidth as gr,wrapTextWithAnsi as _c}from"@earendil-works/pi-tui";import{bold as Sc,dim as Ec}from"kleur/colors";function K(t){return t<1024?`${t} B`:t<1024*1024?`${Ft(t/1024)} KiB`:`${Ft(t/(1024*1024))} MiB`}function Ut(t){let e=Math.max(0,t);if(e<1e4)return`${Ft(e/1e3)}s`;let n=Math.round(e/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/3600),o=Math.floor(n%3600/60),i=n%60;return r?o?`${r}h ${o}m`:`${r}h`:i?`${o}m ${i}s`:`${o}m`}function hr(t){let e=Math.floor(Math.max(0,t)/1e3);return e<60?`${e}s`:`${Math.floor(e/60)}m ${String(e%60).padStart(2,"0")}s`}function de(t){return Math.abs(t)<1e3?String(Math.round(t)):Math.abs(t)<1e6?`${Ft(t/1e3,t<1e5?1:0)}k`:`${Ft(t/1e6)}M`}function Ft(t,e=1){return t.toFixed(e).replace(/\.0+$/,"")}function Tc(t){return u(t).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function Cc(t,e){return typeof e=="boolean"?e?"on":"off":typeof e=="string"?u(e):/Ms$/.test(t)?Ut(e):/Bytes$/.test(t)?K(e):/tokens?/i.test(t)?de(e):String(e)}function li(t){return t?Object.entries(t).filter(e=>e[1]!==null).map(([e,n])=>`${Tc(e)} ${Cc(e,n)}`):[]}function yr(t,e,n=2){let r=Math.min(16,Math.max(...t.map(([s])=>gr(s)),0)+2),o=Math.max(10,e-n-r),i=[];for(let[s,a]of t)_c(a,o).forEach((d,p)=>{let l=p===0?Ec(qt(s,r-1).padEnd(r)):" ".repeat(r);i.push(qt(`${" ".repeat(n)}${l}${d}`,e))});return i}function di(t,e,n){return[qt(Sc(t),n),...yr(e,n)]}function ui(t,e,n){let r=gr(n),o=e.filter(Boolean).join(" \xB7 ");if(r+4>t)return qt(o||n,t);let i=t-r-2,s=qt(o,i),a=Math.max(2,t-gr(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as ut,cyan as dt,dim as Le,green as $c,italic as pi,strikethrough as Pc,underline as fi,yellow as Lc}from"kleur/colors";import{Markdown as Nc}from"@earendil-works/pi-tui";var qc={heading:t=>ut(dt(t)),link:t=>fi(dt(t)),linkUrl:t=>Le(t),code:t=>Lc(t),codeBlock:t=>t,codeBlockBorder:t=>Le(t),quote:t=>pi(t),quoteBorder:t=>Le(t),hr:t=>Le(t),listBullet:t=>dt(t),bold:t=>ut(t),italic:t=>pi(t),strikethrough:t=>Pc(t),underline:t=>fi(t)},ft={selectedPrefix:t=>dt(t),selectedText:t=>ut(t),description:t=>Le(t),scrollInfo:t=>Le(t),noMatch:t=>Le(t)},mi={borderColor:t=>Le(t),selectList:ft};var pt=class{constructor(e,n){this.linkOrigin=n;this.content=u(e),this.markdown=new Nc(tr(this.content,n),0,0,qc)}linkOrigin;content;markdown;cache;getText(){return this.content}setText(e){let n=u(e);n!==this.content&&(this.content=n,this.markdown.setText(tr(n,this.linkOrigin)),this.cache=void 0)}invalidate(){this.markdown.invalidate(),this.cache=void 0}render(e){let n=Math.max(12,e);if(this.cache?.width===n)return this.cache.lines;let r=this.content?this.markdown.render(n).map(Ao):[];return this.cache={width:n,lines:r},r}};function Mt(t,e){return t==="user"?`${ut($c(e?u(e):"You"))}`:`${ut(dt("Oracle"))}`}function gi(t,e,n){let r=dt("\u2502"),o=Math.max(1,e-4);return["",`${r} ${Mt("user",n)}`,...t.render(o).map(i=>`${r} ${ut(i)}`),""]}function Oe(t){return t.shared||t.visibility==="company"?`Shared with the company${t.entityKey?` \xB7 ${u(t.entityKey)}`:""}`:"Private to you"}function hi(t){return t.shared||t.visibility==="company"?"shared":"private"}import{createReadStream as Xc}from"node:fs";import{stat as Zc}from"node:fs/promises";import{basename as el}from"node:path";import{setTimeout as Ci}from"node:timers/promises";var yi={txt:"text/plain",log:"text/plain",md:"text/markdown",markdown:"text/markdown",csv:"text/csv",json:"application/json",pdf:"application/pdf",ts:"text/plain",tsx:"text/plain",js:"text/plain",jsx:"text/plain",mjs:"text/plain",cjs:"text/plain",py:"text/plain",go:"text/plain",rs:"text/plain",yaml:"text/plain",yml:"text/plain",toml:"text/plain",xml:"text/plain",html:"text/plain",css:"text/plain",sh:"text/plain",sql:"text/plain",java:"text/plain",kt:"text/plain",swift:"text/plain",c:"text/plain",h:"text/plain",cpp:"text/plain",rb:"text/plain",php:"text/plain",tf:"text/plain",diff:"text/plain",patch:"text/plain",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"};function wi(t){let e=t.slice(t.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(yi,e)?yi[e]:null}var wr="/api/v1/oracle";var vi="workspace";function Ne(t){let e;try{e=new URL(t)}catch{throw new f(`Invalid Oracle endpoint: ${t}`,m.USAGE)}if(!["http:","https:"].includes(e.protocol)||e.username||e.password)throw new f("Oracle endpoint must be an http or https URL without embedded credentials.",m.USAGE);if(e.pathname!=="/"||e.search||e.hash)throw new f("Oracle endpoint must be the workspace origin, without a path, query or fragment.",m.USAGE);return e.origin}function ke(t,e){return`${Ne(t)}${wr}${e}`}function Fc(t,e,n){let r;try{r=new URL(t)}catch{throw new f(`Oracle server returned an invalid ${n} endpoint.`,m.API_ERROR)}let o=new URL(Ne(e));if(r.origin!==o.origin||!r.pathname.startsWith("/api/oauth/")||r.search||r.hash||r.username||r.password)throw new f(`Oracle server returned an unsafe ${n} endpoint.`,m.API_ERROR);return r.toString()}function Uc(t,e){if(!t||typeof t!="object")throw new f("Oracle server omitted OAuth endpoints.",m.API_ERROR);let n=t,r=["registration","authorization","token","revocation"],o={};for(let i of r){if(typeof n[i]!="string")throw new f(`Oracle server omitted its OAuth ${i} endpoint.`,m.API_ERROR);o[i]=Fc(n[i],e,i)}return o}async function Ge(t,e=fetch,n,r){let o=Ne(t),i;try{i=await e(ke(o,"/config"),{headers:{Accept:"application/json",...n?{Authorization:`Bearer ${n}`}:{}},redirect:"error",signal:r?AbortSignal.any([r,AbortSignal.timeout(2e4)]):AbortSignal.timeout(2e4)})}catch(l){throw new f(`Cannot reach ${o}: ${l instanceof Error?l.message:String(l)}`,m.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}let s=await ht(i,ke(o,"/config")),a=L(gt(s,i.status)),c=`${o}${wr}`;if(a.protocolVersion!==1)throw new f(`Oracle protocol mismatch: this CLI supports ${1}, and the server reports ${String(a.protocolVersion)}.`,m.API_ERROR,"Update @anyforge/control to a version supported by this workspace.");if(a.resource!==c)throw new f("Oracle server resource does not match this workspace origin.",m.API_ERROR);let d=Array.isArray(a.scopes)?a.scopes:null;if(!d||!d.every(l=>l==="read"||l==="write"||l==="approve"))throw new f("Oracle server returned an invalid scope list.",m.API_ERROR);let p={protocolVersion:1,resource:c,scopes:d,oauth:Uc(a.oauth,o)};if(a.identity!==void 0){let l=L(a.identity);if(typeof l.uid!="string"||typeof l.companyId!="string")throw new f("Oracle server returned an invalid identity.",m.API_ERROR);let A=typeof l.companyName=="string"?u(l.companyName).trim().slice(0,120):"";p.identity={uid:u(l.uid),companyId:u(l.companyId),...A?{companyName:A}:{}}}if(a.capabilities!==void 0){let l=L(a.capabilities);if(![l.read,l.write,l.approve].every(A=>typeof A=="boolean"))throw new f("Oracle server returned invalid capabilities.",m.API_ERROR);p.capabilities={read:l.read,write:l.write,approve:l.approve}}if(a.model!==void 0){let l=L(a.model);if(typeof l.id!="string"||typeof l.provider!="string")throw new f("Oracle server returned invalid model metadata.",m.API_ERROR);p.model={id:u(l.id),provider:u(l.provider)}}if(a.limits!==void 0){let l=L(a.limits);if(Object.values(l).some(A=>!["string","number","boolean"].includes(typeof A)&&A!==null))throw new f("Oracle server returned invalid limits metadata.",m.API_ERROR);p.limits=Object.fromEntries(Object.entries(l).map(([A,k])=>[u(A),typeof k=="string"?u(k):k]))}return Array.isArray(a.commands)&&(p.commands=Mc(a.commands)),p}function Mc(t){let e=[];for(let n of t.slice(0,50)){let r=L(n);if(typeof r.name!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(r.name)||typeof r.description!="string"||e.some(i=>i.name===r.name))continue;let o=typeof r.args=="string"?u(r.args).slice(0,60).trim():"";e.push({name:r.name,description:u(r.description).slice(0,200),...o?{args:o}:{}})}return e}var ae=class{endpoint;scopes;source;#e;#n;#t;cachedConfig;constructor(e,n=fetch){this.endpoint=Ne(e.endpoint),this.#e=e.getAccessToken??(async()=>e.accessToken),this.#n=e.onIdentity,this.#t=n,this.scopes=new Set(e.scopes),this.source=e.source}async config(e){if(!this.cachedConfig){let n=await this.#e();try{this.cachedConfig=await Ge(this.endpoint,this.#t,n,e)}catch(o){if(this.source!=="oauth"||!(o instanceof le)||o.status!==401)throw o;let i=await this.#e(n);if(i===n)throw o;this.cachedConfig=await Ge(this.endpoint,this.#t,i,e)}let r=this.cachedConfig.identity;r&&this.#n&&await this.#n(r).catch(()=>{})}return this.cachedConfig}async listSessions(){let e=await this.json("GET","/sessions"),n=Array.isArray(e)?e:L(e).sessions;if(!Array.isArray(n))throw new f("Oracle server returned an invalid sessions list.",m.API_ERROR);return n.map(ki)}async createSession(e={},n){let r=await this.json("POST","/sessions",e,n),o=L(r);return!Array.isArray(o.messages)&&typeof o.sessionId=="string"?this.getSession(o.sessionId,n):bi(r)}async getSession(e,n){return bi(await this.json("GET",`/sessions/${encodeURIComponent(e)}`,void 0,n))}async submitTurn(e,n,r,o){let i,s=ke(this.endpoint,`/sessions/${encodeURIComponent(e)}/turns`),a=async d=>this.#t(s,{method:"POST",headers:{Authorization:`Bearer ${d}`,Accept:"text/event-stream, application/json","Content-Type":"application/json"},redirect:"error",body:JSON.stringify(n),...r?{signal:r}:{}});try{let d=await this.#e();if(i=await a(d),i.status===401&&this.source==="oauth"){let p=await this.#e(d);p!==d&&(i=await a(p))}}catch(d){throw d instanceof f||r?.aborted?d:new f(`Oracle connection was interrupted: ${d instanceof Error?d.message:String(d)}`,m.FAILURE,"The server may still be working. Resume this session to replay its saved stream.")}if(o?.(i),i.ok||await mt(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let d=await ht(i,ke(this.endpoint,`/sessions/${e}/turns`)),p=gt(d,i.status);throw new f(`Oracle server did not start a stream (${jc(p)}).`,m.API_ERROR)}return i}async replayEvents(e,n,r){let o=[];for await(let i of this.streamReplayEvents(e,n,r))o.push(i);return o}async*streamReplayEvents(e,n,r,o,i){let s=`/sessions/${encodeURIComponent(e)}/events?streamId=${encodeURIComponent(n)}&from=${Math.max(0,r)}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await mt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*jt(a,o);return}let d=gt(await ht(a,ke(this.endpoint,s)),a.status),p=Array.isArray(d)?d:L(d).events;if(!Array.isArray(p))throw new f("Oracle replay response did not contain events.",m.API_ERROR);for(let l of p)yield vr(l)}async*streamRequestEvents(e,n,r=-1,o,i){let s=`/sessions/${encodeURIComponent(e)}/events?requestId=${encodeURIComponent(n)}&from=${r}`,a=await this.authenticatedFetch("GET",s,{headers:{Accept:"text/event-stream, application/json"},signal:i},0);if(a.ok||await mt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*jt(a,o);return}let d=gt(await ht(a,ke(this.endpoint,s)),a.status),p=Array.isArray(d)?d:L(d).events;if(!Array.isArray(p))throw new f("Oracle request replay response did not contain events.",m.API_ERROR);for(let l of p)yield vr(l)}async cancelTurn(e,n){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{streamId:n})}async cancelRequest(e,n,r){return this.json("POST",`/sessions/${encodeURIComponent(e)}/cancel`,{requestId:n},r)}async upload(e,n){let r=new FormData;r.set("sessionId",e),r.set("file",n,n.name);let o=await this.authenticatedFetch("POST","/uploads",{method:"POST",body:r,headers:{Accept:"application/json"}});return o.ok||await mt(o),Bc(gt(await ht(o,ke(this.endpoint,"/uploads")),o.status))}async download(e,n){let r=await this.authenticatedFetch("GET",`/sessions/${encodeURIComponent(e)}/files/${encodeURIComponent(n)}`,{headers:{Accept:"application/octet-stream, application/json"}});return r.ok||await mt(r),r}async json(e,n,r,o){let i=await this.authenticatedFetch(e,n,{headers:{Accept:"application/json",...r!==void 0?{"Content-Type":"application/json"}:{}},...r!==void 0?{body:JSON.stringify(r)}:{},...o?{signal:o}:{}});return i.ok||await mt(i),gt(await ht(i,ke(this.endpoint,n)),i.status)}async authenticatedFetch(e,n,r={},o=2e4){try{let i=ke(this.endpoint,n),s=Object.fromEntries(new Headers(r.headers).entries()),a=o<=0?void 0:AbortSignal.timeout(o),c=r.signal&&a?AbortSignal.any([r.signal,a]):r.signal??a,d=k=>this.#t(i,{...r,method:e,headers:{...s,Authorization:`Bearer ${k}`},redirect:"error",...c?{signal:c}:{}}),p=await this.#e(),l=await d(p),A=r.body===void 0||typeof r.body=="string"||r.body===null;if(l.status===401&&this.source==="oauth"){let k=await this.#e(p);k!==p&&A&&(l=await d(k))}return l}catch(i){throw i instanceof f?i:new f(`Cannot reach ${this.endpoint}: ${i instanceof Error?i.message:String(i)}`,m.FAILURE,"Check your network, or pass --endpoint for a self-hosted workspace.")}}};async function*jt(t,e){if(!t.body)throw new f("Oracle stream has no response body.",m.FAILURE);let n=t.body.getReader(),r=new TextDecoder,o="",i=0,s=[],a=0,c=-1,d=()=>{if(s.length===0)return null;let p=s.join(`
|
|
263
|
+
`);if(s=[],a=0,p==="[DONE]")return null;let l;try{l=JSON.parse(p)}catch{throw new f("Oracle stream returned invalid JSON.",m.API_ERROR)}let A=vr(l);return A.type==="finalizing"?A:A.idx<=c?null:(c=A.idx,A)};try{for(;;){let{value:l,done:A}=await n.read();l?.byteLength&&e?.();let k=r.decode(l,{stream:!A});if(o+=k,i+=gn(k),i>2*1024*1024)throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR);let x=0,b=o.indexOf(`
|
|
264
|
+
`,x);for(;b>=0;){let g=o.slice(x,b).replace(/\r$/,"");if(i-=gn(o.slice(x,b+1)),x=b+1,g===""){let R=d();R&&(yield R)}else if(!g.startsWith(":")){let R=g.indexOf(":"),P=R<0?g:g.slice(0,R),j=R<0?"":g.slice(R+1).replace(/^ /,"");if(P==="data"&&(s.push(j),a+=gn(j)+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR)}b=o.indexOf(`
|
|
265
|
+
`,x)}if(o=o.slice(x),A)break}if(o.trim()){let l=o.replace(/\r$/,"");if(l.startsWith("data:")&&(s.push(l.slice(5).replace(/^ /,"")),a+=gn(s[s.length-1]??"")+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR)}let p=d();p&&(yield p)}catch(p){throw await n.cancel("Invalid or oversized Oracle event stream.").catch(()=>{}),p}finally{n.releaseLock()}}function gn(t){return Buffer.byteLength(t,"utf8")}function br(t){let e=["program","initiative","codebase"].filter(o=>typeof t[o]=="string");if(e.length>1)throw new f("Choose only one of --program, --initiative or --codebase.",m.USAGE);let n=e[0];if(!n)return;let r=t[n];if(typeof r!="string"||!r.trim())throw new f(`--${n} needs an id.`,m.USAGE);return{kind:n,id:r.trim()}}function L(t){return!t||typeof t!="object"||Array.isArray(t)?{}:t}function jc(t){if(typeof t=="string")return u(t);try{return u(JSON.stringify(t))}catch{return"unexpected response"}}function gt(t,e){let n=L(t);if(n.ok===!1){let r=Oi(n.error,e),o=typeof n.message=="string"?u(n.message).slice(0,2e3):void 0;throw new le(r,e,o)}return n.ok===!0&&"data"in n?n.data:t}async function ht(t,e){try{return await t.json()}catch{throw new f(`Non-JSON response from ${e} (HTTP ${t.status}).`,m.FAILURE)}}async function mt(t){let e;try{e=await t.clone().json()}catch{e=null}let n=L(e),r=Oi(n.error??n.error_description,t.status),o=typeof n.message=="string"?n.message:typeof n.error_description=="string"?n.error_description:typeof n.detail=="string"?n.detail:void 0;throw new le(r,t.status,typeof o=="string"?u(o).slice(0,2e3):void 0,void 0,Dc(t))}function Dc(t){try{return t.url?new URL(t.url).origin:void 0}catch{return}}function Oi(t,e){if(typeof t!="string")return`http_${e}`;let n=u(t).slice(0,120);return/^[A-Za-z0-9_.-]+$/.test(n)?n:`http_${e}`}function ki(t){let e=L(t);if(typeof e.sessionId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(e.sessionId)||typeof e.title!="string"||e.visibility!=="private"&&e.visibility!=="company"||typeof e.shared!="boolean")throw new f("Oracle server returned an invalid session summary.",m.API_ERROR);return{sessionId:e.sessionId,title:u(e.title),visibility:e.visibility,shared:e.shared,...typeof e.entityKey=="string"?{entityKey:u(e.entityKey)}:{},...typeof e.updatedAt=="number"||e.updatedAt===null?{updatedAt:e.updatedAt}:{}}}function bi(t){let e=L(t),n=ki(e);if(!Array.isArray(e.messages))throw new f("Oracle server returned an invalid session transcript.",m.API_ERROR);let r=e.messages.map((o,i)=>{let s=L(o);if(s.role!=="user"&&s.role!=="assistant"||typeof s.content!="string")throw new f(`Oracle session message ${i+1} is invalid.`,m.API_ERROR);let a=Array.isArray(s.attachments)?s.attachments.map(Kc):void 0,c=Array.isArray(s.files)?s.files.map(zc):void 0;return{role:s.role,content:u(s.content),...typeof s.id=="string"?{id:s.id}:{},...typeof s.authorName=="string"?{authorName:u(s.authorName)}:{},...s.question&&typeof s.question=="object"?{question:Wc(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:Gc(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof e.activeStreamId=="string"?{activeStreamId:e.activeStreamId}:{}}}function vr(t){let e=L(t);if(e.protocolVersion!==1||typeof e.idx!="number"||!Number.isSafeInteger(e.idx)||e.idx<0||typeof e.type!="string")throw new f("Oracle stream event does not match protocol version 1.",m.API_ERROR);return be(e)}function Bc(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle upload response is missing file metadata.",m.API_ERROR);return{fileId:e.fileId,name:u(e.name),mediaType:u(e.mediaType),...typeof e.sizeBytes=="number"?{sizeBytes:e.sizeBytes}:{}}}function Wc(t){let e=L(t);if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",m.API_ERROR);return{id:u(e.id),questions:e.questions.map((n,r)=>{let o=L(n);if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,m.API_ERROR);return{id:u(o.id),header:u(o.header),question:u(o.question),multiSelect:o.multiSelect,options:o.options.map((i,s)=>{let a=L(i);if(typeof a.id!="string"||typeof a.label!="string"||typeof a.description!="string")throw new f(`Oracle question option ${s+1} is invalid.`,m.API_ERROR);return{id:u(a.id),label:u(a.label),description:u(a.description),...typeof a.recommended=="boolean"?{recommended:a.recommended}:{},...typeof a.confirms=="boolean"?{confirms:a.confirms}:{}}})}}),optional:e.optional===!0}}function Gc(t){let e=L(t);if(typeof e.questionId!="string"||!Array.isArray(e.answers))throw new f("Oracle returned an invalid question response.",m.API_ERROR);return{questionId:u(e.questionId),answers:e.answers.map(Hc)}}function Hc(t){let e=L(t);if(typeof e.questionId!="string"||!Array.isArray(e.optionIds)||!e.optionIds.every(n=>typeof n=="string"))throw new f("Oracle returned an invalid question answer.",m.API_ERROR);return{questionId:u(e.questionId),optionIds:e.optionIds.map(n=>u(n)),...typeof e.customText=="string"?{customText:u(e.customText).slice(0,8e3)}:{}}}function Kc(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.name!="string"||typeof e.mediaType!="string")throw new f("Oracle session attachment metadata is invalid.",m.API_ERROR);return{fileId:u(e.fileId),name:u(e.name),mediaType:u(e.mediaType),...typeof e.sizeBytes=="number"&&Number.isFinite(e.sizeBytes)?{sizeBytes:e.sizeBytes}:{}}}function zc(t){let e=L(t);if(typeof e.fileId!="string"||typeof e.filename!="string"||typeof e.mediaType!="string"||typeof e.sizeBytes!="number"||!Number.isFinite(e.sizeBytes))throw new f("Oracle session file metadata is invalid.",m.API_ERROR);return{fileId:u(e.fileId),filename:u(e.filename),mediaType:u(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:u(e.format)}:{}}}import{randomUUID as Vc}from"node:crypto";import{promises as Ae}from"node:fs";import{homedir as Yc}from"node:os";import{join as Or}from"node:path";import*as Ri from"proper-lockfile";function wn(){return Or(Yc(),".anyforge")}function yn(){return Or(wn(),"oracle")}function xi(){return Or(wn(),"oracle.last-session")}function Jc(){return{version:2,active:{},logins:[]}}function hn(t){return t===void 0||typeof t=="string"}function Ai(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.endpoint!="string"||typeof e.clientId!="string"||typeof e.redirectUri!="string"||typeof e.accessToken!="string"||typeof e.refreshToken!="string"||typeof e.expiresAt!="number"||!Number.isFinite(e.expiresAt)||!Array.isArray(e.scopes)||!e.scopes.every(n=>typeof n=="string")||!hn(e.companyId)||!hn(e.companyName)||!hn(e.uid)?null:{endpoint:e.endpoint,...e.companyId?{companyId:e.companyId}:{},...e.companyName?{companyName:e.companyName}:{},...e.uid?{uid:e.uid}:{},clientId:e.clientId,redirectUri:e.redirectUri,accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt,scopes:e.scopes}}function Qc(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;if(e.version===1){let o=Ai(e);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(e.version!==2||!Array.isArray(e.logins)||!hn(e.endpoint)||!e.active||typeof e.active!="object"||Array.isArray(e.active))return null;let n=e.logins.map(Ai);if(n.some(o=>o===null))return null;let r={};for(let[o,i]of Object.entries(e.active)){if(typeof i!="string")return null;r[o]=i}return{version:2,...e.endpoint?{endpoint:e.endpoint}:{},active:r,logins:n}}async function ne(){let t=yn(),e;try{e=await Ae.readFile(t,"utf8")}catch(o){if(o.code==="ENOENT")return Jc();throw o}let n;try{n=JSON.parse(e)}catch{throw new Error(`Oracle credentials at ${t} are invalid JSON; run \`anyforge oracle logout --all\` and sign in again.`)}let r=Qc(n);if(!r)throw new Error(`Oracle credentials at ${t} have an unsupported format; run \`anyforge oracle logout --all\` and sign in again.`);return r}async function Dt(t){if(!t.logins.length){await Ae.rm(yn(),{force:!0});return}await Ti(yn(),t)}async function vn(t){return kn(async()=>{let e=await ne(),n=await t(e);return await Dt(e),n})}function Re(t,e){return t.logins.filter(n=>n.endpoint===e)}function bn(t,e){let n=Re(t,e);return n.find(r=>r.clientId===t.active[e])??n[0]}function re(t){return t.companyName||t.companyId||"workspace not known yet"}function Ii(t,e){let n=e.companyId?t.logins.find(r=>r.endpoint===e.endpoint&&r.companyId===e.companyId&&r.clientId!==e.clientId):void 0;return t.logins=[...t.logins.filter(r=>r!==n&&r.clientId!==e.clientId),e],t.active[e.endpoint]=e.clientId,t.endpoint=e.endpoint,n}function kr(t,e){t.logins=t.logins.filter(o=>!(o.endpoint===e.endpoint&&o.clientId===e.clientId));let n=Re(t,e.endpoint);if(!n.length){if(delete t.active[e.endpoint],t.endpoint===e.endpoint){let o=t.logins[0]?.endpoint;o?t.endpoint=o:delete t.endpoint}return}if(t.active[e.endpoint]!==e.clientId&&n.some(o=>o.clientId===t.active[e.endpoint]))return;let r=n[0];return t.active[e.endpoint]=r.clientId,r}function _i(t,e,n,r){let o=t.logins.find(a=>a.endpoint===e&&a.clientId===n);if(!o)return{changed:!1};let i=o.companyId!==r.companyId||r.companyName!==void 0&&o.companyName!==r.companyName||r.uid!==void 0&&o.uid!==r.uid;o.companyId=r.companyId,r.companyName!==void 0&&(o.companyName=r.companyName),r.uid!==void 0&&(o.uid=r.uid);let s=t.logins.find(a=>a!==o&&a.endpoint===e&&a.companyId===r.companyId);return s&&(t.logins=t.logins.filter(a=>a!==s),t.active[e]===s.clientId&&(t.active[e]=o.clientId)),{changed:i||!!s,...s?{duplicate:s}:{}}}function He(t,e){let n=e.trim(),r=t.find(a=>a.companyId===n);if(r)return r;let o=t.filter(a=>a.companyName?.toLowerCase()===n.toLowerCase());if(o.length===1)return o[0];let i=t.map(a=>`${re(a)}${a.companyId&&a.companyName?` (${a.companyId})`:""}`),s=i.length?`Saved workspaces: ${i.join(", ")}.`:"No Oracle logins are saved for this endpoint.";throw o.length>1?new y(`More than one saved workspace is named "${n}". Use its id instead.`,s):new y(`No saved Oracle login for workspace "${n}".`,`${s} Add one with \`anyforge oracle login --workspace "${n}"\`.`)}async function Si(){let t;try{t=await Ae.readFile(xi(),"utf8")}catch(r){if(r.code==="ENOENT")return[];throw r}let e;try{e=JSON.parse(t)}catch{return[]}if(!e||typeof e!="object"||Array.isArray(e))return[];let n=e;return n.version!==2||!Array.isArray(n.sessions)?[]:n.sessions.filter(r=>{if(!r||typeof r!="object")return!1;let o=r;return typeof o.endpoint=="string"&&typeof o.companyId=="string"&&typeof o.sessionId=="string"})}async function Ei(t,e,n=""){let r=(await Si()).filter(o=>!(o.endpoint===e&&o.companyId===n));await Ti(xi(),{version:2,sessions:[...r,{endpoint:e,companyId:n,sessionId:t}]})}async function On(t,e=""){return(await Si()).find(n=>n.endpoint===t&&n.companyId===e)?.sessionId}async function Ti(t,e){await Ae.mkdir(wn(),{recursive:!0,mode:448});let n=`${t}.${Vc()}.tmp`;try{await Ae.writeFile(n,`${JSON.stringify(e,null,2)}
|
|
266
|
+
`,{mode:384,flag:"wx"}),await Ae.rename(n,t),await Ae.chmod(t,384)}finally{await Ae.rm(n,{force:!0}).catch(()=>{})}}async function kn(t){await Ae.mkdir(wn(),{recursive:!0,mode:448});let e=yn(),n=await Ri.lock(e,{realpath:!1,lockfilePath:`${e}.refresh-lock`,stale:6e4,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}});try{return await t()}finally{await n()}}var Ar=8192,$i=10*1048576,tl=20*1048576,Pi=10,Li=3;function Rr(t,e){if(t.type!=="tool_call"&&t.type!=="tool_call_start"||typeof t.spawnId=="string")return!1;let n=typeof t.id=="string"?t.id:void 0;if(n){if(e.has(n))return!1;e.add(n)}return!lt(t.name)}var xe=class{lastIdx=-1;answer="";question;error;done;streamId;files=[];toolIds=new Set;accept(e){return e.type==="finalizing"?!0:e.idx<=this.lastIdx?!1:(this.lastIdx=e.idx,e.type==="stream_start"&&typeof e.streamId=="string"?this.streamId=u(e.streamId):e.type==="delta"&&typeof e.content=="string"?this.answer+=e.content:Rr(e,this.toolIds)?this.answer="":e.type==="question"?this.question=Wt(e):e.type==="error"?this.error=Ir(e):e.type==="done"?this.done=e:e.type==="navigator_file_created"&&nl(this.files,e.file),!0)}get settled(){return!!(this.done||this.error)}outcome(){return{answer:this.answer,...this.question?{question:this.question}:{},...this.error?{error:this.error}:{},...this.done?{done:this.done}:{},...this.streamId?{streamId:this.streamId}:{},...this.files.length?{files:[...this.files]}:{}}}};async function wt(t,e,n,r,o,i=new xe){let s=async l=>{let A=i.streamId;i.accept(l)&&(i.streamId&&i.streamId!==A&&r.onStream?.(i.streamId),await r.onEvent(l))},a=()=>xr(t,e,i,s,o,r.onActivity),c=async()=>{try{for await(let l of t.streamRequestEvents(e,n.requestId,-1,r.onActivity,o))await s(l)}catch(l){throw o?.aborted?l:new f("Oracle connection failed and the submitted request could not be recovered by its request id. It was not resubmitted with a new id.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)}if(i.settled)return i.outcome();if(i.streamId)return a();throw new f("Oracle request lookup returned no stream events. It was not resubmitted with a new id.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the saved session.`)},d=async()=>{try{let l=await t.submitTurn(e,n,o);for await(let A of jt(l,r.onActivity))await s(A)}catch(l){if(o?.aborted||l instanceof f&&l.exitCode===m.API_ERROR)throw l;return i.streamId?a():c()}return i.settled?i.outcome():i.streamId?a():c()},p;try{p=await t.submitTurn(e,n,o)}catch(l){if(o?.aborted||l instanceof f&&l.exitCode!==m.FAILURE)throw l;return d()}if(!p)throw new f("Oracle did not return a stream response.",m.FAILURE);r.onResponse?.();try{for await(let l of jt(p,r.onActivity))await s(l)}catch(l){if(o?.aborted||l instanceof f)throw l;return i.streamId?a():d()}return i.settled?i.outcome():i.streamId?a():d()}async function xr(t,e,n,r,o,i){let s=n.streamId;if(!s)throw new f("Oracle stream replay needs a stream id.",m.FAILURE);let a=0;for(;!n.settled&&a<Li;){if(o?.aborted)throw o.reason;try{for await(let c of t.streamReplayEvents(e,s,n.lastIdx+1,i,o))await r(c);if(!n.settled){if((await t.getSession(e,o)).activeStreamId!==s)break;await Ci(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof f&&c.exitCode===m.API_ERROR||(a+=1,a>=Li))throw c;await Ci(300,void 0,o?{signal:o}:void 0)}}if(!n.settled)throw new f("Oracle stream could not be fully replayed; the turn was not submitted again.",m.FAILURE,`Run anyforge oracle resume ${e} to inspect the session.`);return n.outcome()}function Bt(t,e){let n=e.done,r=n?.servedBy,o=Ke(n?.tokens),i=J(n?.rounds),s=typeof n?.exitReason=="string"?u(n.exitReason):void 0,a=n?.exitReason==="completed"&&!e.question,c=e.files??(Array.isArray(n?.files)?n.files.map(qe):void 0),d=e.error?"error":e.question?"awaiting_input":a?"complete":"interrupted";return{protocolVersion:1,sessionId:t,status:d,answer:u(e.answer),partial:!a,...typeof n?.messageId=="string"?{messageId:u(n.messageId)}:typeof n?.answerId=="string"?{messageId:u(n.answerId)}:{},...e.question?{question:e.question}:{},...e.error?{error:u(e.error)}:{},...e.streamId?{streamId:u(e.streamId)}:{},...r?{servedBy:be(r)}:{},...o?{usage:{...o,...i!==void 0?{rounds:i}:{}}}:{},...i!==void 0?{rounds:i}:{},...s?{exitReason:s}:{},...c?.length?{files:be(c)}:{}}}function T(t,e){return typeof t=="string"?u(t).slice(0,e):""}function Ir(t){return typeof t.message=="string"?u(t.message).slice(0,2e3):"Oracle turn failed."}function z(t){return u(t instanceof Error?t.message:String(t)).slice(0,2e3)}function Wt(t){let e=t.question;if(!e||typeof e!="object")throw new f("Oracle question event omitted its question card.",m.API_ERROR);return rl(e)}function qe(t){let e=t&&typeof t=="object"&&!Array.isArray(t)?t:{};if(typeof e.fileId!="string"||!e.fileId||typeof e.filename!="string"||!e.filename||typeof e.mediaType!="string"||!e.mediaType||typeof e.sizeBytes!="number"||!Number.isSafeInteger(e.sizeBytes)||e.sizeBytes<0)throw new f("Oracle generated-file event omitted safe file metadata.",m.API_ERROR);return{fileId:u(e.fileId),filename:u(e.filename),mediaType:u(e.mediaType),sizeBytes:e.sizeBytes,...typeof e.format=="string"?{format:u(e.format)}:{}}}function nl(t,e){let n=qe(e);t.some(r=>r.fileId===n.fileId)||t.push(n)}function rl(t){let e=t;if(typeof e.id!="string"||!Array.isArray(e.questions))throw new f("Oracle returned an invalid question card.",m.API_ERROR);return{id:u(e.id),optional:e.optional===!0,questions:e.questions.map((n,r)=>{let o=n&&typeof n=="object"?n:{};if(typeof o.id!="string"||typeof o.header!="string"||typeof o.question!="string"||typeof o.multiSelect!="boolean"||!Array.isArray(o.options))throw new f(`Oracle question ${r+1} is invalid.`,m.API_ERROR);return{id:u(o.id),header:u(o.header),question:u(o.question),multiSelect:o.multiSelect,options:o.options.map(i=>{let s=i&&typeof i=="object"?i:{};if(typeof s.id!="string"||typeof s.label!="string"||typeof s.description!="string")throw new f("Oracle question option is invalid.",m.API_ERROR);return{id:u(s.id),label:u(s.label),description:u(s.description),...typeof s.recommended=="boolean"?{recommended:s.recommended}:{},...typeof s.confirms=="boolean"?{confirms:s.confirms}:{}}})}})}}function J(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function Ke(t){if(typeof t=="number"&&Number.isFinite(t))return{totalTokens:t};if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,n=J(e.in)??J(e.input)??J(e.inputTokens),r=J(e.cacheRead),o=J(e.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=J(e.out)??J(e.output)??J(e.outputTokens),a=J(e.total)??J(e.totalTokens)??(i!==void 0||s!==void 0?(i??0)+(s??0):void 0);if(!(i===void 0&&s===void 0&&a===void 0))return{...i!==void 0?{inputTokens:i}:{},...r?{cachedInputTokens:r}:{},...s!==void 0?{outputTokens:s}:{},...a!==void 0?{totalTokens:a}:{}}}function vt(t){if(t&&typeof t=="object"){let e=t;if(typeof e.accepted=="boolean")return e.accepted?"Oracle server accepted the cancellation request.":"Oracle server did not accept the cancellation request.";if(typeof e.cancelled=="boolean")return e.cancelled?"Oracle server cancelled the active turn.":"Oracle server did not cancel the active turn."}return"Oracle server acknowledged the cancellation request."}function bt(t,e){let n=e&&t&&(t.kind==="program"||t.kind==="initiative")?{kind:t.kind,id:t.id}:void 0;return{...!e&&t?{context:t}:{},...n?{sharedEntity:n}:{},visibility:e?"company":"private"}}async function Gt(t,e,n){await Ei(t,e,n).catch(()=>{})}async function An(t,e,n,r){if(!n.length)return[];if(n.length>Pi)throw new y(`Attach no more than ${Pi} files per turn.`);let o=0,i=[];for(let a of n){let c=await Zc(a);if(!c.isFile())throw new y(`Attachment is not a file: ${a}`);if(c.size>$i)throw new y(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let d=await ol(a,$i);if(o+=d.byteLength,o>tl)throw new y("Attachments exceed the 20 MiB total CLI limit.");let p=el(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",l=wi(p)??"";if(!l)throw new y(`Unsupported attachment type: ${a}`);i.push({content:d,name:p,mediaType:l})}let s=[];for(let{content:a,name:c,mediaType:d}of i){await r(u(c),a.byteLength);let p=new ArrayBuffer(a.byteLength);new Uint8Array(p).set(a);let l=new File([p],c,{type:d});s.push(await t.upload(e,l))}return s}async function ol(t,e){let n=[],r=0;for await(let o of Xc(t)){let i=Buffer.isBuffer(o)?o:Buffer.from(o);if(r+=i.byteLength,r>e)throw new y(`Attachment exceeds the ${K(e)} limit: ${t}`);n.push(i)}return Buffer.concat(n)}var Ve=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],sl=2e4,al=6,cl={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},ll={max_rounds:"reached the step limit",max_tokens:"reached the output limit",token_budget:"reached the token budget",deadline:"ran out of time",interrupted:"interrupted",loop_detected:"stopped a repeating loop",cancelled:"cancelled"},Rn=class{constructor(e,n){this.startedAt=e;this.sessionId=n;this.lastSignalAt=e}startedAt;sessionId;phase="sending";answer="";tools=[];notices=[];files=[];lastSignalAt;endedAt;end;done;upload;round;maxRounds;serverPhase;agents=new Map;toolIds=new Set;get toolCount(){return this.tools.length}get failedCount(){return this.tools.filter(e=>e.status==="failed").length}get finished(){return this.end!==void 0}setUploading(e,n,r){this.phase="uploading",this.upload=`${e} (${K(n)})`,this.lastSignalAt=r}setPhase(e,n){this.phase=e,this.lastSignalAt=n}touch(e){this.lastSignalAt=e}apply(e,n){if(this.lastSignalAt=n,this.phase==="cancelling")return this.record(e);switch(e.type){case"stream_start":case"thinking":this.phase!=="finishing"&&(this.phase="thinking");break;case"round_start":this.round=J(e.round),this.maxRounds=J(e.maxRounds)??this.maxRounds;for(let r=this.tools.length-1;r>=0;r--)this.tools[r]?.started&&this.tools.splice(r,1);this.phase!=="finishing"&&(this.phase="thinking");break;case"phase":this.serverPhase=typeof e.phase=="string"?e.phase:void 0;break;case"delta":this.phase!=="finishing"&&(this.phase="writing");break;case"tool_result":this.phase!=="finishing"&&(this.phase="thinking");break;case"finalizing":this.phase="finishing";break}this.record(e)}record(e){switch(e.type){case"delta":typeof e.content=="string"&&(this.answer+=e.content);break;case"tool_call_start":case"tool_call":this.recordToolCall(e);break;case"tool_result":{let n=this.tools.find(r=>r.id!==void 0&&r.id===e.toolUseId);n&&(n.status=e.ok===!1?"failed":"ok");break}case"subagent_start":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"running");break;case"subagent_done":typeof e.spawnId=="string"&&this.agents.set(e.spawnId,"done");break;case"warning":this.notices.push({tone:"warning",text:`Warning: ${T(e.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${T(e.filename,200)}: ${T(e.error,500)}`});break;case"attachment_notice":if(Array.isArray(e.notices))for(let n of e.notices){let r=n&&typeof n=="object"?n:{};this.notices.push({tone:"warning",text:`${T(r.name,160)}: ${T(r.message,500)}`})}break;case"provider_fallback":{let n=T(e.from,120),r=T(e.to,120),o=T(e.reason,40).replaceAll("_"," ");this.notices.push({tone:"info",text:`Switched model${n?` from ${n}`:""}${r?` to ${r}`:""}${o?` (${o})`:""}.`});break}case"navigator_file_created":{let n=qe(e.file);if(this.files.some(r=>r.fileId===n.fileId))break;this.files.push(n),this.notices.push({tone:"info",text:`Generated ${n.filename} (${K(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`});break}case"error":this.notices.push({tone:"error",text:Ir(e)});break;case"done":this.done=e;break}}recordToolCall(e){let n=typeof e.id=="string"?e.id:void 0,r=Rr(e,this.toolIds),o=typeof e.name=="string"?e.name:"";if(lt(o))return;let i=T(typeof e.label=="string"?e.label:ct(o),120)||"tool",s=n?this.tools.find(c=>c.id===n):void 0;if(s){s.label=i,e.type==="tool_call"&&delete s.started;return}let a={label:i,status:"running",...n?{id:n}:{},...typeof e.spawnId=="string"?{spawnId:e.spawnId}:{},...e.type==="tool_call_start"?{started:!0}:{}};r&&this.answer.trim()&&(a.note=u(this.answer.trim()),this.answer=""),this.tools.push(a),this.phase!=="finishing"&&(this.phase="thinking")}note(e,n){this.notices.push({tone:e,text:u(n)})}finish(e,n){this.end=e,this.endedAt=n}label(){if(this.phase==="cancelling")return"Cancelling";if(this.phase==="uploading")return`Uploading ${this.upload??"attachment"}`;if(this.phase==="sending")return"Sending";if(this.phase==="preparing")return"Preparing context";if(this.phase==="finishing")return"Finishing up";let e=[...this.agents.values()].filter(r=>r==="running").length;if(e)return`Researching (${e} agent${e===1?"":"s"})`;let n=this.tools.filter(r=>r.status==="running"&&!r.spawnId);return n.length===1?`Running ${n[0]?.label}`:n.length>1?`Running ${n.length} tools`:this.phase==="writing"?"Writing":this.serverPhase&&cl[this.serverPhase]||"Thinking"}statusLine(e,n){let r=[this.label(),hr(e-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=e-this.lastSignalAt;return o>=sl&&this.phase!=="cancelling"&&r.push(`no update for ${hr(o)}`),n&&e-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let e=[],n=Ut((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=qi("\u2713"):this.end==="awaiting_input"?o=_r("?")+" Waiting for your answer":this.end==="cancelled"?o=Er("\u2717")+" Interrupted":this.end==="error"?o=Sr("\u2717")+" Error":o=Er("\u2717")+` Stopped: ${r&&ll[r]||"no final answer"}`,e.push(n),this.tools.length&&e.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=Ke(this.done?.tokens);(i?.inputTokens!==void 0||i?.outputTokens!==void 0)&&e.push(`${de(i.inputTokens??0)} in${i.cachedInputTokens?` (${de(i.cachedInputTokens)} cached)`:""} / ${de(i.outputTokens??0)} out`);let s=Kt(this.done?.servedBy);return s&&e.push(s),`${o} ${ze(e.join(" \xB7 "))}`}};function Kt(t){if(!t||typeof t!="object")return"";let e=t,n=T(e.model??e.id,100);if(!n)return"";let r=e.fallback&&typeof e.fallback=="object"?e.fallback:void 0;if(r&&typeof r.from=="string"){let o=typeof r.reason=="string"?`: ${T(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${T(r.from,100)}${o})`}return n}var xn=class{constructor(e,n){this.activity=e;this.view=n;this.answer=new pt("",n.linkOrigin)}activity;view;answer;getText(){return this.answer.getText()}invalidate(){this.answer.invalidate()}render(e){let n=this.activity;this.answer.setText(n.answer);let r=[Mt("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=_r(Ve[this.view.frame()%Ve.length]??"*");r.push(Ht(` ${o} ${ze(n.label())}`,e))}r.push(...this.renderTools(e)),r.push(...this.answer.render(e));for(let o of n.notices){let i=o.tone==="error"?Sr:o.tone==="warning"?Er:ze;r.push(...Ni(i(o.text),e))}return n.finished&&r.push(Ht(n.summaryLine(),e)),r}renderTools(e){let{tools:n}=this.activity;if(!n.length)return[];if(this.activity.finished&&!this.view.expanded()){let s=this.activity.failedCount,a=`Used ${n.length} tool${n.length===1?"":"s"}${s?` \xB7 ${s} failed`:""} (ctrl+o to expand)`;return[Ht(ze(` \u23BF ${a}`),e)]}let r=this.activity.finished?n:n.slice(-al),o=n.length-r.length,i=[];return o&&i.push(Ht(ze(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),e)),r.forEach((s,a)=>{if(s.note)for(let l of Ni(s.note,Math.max(10,e-4)).slice(0,3))i.push(` ${ze(il(l))}`);let c=s.status==="running"?_r(Ve[this.view.frame()%Ve.length]??"*"):s.status==="ok"?qi("\u2713"):Sr("\u2717"),d=a===0&&!o?" \u23BF ":" ",p=s.spawnId?` ${ze("agent \xB7")} `:"";i.push(Ht(`${d}${p}${c} ${s.label}`,e))}),i}};import{truncateToWidth as fl,visibleWidth as ml,wrapTextWithAnsi as Ui}from"@earendil-works/pi-tui";import{bold as Mi,cyan as gl,dim as Ie}from"kleur/colors";import{CombinedAutocompleteProvider as dl}from"@earendil-works/pi-tui";var zt=[{name:"help",description:"Show commands and keyboard shortcuts"},{name:"status",description:"Show session, access, model, usage and limits"},{name:"model",description:"Show the workspace model and the model that answered last"},{name:"usage",description:"Show turns and tokens used in this terminal"},{name:"new",args:"[shared]",description:"Start a new conversation",idleOnly:!0,completions:["shared"]},{name:"clear",description:"Clear the screen and start a new private conversation",idleOnly:!0},{name:"resume",args:"[session-id]",description:"Pick or open a saved conversation",idleOnly:!0},{name:"workspace",args:"[name|id]",description:"Switch to another saved workspace and start a new conversation there",idleOnly:!0},{name:"context",args:"[clear|program|initiative|codebase <id>]",description:"Show or change the context for the next message",completions:["clear","program ","initiative ","codebase "]},{name:"attach",args:"<path>",description:"Attach a file to your next message"},{name:"copy",description:"Copy the last answer as Markdown"},{name:"files",description:"List files Oracle generated in this conversation"},{name:"exit",description:"Leave the Oracle",aliases:["quit"]}],ul=[{name:"design",args:"[topic]",description:"Talk through a technical design before anything is created"},{name:"catchup",description:"Catch up on your work: what needs you, what landed, what is running"}];function Tr(t){let e=new Set(zt.flatMap(n=>[n.name,...n.aliases??[]]));return(t??ul).filter(n=>!e.has(n.name))}function Fi(t,e){let n=/^\/([A-Za-z][\w-]*)(?:\s+([\s\S]*))?$/.exec(t.trim());if(!n)return null;let r=(n[1]??"").toLowerCase(),o=(n[2]??"").trim(),i=zt.find(a=>a.name===r||a.aliases?.includes(r));if(i)return{kind:"local",command:i,arg:o,args:o?o.split(/\s+/):[]};let s=e.find(a=>a.name===r);return s?{kind:"server",command:s}:{kind:"unknown",name:r}}function Cr(t,e){let n=[...zt.flatMap(i=>[i,...(i.aliases??[]).map(s=>({...i,name:s}))]).map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{},...i.completions?{getArgumentCompletions:s=>pl(i.completions??[],s)}:{}})),...t.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new dl(n,e),o=/^\s*\/attach\s+/;return{async getSuggestions(i,s,a,c){let d=(i[s]??"").slice(0,a);return s!==0||!d.trimStart().startsWith("/")?null:o.test(d)?r.getSuggestions(i,s,a,{...c,force:!0}):r.getSuggestions(i,s,a,c)},applyCompletion(i,s,a,c,d){return r.applyCompletion(i,s,a,c,d)},shouldTriggerFileCompletion(i,s,a){return s===0&&o.test((i[s]??"").slice(0,a))}}}function pl(t,e){let n=t.filter(r=>r.startsWith(e.toLowerCase())&&r!==e).map(r=>({value:r,label:r.trim()}));return n.length?n:null}function Vt(t,e){return t?e?"write + approve":"write":"read only"}function Ot(t){return t?`${t.kind} ${u(t.id)}`:"no context"}function Fe(t){return t?u(t.companyName||t.companyId):""}function ji(t,e){let n=[`${Mi(gl("\u26A1\uFE0E AnyForge Oracle"))}${Ie(` \xB7 v${u(t.version)}`)}`,`${t.workspace?`${Mi(t.workspace)} \xB7 `:""}${Oe(t.session)} \xB7 ${t.access} \xB7 ${Ot(t.context)}`,...t.session.messages.length?[`Resumed: ${u(t.session.title)}`]:[],Ie("Only your messages and the files you attach leave this terminal."),Ie("/help commands \xB7 /status details")];if(e<24)return n.flatMap(s=>Ui(s,Math.max(1,e)));let r=Math.min(e,76),o=r-4,i=[Ie(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>Ui(a,o))){let a=fl(s,o);i.push(`${Ie("\u2502")} ${a}${" ".repeat(Math.max(0,o-ml(a)))} ${Ie("\u2502")}`)}return i.push(Ie(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function Di(t){let e=(r,o)=>r?`${Ie(r)} ${o}`:o,n=[{title:"Commands",rows:zt.map(r=>[`/${r.name}`,e(r.args,r.aliases?.length?`${r.description} (also /${r.aliases.join(", /")})`:r.description)])}];if(t.server.length){let r=t.server.map(o=>[`/${o.name}`,e(o.args,u(o.description))]);t.server.some(o=>o.name==="catchup")&&!t.canApprove&&r.push(["",Ie("Acting on /catchup decisions needs a session started with `anyforge oracle --write --approve`.")]),n.push({title:"Workspace commands (answered by Oracle)",rows:r})}return n.push({title:"Keys",rows:[["Enter","Send. While Oracle works, queues one message for when it finishes"],["Shift+Enter","New line (Ctrl+J also works)"],["Esc","Interrupt the current answer"],["Ctrl+C","Interrupt the answer, or clear the input; press twice to exit"],["Ctrl+D","Exit when the input is empty"],["Ctrl+O","Show or hide tool activity for finished answers"],["Ctrl+Shift+F","Search the conversation"],["Up / Down","Recall messages sent in this terminal"],["Tab","Complete a command, or a file path after /attach"],["PgUp / PgDn","Scroll; Home and End jump to the top and bottom"]]}),n}function $r(t){let n=[["Model",`workspace default ${t.model?`${u(t.model.provider)}/${u(t.model.id)}`:"chosen by the server"}`]],r=Kt(t.lastServedBy);if(r){let o=t.lastServedBy,i=T(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function Pr(t){return[["This run",t.turns?`${t.turns} turn${t.turns===1?"":"s"} \xB7 ${t.tools} tool${t.tools===1?"":"s"} \xB7 ${de(t.inputTokens)} in / ${de(t.outputTokens)} out tokens`:"no turns yet"]]}function Lr(t){if(!t?.finished)return[];let e=[Ut((t.endedAt??t.startedAt)-t.startedAt)];e.push(`${t.toolCount} tool${t.toolCount===1?"":"s"}${t.failedCount?` (${t.failedCount} failed)`:""}`);let n=J(t.done?.rounds);n!==void 0&&e.push(`${n} round${n===1?"":"s"}`);let r=Ke(t.done?.tokens);(r?.inputTokens!==void 0||r?.outputTokens!==void 0)&&e.push(`${de(r.inputTokens??0)} in${r.cachedInputTokens?` (${de(r.cachedInputTokens)} cached)`:""} / ${de(r.outputTokens??0)} out tokens`);let o=T(t.done?.exitReason,40);return e.push(o?o.replaceAll("_"," "):t.end??""),[["Last turn",e.filter(Boolean).join(" \xB7 ")]]}function Bi(t){let e=t.capabilities?["read","write","approve"].filter(o=>t.capabilities?.[o]).join(", ")||"none":"not reported",n=t.contextText?` \xB7 note ${K(Buffer.byteLength(t.contextText,"utf8"))}`:"",r=li(t.limits);return[["Session",`${t.session.sessionId} \xB7 ${u(t.session.title)||"Untitled"}`],["Audience",Oe(t.session)],["Access",`this session ${Vt(t.write,t.approve)} \xB7 grant ${t.grantScopes.join(", ")||"none"} \xB7 workspace role ${e}`],["Context",`${Ot(t.context)}${n}`],...$r(t),...Lr(t.lastTurn),...Pr(t.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...t.identity?[["Workspace",hl(t.identity,t.savedWorkspaces)]]:[],["Endpoint",t.endpoint],["Version",`CLI ${u(t.version)} \xB7 protocol ${1}`],["Attached",t.pendingFiles.length?`${t.pendingFiles.map(u).join(", ")} (uploads with your next message)`:"nothing waiting"]]}function hl(t,e){let n=t.companyName?`${u(t.companyName)} (${u(t.companyId)})`:`company ${u(t.companyId)}`,r=e!==void 0&&e>1?` \xB7 ${e} saved, /workspace to switch`:"";return`${n} \xB7 user ${u(t.uid)}${r}`}function Wi(t,e){return e.length?e.map((n,r)=>[`${r+1}.`,`${u(n.filename)} (${K(n.sizeBytes)}) \xB7 anyforge oracle download ${t} ${u(n.fileId)} --output .`]):[["Files","Oracle has not generated files in this conversation."]]}import{randomUUID as bl}from"node:crypto";import{stat as Ol}from"node:fs/promises";import{basename as Gi}from"node:path";import{bold as kl,cyan as Hi,dim as Ki,red as At,yellow as Al}from"kleur/colors";import{Container as Rl,Editor as xl,ProcessTerminal as Il,ScrollView as _l,SelectList as zi,TuiAltScreen as Sl,VStack as El,getNativeClipboard as Tl,matchesKey as Sn,truncateToWidth as Vi,wrapTextWithAnsi as Cl}from"@earendil-works/pi-tui";import{bold as yl,cyan as Nr,dim as In,yellow as wl}from"kleur/colors";import{Input as vl,Text as Ye,matchesKey as kt}from"@earendil-works/pi-tui";var _n=class{constructor(e,n,r){this.tui=e;this.question=n;this.onComplete=r;(n.questions[0]?.options.length??0)===0&&(this.mode="custom"),this.customInput.onSubmit=o=>{let i=o.trim().slice(0,8e3);if(!i){this.errorMessage="Enter a short answer before continuing.",this.tui.requestRender();return}this.recordCurrent(i)},this.customInput.onEscape=()=>{if(!this.question.questions[this.questionIndex]?.options.length){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.setValue(""),this.mode="select",this.tui.setFocus(this),this.tui.requestRender()}}tui;question;onComplete;focused=!1;questionIndex=0;optionIndex=0;mode="select";errorMessage="";selected=new Map;answers=[];customInput=new vl({prompt:`${Nr("Your answer: ")} `,placeholder:"Write a short answer and press Enter",placeholderStyle:In});invalidate(){}render(e){let n=this.question.questions[this.questionIndex];if(!n)return[];this.customInput.focused=this.focused&&this.mode==="custom";let r=Math.max(12,e-2),o=this.question.questions.length>1?` \xB7 ${this.questionIndex+1}/${this.question.questions.length}`:"",i=[...new Ye(`${yl(Nr(u(n.header)))}${In(o)}`,0,0).render(r),...new Ye(u(n.question),0,0).render(r),...this.renderOptions(n,r)];return this.mode==="custom"?(i.push(...this.customInput.render(r)),i.push(...new Ye(In(`Enter submits your text${n.options.length?" \xB7 Esc back to the choices":this.question.optional?" \xB7 Esc skip":""}`),0,0).render(r))):i.push(...new Ye(In(this.instructions(n)),0,0).render(r)),this.errorMessage&&i.push(...new Ye(wl(this.errorMessage),0,0).render(r)),i.map(s=>`${Nr("\u2502")} ${s}`)}handleInput(e){if(this.mode==="custom"){this.customInput.handleInput(e);return}let n=this.question.questions[this.questionIndex];if(!n)return;let r=this.selectedFor(n);if(n.options.length===0){if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(kt(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(e);return}if(kt(e,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(kt(e,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(kt(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}if(e.toLowerCase()==="s"&&this.question.optional){this.finish(null);return}if(e.toLowerCase()==="o"){this.mode="custom",this.tui.setFocus(this),this.tui.requestRender();return}let o=Number(e);if(Number.isInteger(o)&&o>=1&&o<=n.options.length){let i=n.options[o-1]?.id;i&&(this.optionIndex=o-1,n.multiSelect?r.has(i)?r.delete(i):r.add(i):(r.clear(),r.add(i)),this.tui.requestRender());return}if(kt(e,"space")&&n.multiSelect){let i=n.options[this.optionIndex];i&&(r.has(i.id)?r.delete(i.id):r.add(i.id),this.tui.requestRender());return}if(kt(e,"enter")){if(r.size===0){this.errorMessage="Select an option explicitly, type a custom answer, or skip if optional.",this.tui.requestRender();return}this.recordCurrent()}}renderOptions(e,n){let r=this.selectedFor(e),o=[];return e.options.forEach((i,s)=>{let a=r.has(i.id),c=e.multiSelect?a?"[x]":"[ ]":a?"(x)":"( )",d=s===this.optionIndex?"\u203A":" ",p=i.recommended?" \xB7 recommended":"",l=i.confirms?" \xB7 confirms the shown action":"",A=`${d} ${s+1}. ${c} ${u(i.label)}${p}${l}`;o.push(...new Ye(A,0,0).render(n)),o.push(...new Ye(` ${u(i.description)}`,0,0).render(n))}),o}instructions(e){return e.options.length===0?`Write a short answer${this.question.optional?" \xB7 s or Esc skip card":""}`:`\u2191/\u2193 move \xB7 1\u2013${e.options.length} ${e.multiSelect?"toggle":"select"}${e.multiSelect?" \xB7 Space toggle":""} \xB7 Enter continue \xB7 o write your own${this.question.optional?" \xB7 s or Esc skip card":""}`}requireAnswer(){let e=this.question.questions[this.questionIndex];this.errorMessage=e?.options.length?"Oracle needs an answer to continue: pick an option, or press o to write your own.":"Oracle needs an answer to continue: type it and press Enter.",this.tui.requestRender()}selectedFor(e){let n=this.selected.get(e.id);return n||(n=new Set,this.selected.set(e.id,n)),n}recordCurrent(e){let n=this.question.questions[this.questionIndex];if(!n)return;e&&this.selectedFor(n).clear();let r=[...this.selectedFor(n)],o={questionId:n.id,optionIds:r,...e?{customText:e}:{}},i=this.answers.findIndex(s=>s.questionId===n.id);if(i>=0?this.answers.splice(i,1,o):this.answers.push(o),this.customInput.setValue(""),this.mode="select",this.questionIndex<this.question.questions.length-1){this.questionIndex+=1,this.optionIndex=0,this.mode=this.question.questions[this.questionIndex]?.options.length===0?"custom":"select",this.errorMessage="",this.tui.setFocus(this),this.tui.requestRender();return}this.finish({questionId:this.question.id,answers:this.answers.map(s=>({...s}))})}finish(e){this.onComplete(e)}};var Yi=1500,$l=100,Pl="\x1B]133;A\x07";function Rt(t){return{render:t,invalidate(){}}}function Ji(t,e,n,r){let o=new pt(e,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>t==="user"?gi(o,i,n).map((s,a)=>a===1?`${Pl}${s}`:s):[Mt("assistant"),...o.render(i)]}}async function Qi(t){let{client:e,config:n}=t,r=new Il,o=new Sl(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new Rl,s=new _l(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new xl(o,mi),c=Tr(n.commands),d=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,p=t.session,l=t.initialContext,A=t.contextText,k=t.canWrite,x=t.canApprove,b,g,R,P,j=[...t.initialFiles],G=!1,he=!1,Ze=!1,Dn=0,et,Br=0,Jt=!1,Ue="",_t,Qt,tt=[],nt={turns:0,inputTokens:0,outputTokens:0,tools:0},Xt=new Map,ye,_s=Rt(h=>{if(b){let w=Hi(Ve[Dn%Ve.length]??"*");return[Vi(`${w} ${b.activity.statusLine(Date.now(),d())}`,h)]}return ye?[Vi(`${Hi("?")} Waiting for your answer to the question above`,h)]:[]}),Ss=Rt(h=>{let w=[Fe(n.identity),Vt(k,x),Ot(l),hi(p),j.length?`${j.length} file${j.length===1?"":"s"} attached`:"",g!==void 0?"1 message queued":""];return[Ki(ui(h,w,b?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new El([{component:s,basis:0,grow:1,minSize:1},{component:_s,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:Ss,basis:"auto",shrink:0,minSize:1}]));let oe=()=>o.requestRender(),F=h=>o.flash(u(h),3e3),Ee=h=>{i.addChild(h),oe()},V=(h,w=Ki)=>{let v=u(h);Ee(Rt(O=>Cl(w(v),Math.max(1,O))))},Zt=(h,w)=>{Ee(Rt(v=>["",...di(h,w,v)]))},Es=()=>{et||(et=setInterval(()=>{Dn+=1,oe()},$l),et.unref())},Bn=()=>{et&&clearInterval(et),et=void 0},rt=()=>{G||o.hasOverlay()||o.setFocus(ye??a)},Wr=h=>{R=h;let w=Xt.get(h.id);w&&i.removeChild(w);let v=new _n(o,h,O=>{Xt.delete(h.id),ye===v&&(ye=void 0),i.removeChild(v),$s(O??{questionId:h.id,skipped:!0,answers:[]}),rt()});Xt.set(h.id,v),ye=v,Ee(v)},en=h=>{i.clear(),Xt.clear(),ye=void 0,p=h,R=void 0,P=void 0,tt=[],Ue="";let w={version:t.version,workspace:Fe(n.identity),session:h,access:Vt(k,x),context:l};Ee(Rt(O=>ji(w,O)));let v;for(let O of h.messages){Ee(Ji(O.role,O.content,O.authorName,e.endpoint)),O.role==="assistant"&&(Ue=O.content),O.question&&(v=O.question),O.questionResponse&&(V("A stored response was recorded for this question."),v?.id===O.questionResponse.questionId&&(v=void 0));for(let I of O.files??[])tt.some(C=>C.fileId===I.fileId)||tt.push(I),V(`File: ${I.filename} (${I.mediaType}, ${K(I.sizeBytes)}) \xB7 anyforge oracle download ${h.sessionId} ${I.fileId} --output .`)}v&&Wr(v),rt(),Gt(h.sessionId,e.endpoint,n.identity?.companyId)},Gr,Ts=new Promise(h=>{Gr=h}),tn=()=>{G||(G=!0,Bn(),b?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),Gr())},Wn=async()=>{let h=b;if(!h||h.cancelling)return;h.cancelling=!0,h.activity.setPhase("cancelling",Date.now()),oe();let{streamId:w,requestId:v}=h;h.controller.abort(new Error("Turn cancelled by user."));let O;if(w)try{O=vt(await e.cancelTurn(p.sessionId,w))}catch(I){O=`Could not confirm server cancellation: ${z(I)}`}else if(v)try{O=vt(await e.cancelRequest(p.sessionId,v))}catch(I){O=`Could not confirm server cancellation by request id: ${z(I)}`}else O="The turn had not reached the server; nothing was sent.";h.activity.note("info",O),oe()},ot=async h=>{if(b||G)return;if(h.kind==="submit"&&h.response){let $=R;if(!$||$.id!==h.response.questionId){F("That question is no longer active. Resume the session to refresh it.");return}V(Ll($,h.response)),R=void 0}else h.kind==="submit"&&Ee(Ji("user",h.text));let w=p.sessionId,v=new Rn(Date.now(),w),O=new xn(v,{expanded:()=>Ze,frame:()=>Dn,linkOrigin:e.endpoint}),I={activity:v,view:O,controller:new AbortController,cancelling:!1};b=I,Ee(O),Es();let C=new xe,Q=$=>{v.apply($,Date.now()),$.type==="question"&&(Wr(Wt($)),rt()),oe()},Me={onEvent:Q,onStream:$=>{I.streamId=$},onResponse:()=>{v.phase==="sending"&&v.setPhase("preparing",Date.now())},onActivity:()=>v.touch(Date.now())},ie="error";try{if(h.kind==="replay"){C.streamId=h.streamId,I.streamId=h.streamId,v.setPhase("thinking",Date.now());let $=await xr(e,w,C,async ue=>{C.accept(ue)&&Q(ue)},I.controller.signal,Me.onActivity);ie=Bt(w,$).status}else{let $=h.response?[]:j.splice(0),ue=await An(e,w,$,(Gn,Bs)=>{v.setUploading(Gn,Bs,Date.now()),oe()});I.controller.signal.throwIfAborted(),v.setPhase("sending",Date.now());let nn=bl();I.requestId=nn;let js={requestId:nn,text:h.response?"":h.text,readOnly:!k,...x?{allowApprove:!0}:{},...l?{context:l}:{},...A?{contextText:A}:{},...h.response?{questionResponse:h.response}:{},...ue.length?{attachments:ue.map(Gn=>({fileId:Gn.fileId}))}:{}},Ds=await wt(e,w,js,Me,I.controller.signal,C);ie=Bt(w,Ds).status}}catch($){if(I.controller.signal.aborted)ie="cancelled";else{ie="error";let ue=$ instanceof f&&$.hint?` ${u($.hint)}`:"";v.note("error",`${z($)}${ue}`)}}finally{v.finish(ie,Date.now()),b=void 0,Bn(),G||(he=ie==="cancelled"),_t=v,v.answer.trim()&&(Ue=v.answer),v.done?.servedBy&&(Qt=v.done.servedBy);let $=Ke(v.done?.tokens);nt.turns+=1,nt.tools+=v.toolCount,nt.inputTokens+=$?.inputTokens??0,nt.outputTokens+=$?.outputTokens??0;for(let ue of v.files)tt.some(nn=>nn.fileId===ue.fileId)||tt.push(ue);oe()}if(!G){if(I.streamId&&ie!=="cancelled"){let $=await e.getSession(w).catch(()=>{});$&&$.sessionId===p.sessionId&&(p=$)}Cs(ie)}},Cs=h=>{if(!(G||b)){if(P){let w=P;P=void 0,ot({kind:"submit",text:"",response:w});return}if(g!==void 0){let w=g;if(g=void 0,h==="complete"&&!ye){ot({kind:"submit",text:w});return}a.getText()||a.setText(w),F("Your queued message is back in the input; press Enter to send it.")}rt(),oe()}},$s=h=>{if(b){P=h,F("Answer saved; Oracle continues once the current turn settles.");return}ot({kind:"submit",text:"",response:h})},Hr=async h=>{try{let w=await e.getSession(h);en(w),w.activeStreamId&&ot({kind:"replay",streamId:w.activeStreamId}),F(`Resumed ${w.title||w.sessionId}.`)}catch(w){V(z(w),At)}},Ps=async()=>{try{let h=await e.listSessions();if(!h.length){F("No saved conversations to resume.");return}let w=new zi(h.map(C=>({value:C.sessionId,label:C.title,description:Oe(C)})),Math.min(12,h.length),ft),v=await On(e.endpoint,n.identity?.companyId),O=v?h.findIndex(C=>C.sessionId===v):-1;O>=0&&w.setSelectedIndex(O);let I=o.showOverlay(w,{width:"85%",maxHeight:"80%",anchor:"center"});w.onSelect=C=>{I.hide(),Hr(C.value)},w.onCancel=()=>{I.hide(),rt()},I.focus()}catch(h){V(z(h),At)}},Kr=async h=>{if(h&&(!l||l.kind!=="program"&&l.kind!=="initiative")){F("Use /context program <id> or /context initiative <id> before /new shared.");return}try{let w=await e.createSession(bt(l,h));en(w),F(`Started a new conversation \xB7 ${Oe(w)}.`)}catch(w){V(z(w),At)}},zr=async(h,w)=>{let v=u(re(w));if(w.companyId&&w.companyId===n.identity?.companyId){F(`Already in ${v}.`);return}Jt=!0;try{F(`Connecting to ${v}...`);let O=await h.connect(w),I=await O.client.createSession(bt(void 0,!1));e=O.client,n=O.config,c=Tr(n.commands),a.setAutocompleteProvider(Cr(c,process.cwd()));let C=e.scopes.has("write")&&n.capabilities?.write===!0,Q=e.scopes.has("approve")&&n.capabilities?.approve===!0,Me=k&&!C||x&&!Q;k&&!C&&(k=!1),(!k||!Q)&&(x=!1);let ie=l!==void 0;l=void 0,Qt=void 0,_t=void 0,en(I),V(`Switched to ${Fe(n.identity)||v}. This is a new conversation in that workspace.${ie?" Context was cleared because its id belongs to the previous workspace.":""}`),Me&&V(`Access here is ${Vt(k,x)}: this workspace's login or your role there does not allow more.`,Al)}catch(O){let I=O instanceof f&&O.hint?` ${u(O.hint)}`:"";V(`Could not switch to ${v}: ${z(O)}${I}`,At)}finally{Jt=!1}},Ls=async(h,w)=>{let v=n.identity?.companyId,O=new zi(w.map(Q=>({value:Q.clientId,label:re(Q),description:[Q.companyId,Q.scopes.join(", "),Q.companyId===v?"current":""].filter(Boolean).join(" \xB7 ")})),Math.min(12,w.length),ft),I=w.findIndex(Q=>Q.companyId===v);I>=0&&O.setSelectedIndex(I);let C=o.showOverlay(O,{width:"85%",maxHeight:"80%",anchor:"center"});O.onSelect=Q=>{C.hide();let Me=w.find(ie=>ie.clientId===Q.value);Me&&zr(h,Me)},O.onCancel=()=>{C.hide(),rt()},C.focus()},Ns=async h=>{let w=t.workspaces;if(!w){V(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${Fe(n.identity)||"one workspace"}. To switch workspaces, unset it and sign in with \`anyforge oracle login --workspace <name>\`.`);return}let v=await w.list();if(h){let O;try{O=He(v,h)}catch(I){let C=I instanceof f&&I.hint?` ${u(I.hint)}`:"";V(`${z(I)}${C}`,At);return}await zr(w,O);return}if(v.length<=1){V(`${Fe(n.identity)||"This workspace"} is the only saved workspace. To add another, run \`anyforge oracle login --workspace <name>\` in another terminal, then use /workspace here.`);return}await Ls(w,v)},qs=h=>({session:p,write:k,approve:x,grantScopes:[...e.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...l?{context:l}:{},...A?{contextText:A}:{},...n.model?{model:n.model}:{},lastServedBy:Qt,..._t?{lastTurn:_t}:{},totals:{...nt},...n.limits?{limits:n.limits}:{},...n.identity?{identity:n.identity}:{},...h!==void 0?{savedWorkspaces:h}:{},endpoint:e.endpoint,version:t.version,pendingFiles:j.map(w=>Gi(w))}),Fs=async()=>{if(!Ue.trim()){F("There is no answer to copy yet.");return}try{let h=Tl();h?.setText?await h.setText(Ue):r.write(`\x1B]52;c;${Buffer.from(Ue,"utf8").toString("base64")}\x07`),F(`Copied the last answer (${Ue.length} characters).`)}catch(h){F(`Could not copy: ${z(h)}`)}},Us=async(h,w,v)=>{if(h.idleOnly&&Jt){F("Wait for the workspace switch to finish.");return}if(h.idleOnly&&b){F(`Wait for Oracle to finish, or press Esc, before /${h.name}.`);return}switch(h.name){case"help":Ee(Rt(O=>["",...Di({server:c,canApprove:x}).flatMap(I=>[kl(I.title),...yr(I.rows,O)])]));return;case"status":{let O=await t.workspaces?.list().then(I=>I.length).catch(()=>{});Zt("Status",Bi(qs(O)));return}case"workspace":await Ns(v);return;case"model":Zt("Model",$r({...n.model?{model:n.model}:{},lastServedBy:Qt}));return;case"usage":Zt("Usage",[...Lr(_t),...Pr(nt)]);return;case"files":Zt("Generated files",Wi(p.sessionId,tt));return;case"copy":await Fs();return;case"exit":tn();return;case"new":if(v&&v!=="shared"){F("Usage: /new or /new shared");return}await Kr(v==="shared");return;case"clear":await Kr(!1);return;case"resume":v?await Hr(v):await Ps();return;case"context":{if(!v){V(`Context: ${Ot(l)}${A?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(v==="clear")l=void 0;else{let[O,I,...C]=w;if(!["program","initiative","codebase"].includes(O??"")||!I||C.length){F("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}l={kind:O,id:I}}V(`Context for your next message: ${Ot(l)}.`);return}case"attach":{if(!v){F("Usage: /attach <file path>");return}let O=await Ol(v).catch(()=>{});if(!O?.isFile()){F(`Not a readable file: ${v}`);return}j.push(v),V(`Attached ${Gi(v)} (${K(O.size)}). It uploads with your next message.`);return}}},Vr=h=>{let w=h.trim();if(!w)return;a.addToHistory(w);let v=Fi(w,c);if(v?.kind==="local"){Us(v.command,v.args,v.arg).catch(O=>V(z(O),At));return}if(v?.kind==="unknown"){a.setText(w),F(`Unknown command /${v.name}. Type /help to see the commands.`);return}if(Jt){a.setText(w),F("Switching workspace; send your message once it has opened.");return}if(b){let O=g!==void 0;g=w,F(O?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),oe();return}ot({kind:"submit",text:w})},Ms=o.addInputListener(h=>{if(Sn(h,"ctrl+c")){if(b)return Wn(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),oe(),{consume:!0};let w=Date.now();return w-Br<=Yi?tn():(Br=w,o.flash("Press Ctrl+C again to exit",Yi)),{consume:!0}}if(Sn(h,"escape")){let w=ye!==void 0&&o.getFocusedComponent()===ye;return b&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!w?(Wn(),{consume:!0}):void 0}if(Sn(h,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(tn(),{consume:!0}):void 0;if(Sn(h,"ctrl+o"))return Ze=!Ze,F(Ze?"Showing tool activity":"Hiding tool activity"),oe(),{consume:!0}}),Yr=()=>{let h=!!b;Wn().finally(()=>{h&&(he=!0),tn()})};process.on("SIGINT",Yr),a.onSubmit=Vr,a.onChange=()=>oe(),a.setAutocompleteProvider(Cr(c,process.cwd())),en(t.session),o.start(),p.activeStreamId&&ot({kind:"replay",streamId:p.activeStreamId}),t.initialPrompt&&Vr(t.initialPrompt),await Ts,Ms(),process.off("SIGINT",Yr),Bn(),b?.controller.abort(),await r.drainInput().catch(()=>{}),he&&(process.exitCode=130)}function Ll(t,e){if(e.skipped)return"Skipped the optional Oracle question.";let n=[];for(let r of e.answers){let o=t.questions.find(i=>i.id===r.questionId);for(let i of r.optionIds){let s=o?.options.find(a=>a.id===i);s&&n.push(s.label)}r.customText&&n.push(r.customText)}return`You answered: ${n.map(u).join("; ")||"no options selected"}`}import{createHash as Nl,randomBytes as Xi,timingSafeEqual as ql}from"node:crypto";import{createServer as Fl}from"node:http";import Ul from"open";var $n="https://crew.anyforge.ai",Ml=6e4,jl=5*6e4,En=new Map;async function Je(t){t.signal?.throwIfAborted();let e=Ne(t.endpoint),n=await Ge(e,fetch,void 0,t.signal),r=["read"];t.write&&r.push("write"),t.approve&&(r.includes("write")||r.push("write"),r.push("approve"));let o=Xi(32).toString("base64url"),i=await Gl(o,t.signal);try{let s={client_name:"AnyForge CLI",redirect_uris:[i.redirectUri],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"},a=await Hl(n.oauth.registration,s,t.signal);if(typeof a.client_id!="string"||!a.client_id)throw new f("OAuth registration did not return a client id.",m.API_ERROR);let c=Xi(32).toString("base64url"),d=Nl("sha256").update(c).digest("base64url"),p={client_id:a.client_id,redirect_uri:i.redirectUri,response_type:"code",scope:r.join(" "),state:o,code_challenge:d,code_challenge_method:"S256",resource:n.resource},l=new URL(n.oauth.authorization);for(let[G,he]of Object.entries(p))l.searchParams.set(G,String(he));let A=t.workspace?.trim();A&&l.searchParams.set(vi,A),t.onAuthorizationUrl?.(l.toString()),t.signal?.throwIfAborted();try{t.openBrowser?await t.openBrowser(l.toString()):await Ul(l.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
|
|
267
|
+
${l.toString()}
|
|
268
|
+
`)}let k=await i.wait();if(!is(k.state,o))throw new f("Oracle sign-in callback state did not match; authorization was rejected.",m.API_ERROR);if(k.error)throw new f(`Oracle authorization failed: ${u(k.error)}${k.errorDescription?` (${u(k.errorDescription)})`:""}`,m.API_ERROR);if(!k.code)throw new f("Oracle authorization callback did not include a code.",m.API_ERROR);let x={client_id:a.client_id,grant_type:"authorization_code",code:k.code,redirect_uri:i.redirectUri,code_verifier:c,resource:n.resource},b=await cs(n.oauth.token,x,t.signal);as(b);let g=ss(b.scope);if(!g.includes("read")||g.some(G=>!r.includes(G)))throw await Cn(n.oauth.revocation,a.client_id,b.access_token,b.refresh_token).catch(()=>{}),new f("The granted Oracle scopes are invalid for this request.",m.API_ERROR);let R=await Ge(e,fetch,b.access_token,t.signal).then(G=>G.identity).catch(()=>{});t.signal?.throwIfAborted();let P={endpoint:e,...es(R),clientId:a.client_id,redirectUri:i.redirectUri,accessToken:b.access_token,refreshToken:b.refresh_token,expiresAt:Date.now()+b.expires_in*1e3,scopes:g},j=await vn(G=>Ii(G,P));return j&&await Cn(n.oauth.revocation,j.clientId,j.accessToken,j.refreshToken).catch(()=>{}),P}finally{await i.close()}}function es(t){return t?{companyId:t.companyId,...t.companyName?{companyName:t.companyName}:{},uid:t.uid}:{}}async function ts(t={}){if(t.workspace&&!t.all){let n=Yt(await ne(),t.endpointOverride);n&&await qr(n)}let e=await kn(async()=>{let n=await ne(),r=[];if(t.all)r=[...n.logins];else{let s=Yt(n,t.endpointOverride);t.workspace?r=[He(s?Re(n,s):[],t.workspace)]:s&&(r=[bn(n,s)].filter(a=>!!a))}let o=[],i;for(let s of r){try{await ns(s)}catch(a){o.push({login:s,error:a})}i=kr(n,s)??i}return await Dt(n),{removed:r,failures:o,...i&&!t.all?{nowActive:i}:{}}});if(e.failures.length){let n=e.failures.map(({login:r,error:o})=>{let i=o instanceof Error?u(o.message):"unknown error";return e.failures.length>1||t.all?`${u(re(r))}: ${i}`:i});throw new f(`Local Oracle credentials were cleared, but server revocation failed for ${e.failures.length===1?"one login":`${e.failures.length} logins`}: ${n.join("; ")}`,m.FAILURE,"Run `anyforge oracle login` again if you still need access. The server will expire these access tokens automatically.")}return{removed:e.removed,...e.nowActive?{nowActive:e.nowActive}:{}}}async function ns(t){let e=await Ge(t.endpoint);await Cn(e.oauth.revocation,t.clientId,t.accessToken,t.refreshToken)}function Yt(t,e){let n=e||process.env.ANYFORGE_ENDPOINT;return n?Ne(n):t.endpoint}function rs(){return new y("ANYFORGE_API_TOKEN is set, so Oracle uses that key and its one workspace; saved Oracle logins are not used.","Unset ANYFORGE_API_TOKEN to choose between your saved workspaces.")}async function Pn(t,e){let n=t||process.env.ANYFORGE_ENDPOINT;if(process.env.ANYFORGE_API_TOKEN){if(e)throw rs();return Zi(n)}let r=await ne(),o=Yt(r,n);if(e)return _e(await Dl(o,e));let i=o?bn(r,o):void 0;if(i)return _e(i);try{return await Zi(n)}catch(s){throw r.logins.length&&n?new fe:s instanceof fe?new fe:s}}async function Dl(t,e){if(!t)return He([],e);let n=await Ln(t);return He(n,e)}function _e(t){return{endpoint:t.endpoint,accessToken:t.accessToken,scopes:t.scopes.filter(Fr),source:"oauth",getAccessToken:async e=>Bl(t.endpoint,t.clientId,e),onIdentity:async e=>{if(t.companyId===e.companyId&&(e.companyName===void 0||t.companyName===e.companyName)&&t.uid===e.uid)return;let r=await vn(i=>_i(i,t.endpoint,t.clientId,e));Object.assign(t,es(e));let o=r.duplicate;o&&await ns(o).catch(()=>{})}}}async function Ln(t){return await qr(t),Re(await ne(),t)}async function qr(t){let e=Re(await ne(),t).filter(n=>!n.companyId);for(let n of e)await new ae(_e(n)).config().catch(()=>{})}async function xt(t){let e=Yt(await ne(),t);if(!e)return{logins:[]};let n=await Ln(e),r=bn(await ne(),e);return{endpoint:e,logins:n,...r?{active:r}:{}}}async function os(t,e){if(process.env.ANYFORGE_API_TOKEN)throw rs();let n=Yt(await ne(),t);return n&&await qr(n),vn(r=>{let o=He(n?Re(r,n):[],e);return r.active[o.endpoint]=o.clientId,r.endpoint=o.endpoint,o})}async function Zi(t){let e=await $e(t),r=(await Tt({apiKey:e.apiKey,endpoint:e.endpoint})).scopes.filter(Fr);return{endpoint:e.endpoint,accessToken:e.apiKey,scopes:r,source:"control"}}async function Bl(t,e,n){let r=Ne(t),o=`${r}
|
|
269
|
+
${e}`,i=En.get(o);if(i)return i;let s=kn(()=>Wl(r,e,n));En.set(o,s);try{return await s}finally{En.get(o)===s&&En.delete(o)}}async function Wl(t,e,n){let r=await ne(),o=r.logins.find(d=>d.endpoint===t&&d.clientId===e);if(!o)throw new fe;if(o.expiresAt>Date.now()+Ml&&(!n||o.accessToken!==n))return o.accessToken;let i=async()=>{kr(r,o),await Dt(r)},s=await Ge(o.endpoint),a;try{let d={client_id:o.clientId,grant_type:"refresh_token",refresh_token:o.refreshToken,resource:s.resource};a=await cs(s.oauth.token,d)}catch(d){throw d instanceof Tn&&["invalid_grant","invalid_client","invalid_target"].includes(d.code)?(await i(),new fe):d}as(a);let c;try{c=ss(a.scope)}catch(d){throw await i(),d}if(c.some(d=>!o.scopes.includes(d)))throw await Cn(s.oauth.revocation,o.clientId,a.access_token,a.refresh_token),await i(),new f("Oracle refresh attempted to widen the saved grant; credentials were cleared.",m.API_ERROR);return o.accessToken=a.access_token,o.refreshToken=a.refresh_token,o.expiresAt=Date.now()+a.expires_in*1e3,o.scopes=c,await Dt(r),o.accessToken}var Tn=class extends f{constructor(n,r,o){super(`OAuth ${n}: ${r}`,o===401?m.NO_CREDENTIALS:m.API_ERROR);this.code=n;this.name="OAuthResponseError"}code};async function Gl(t,e){e?.throwIfAborted();let n,r,o=!1,i=new Promise((p,l)=>{n=p,r=l});i.catch(()=>{});let s=Fl((p,l)=>{if((p.url?.length??0)>8192){l.writeHead(414,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is too large.");return}let A=p.url??"/";if(!A.startsWith("/callback")||A.startsWith("//")){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response path is invalid.");return}let k=new URL(A,"http://127.0.0.1");if(p.method!=="GET"||k.pathname!=="/callback"){l.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}).end("Not found");return}let x=k.searchParams.get("code")?.slice(0,2048)??"",b=k.searchParams.get("state")?.slice(0,256)??"",g=k.searchParams.get("error")?.slice(0,256)??void 0,R=k.searchParams.get("error_description")?.slice(0,1024)??void 0;if(!b){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response is missing state.");return}if(!is(b,t)){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response state did not match.");return}if(o){l.writeHead(409,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response already received.");return}o=!0,l.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'"}).end('<!doctype html><meta charset="utf-8"><title>AnyForge Oracle</title><p>Sign-in response received. You may close this tab.</p>'),n({code:x,state:b,...g?{error:g}:{},...R?{errorDescription:R}:{}})});await new Promise((p,l)=>{s.once("error",l),s.listen(0,"127.0.0.1",()=>{s.removeListener("error",l),p()})});let a=s.address();if(!a||typeof a=="string")throw new f("Could not bind an Oracle sign-in callback port.",m.FAILURE);let c=setTimeout(()=>{o||(o=!0,r(new f("Oracle sign-in timed out before the browser returned.",m.FAILURE)))},jl);c.unref();let d=()=>{o||(o=!0,r(e?.reason??new DOMException("Oracle sign-in cancelled.","AbortError"))),clearTimeout(c),s.closeAllConnections(),s.close()};return e?.addEventListener("abort",d,{once:!0}),e?.aborted&&d(),{redirectUri:`http://127.0.0.1:${a.port}/callback`,wait:()=>i,close:async()=>{e?.removeEventListener("abort",d),clearTimeout(c),s.listening&&(s.closeAllConnections(),await new Promise(p=>s.close(()=>p())))}}}function is(t,e){let n=Buffer.from(t),r=Buffer.from(e);return n.length===r.length&&ql(n,r)}function ss(t){let e=t.split(/[\s+]+/).filter(Boolean);if(!e.every(Fr))throw new f("OAuth server returned an unknown Oracle scope.",m.API_ERROR);let n=[...new Set(e)];if(!n.includes("read"))throw new f("Oracle OAuth grant is missing the required read scope.",m.API_ERROR);return n}function Fr(t){return t==="read"||t==="write"||t==="approve"}function as(t){if(!t||typeof t.access_token!="string"||!t.access_token||typeof t.refresh_token!="string"||!t.refresh_token||typeof t.token_type!="string"||t.token_type.toLowerCase()!=="bearer"||typeof t.expires_in!="number"||!Number.isFinite(t.expires_in)||t.expires_in<=0||typeof t.scope!="string")throw new f("OAuth token response did not match the supported format.",m.API_ERROR)}async function Hl(t,e,n){return ls(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e),signal:n})}async function cs(t,e,n){return ls(t,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams(Object.entries(e).map(([r,o])=>[r,String(o)])).toString(),signal:n})}async function ls(t,e){let n,r=AbortSignal.timeout(2e4);try{n=await fetch(t,{...e,redirect:"error",signal:e.signal?AbortSignal.any([e.signal,r]):r})}catch(i){throw e.signal?.throwIfAborted(),new f(`OAuth request failed: ${u(i instanceof Error?i.message:String(i))}`,m.FAILURE)}let o=null;try{o=await n.json()}catch{}if(e.signal?.throwIfAborted(),!n.ok){let i=o&&typeof o=="object"?o:{},s=typeof i.error=="string"?i.error:`http_${n.status}`,a=typeof i.error_description=="string"?u(i.error_description):s;throw new Tn(s,a,n.status)}if(!o||typeof o!="object"||Array.isArray(o))throw new f(`OAuth endpoint returned invalid JSON (${n.status}).`,m.API_ERROR);return o}async function Cn(t,e,n,r){let o=async i=>{let s=await fetch(t,{method:"POST",redirect:"error",signal:AbortSignal.timeout(2e4),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:new URLSearchParams({client_id:e,token:i}).toString()});if(!s.ok){let a=`HTTP ${s.status}`;try{let c=await s.json();typeof c.error_description=="string"&&(a=u(c.error_description))}catch{}throw new f(`OAuth token revocation failed: ${a}`,m.API_ERROR)}};await o(r),n!==r&&await o(n)}var id=1048576,sd=25*1048576;async function hs(t){let[e,...n]=t.positional,r=["exec","sessions","resume","login","logout","download","workspaces","use"].includes(e??""),o=Qe(t.flags,"print");if(o!==void 0&&t.positional.length)throw new y("-p/--print takes the prompt as its value and cannot be combined with a subcommand or positional prompt.");let i=Qe(t.flags,"workspace")?.trim()||void 0,s=r?void 0:t.positional.join(" ");if(B(t.flags,"read-only")&&(B(t.flags,"write")||B(t.flags,"approve")))throw new y("--read-only cannot be combined with --write or --approve.");if(!r&&o===void 0&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new y("Interactive Oracle needs a terminal.",'Use `anyforge oracle exec "your question"` for scripts and pipes.');if(e==="login"){if(n.length)throw new y("oracle login does not accept positional arguments.");let b=await Je({endpoint:t.endpointOverride??t.endpoint,write:B(t.flags,"write"),approve:B(t.flags,"approve"),...i?{workspace:i}:{}}),R=[B(t.flags,"write")?"write":void 0,B(t.flags,"approve")?"approve":void 0].filter(P=>!!P).filter(P=>!b.scopes.includes(P));process.stdout.write(`${ds(b)}
|
|
270
|
+
`),R.length&&process.stderr.write(`This account was granted read only; the server withheld: ${R.join(", ")}.
|
|
271
|
+
`),await cd(b,i);return}if(e==="logout"){if(n.length)throw new y("oracle logout does not accept positional arguments.");let b=B(t.flags,"all");if(b&&i)throw new y("Choose either --workspace or --all.");let g=await ts({...t.endpointOverride?{endpointOverride:t.endpointOverride}:{},...i?{workspace:i}:{},all:b});if(!g.removed.length){process.stdout.write(`No saved Oracle login.
|
|
272
|
+
`);return}let R=g.removed.map(P=>u(re(P))).join(", ");process.stdout.write(`Logged out of ${R}; ${g.removed.length===1?"its access was":"their access was"} revoked.
|
|
273
|
+
`),g.nowActive&&process.stdout.write(`Active workspace is now ${u(re(g.nowActive))}.
|
|
274
|
+
`);return}if(e==="workspaces"){if(n.length)throw new y("oracle workspaces does not accept positional arguments.");await dd(t.endpointOverride,t.flags.json===!0);return}if(e==="use"){let b=n.join(" ").trim();if(!b)throw new y("oracle use needs a workspace name or id.","Run `anyforge oracle workspaces` to list your saved workspaces.");let g=await os(t.endpointOverride,b);process.stdout.write(`Now using ${u(re(g))} (${g.scopes.join(", ")}) at ${g.endpoint}.
|
|
275
|
+
`);return}let a;try{a=await fs(t.endpointOverride,i)}catch(b){if(!r&&o===void 0&&b instanceof f&&b.exitCode===m.NO_CREDENTIALS&&process.stdin.isTTY&&process.stdout.isTTY){let g=(await import("prompts")).default;if((await g({type:"confirm",name:"login",message:"Sign in to AnyForge Oracle now?",initial:!0})).login!==!0)throw b;let P=await Je({endpoint:t.endpointOverride??t.endpoint,...i?{workspace:i}:{}});process.stderr.write(`${ds(P)}
|
|
276
|
+
`),a=await fs(t.endpointOverride)}else throw b}if(e==="sessions"){if(n.length)throw new y("oracle sessions does not accept positional arguments.");let b=t.flags.json===!0;b||await us(await a.config().catch(()=>{}));let g=await a.listSessions();pd(g,b);return}if(e==="download"){await hd(a,n,t.flags);return}if(e==="exec"||o!==void 0){if(B(t.flags,"write")||B(t.flags,"approve"))throw new y("oracle exec is read-only. Use interactive `anyforge oracle --write` for authorized actions.");if(t.flags.json===!0&&B(t.flags,"stream-json"))throw new y("Choose either --json or --stream-json.");let b=await ud(o??n.join(" ")),g=await a.config();Ur(a,g),t.flags.json!==!0&&!B(t.flags,"stream-json")&&await us(g),ms(Qe(t.flags,"context"));let R=br(t.flags),P=await a.createSession(bt(R,!1));await Gt(P.sessionId,a.endpoint,g.identity?.companyId);let j=await An(a,P.sessionId,ps(t.flags),(he,Ze)=>ee(process.stderr,`Uploading explicit attachment ${he} (${K(Ze)}).
|
|
277
|
+
`)),G={requestId:Vl(),text:b,readOnly:!0,...R?{context:R}:{},...Qe(t.flags,"context")?{contextText:Qe(t.flags,"context")}:{},...j.length?{attachments:j.map(he=>({fileId:he.fileId}))}:{}};await md(a,P.sessionId,G,{json:t.flags.json===!0,streamJson:B(t.flags,"stream-json"),config:g});return}if(e==="resume"&&n.length>1)throw new y("oracle resume accepts at most one session id.");if(e==="resume"&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new y("oracle resume is interactive and needs a terminal.","Use `anyforge oracle sessions` to list sessions, or run `anyforge oracle exec` for a new scripted turn.");let c=await a.config();Ur(a,c);let d=br(t.flags),p=Qe(t.flags,"context");ms(p);let l=B(t.flags,"shared"),A=B(t.flags,"write")||B(t.flags,"approve");if(A&&(!a.scopes.has("write")||c.capabilities?.write!==!0))throw new f("This Oracle grant or current workspace role does not allow write actions.",m.API_ERROR);if(B(t.flags,"approve")&&(!a.scopes.has("approve")||c.capabilities?.approve!==!0))throw new f("This Oracle grant or current workspace role does not allow approval actions.",m.API_ERROR);if(l&&(!d||d.kind!=="program"&&d.kind!=="initiative"))throw new y("--shared requires --program or --initiative.");let k;if(e==="resume"){let g=(n[0]||void 0)??await fd(await a.listSessions(),await On(a.endpoint,c.identity?.companyId));k=await a.getSession(g)}else k=await a.createSession(bt(d,l));await Gt(k.sessionId,a.endpoint,c.identity?.companyId);let x=await Promise.resolve().then(()=>(qn(),Nn));await Qi({version:x.default.version,client:a,config:c,session:k,initialPrompt:s,initialContext:d,contextText:p,shared:l,canWrite:A,canApprove:B(t.flags,"approve"),initialFiles:ps(t.flags),...a.source==="oauth"?{workspaces:ad(a.endpoint)}:{}})}function ad(t){return{list:()=>Ln(t),connect:async e=>{let n=new ae(_e(e)),r=await n.config();return Ur(n,r),{client:n,config:r}}}}function ds(t){let e=t.scopes.join(", ");return t.companyId?`Connected to ${u(re(t))} (${e}) at ${t.endpoint}.`:`Connected to ${t.endpoint} (${e}). The server did not say which workspace; \`anyforge oracle workspaces\` shows it once it does.`}async function cd(t,e){let n=u(re(t));e&&t.companyId&&!ld(t,e)&&process.stderr.write(`You asked for "${u(e)}", but ${n} was chosen on the consent page, so this login is for ${n}.
|
|
278
|
+
`);let r=Re(await ne(),t.endpoint).length;r>1&&process.stderr.write(`${n} is now your active workspace (${r} saved). List them with \`anyforge oracle workspaces\`; switch with \`anyforge oracle use <name>\`.
|
|
279
|
+
`)}function ld(t,e){return t.companyId===e||t.companyName?.toLowerCase()===e.toLowerCase()}async function us(t){let e=Fe(t?.identity);e&&await ee(process.stderr,`${Se(`Workspace: ${e}`)}
|
|
280
|
+
`)}async function dd(t,e){let n=await xt(t),r=n.logins.map(o=>({name:o.companyName??null,companyId:o.companyId??null,endpoint:o.endpoint,scopes:o.scopes,active:o.clientId===n.active?.clientId}));if(e)process.stdout.write(`${JSON.stringify(r,null,2)}
|
|
281
|
+
`);else if(!r.length)process.stdout.write("No saved Oracle logins. Run `anyforge oracle login` to add one.\n");else{process.stdout.write(`${Se(`Oracle workspaces saved for ${n.endpoint??"this endpoint"}:`)}
|
|
282
|
+
`);for(let o of n.logins){let i=o.clientId===n.active?.clientId,s=u(re(o)),a=o.companyId&&o.companyName?u(o.companyId):"",c=[i?jr(`* ${s}`):` ${s}`,a?Se(a):"",Se(o.scopes.join(", ")),i?gs("active"):""];process.stdout.write(`${c.filter(Boolean).join(" ")}
|
|
283
283
|
`)}}process.env.ANYFORGE_API_TOKEN&&process.stderr.write(`ANYFORGE_API_TOKEN is set, so Oracle commands use that key and its workspace instead of these logins.
|
|
284
|
-
`)}function
|
|
284
|
+
`)}function B(t,e){return t[e]===!0}function Qe(t,e){let n=t[e];return typeof n=="string"?n:void 0}function ps(t){let e=t.file;return typeof e=="string"?[e]:Array.isArray(e)?e:[]}async function fs(t,e){try{return new ae(await Pn(t,e))}catch(n){throw n instanceof f&&n.exitCode===m.NO_CREDENTIALS?new f("Not authenticated for the hosted Oracle.",m.NO_CREDENTIALS,"Run `anyforge oracle login`, or set ANYFORGE_API_TOKEN for an existing Control grant."):n}}function Ur(t,e){if(!t.scopes.has("read")||e.capabilities?.read===!1)throw new f("This credential does not have Oracle read access.",m.API_ERROR)}function ms(t){if(t&&Buffer.byteLength(t,"utf8")>Ar)throw new y(`--context must be ${Ar/1024} KiB or less.`)}async function ud(t){let e=t.trim();if(process.stdin.isTTY){if(e)return e;throw new y("oracle exec requires a prompt when stdin is a terminal.",'Example: anyforge oracle exec "summarize this program"')}let n=[],r=0;for await(let i of process.stdin){let s=Buffer.isBuffer(i)?i:Buffer.from(i);if(r+=s.byteLength,r>id)throw new y("Piped Oracle input exceeds the 1 MiB limit.");n.push(s)}let o=Buffer.concat(n).toString("utf8").trim();if(!e&&!o)throw new y("oracle exec needs a prompt in argv or piped stdin.");return[e,o].filter(Boolean).join(`
|
|
285
285
|
|
|
286
|
-
`)}function
|
|
286
|
+
`)}function pd(t,e){if(e){process.stdout.write(`${JSON.stringify(t,null,2)}
|
|
287
287
|
`);return}if(!t.length){process.stdout.write(`No Oracle sessions are available.
|
|
288
|
-
`);return}for(let n of t){let r=
|
|
289
|
-
`)}}async function
|
|
290
|
-
`)}catch(
|
|
291
|
-
`)}else try{let
|
|
292
|
-
`)}catch(
|
|
293
|
-
`)}},
|
|
294
|
-
`),
|
|
295
|
-
`):(
|
|
296
|
-
`),
|
|
297
|
-
`),
|
|
298
|
-
`),
|
|
299
|
-
`),(
|
|
300
|
-
`)}describe(e){switch(e.type){case"tool_call":{if(
|
|
301
|
-
`):"";default:return""}}};function
|
|
302
|
-
`:""}async function
|
|
303
|
-
`)}function
|
|
304
|
-
${
|
|
305
|
-
|
|
306
|
-
${
|
|
288
|
+
`);return}for(let n of t){let r=Oe(n);process.stdout.write(`${jr(n.sessionId)} ${u(n.title)} ${Se(r)}
|
|
289
|
+
`)}}async function fd(t,e){if(!t.length)throw new f("No accessible Oracle sessions are available.",m.API_ERROR);return new Promise((n,r)=>{let o=new td,i=new od(o),s=new rd(jr("Resume an Oracle session")),a=new nd(t.map(l=>({value:l.sessionId,label:l.title,description:Oe(l)})),12,ft),c=e?t.findIndex(l=>l.sessionId===e):-1;c>=0&&a.setSelectedIndex(c),i.addChild(s),i.addChild(a),a.onSelect=l=>{p(),n(l.value)},a.onCancel=()=>{p(),r(new f("Session selection cancelled.",m.FAILURE))};let d=()=>{p(),r(new f("Session selection interrupted.",m.INTERRUPTED))},p=()=>{process.off("SIGINT",d),i.stop()};process.once("SIGINT",d),i.start()})}async function ee(t,e){t.write(e)||await new Promise(n=>t.once("drain",n))}async function md(t,e,n,r){let o=new xe,i=!1,s,a=new AbortController,c=()=>s??=d(),d=async()=>{if(i=!0,process.exitCode=130,a.abort(new Error("Turn cancelled by Ctrl+C.")),o.streamId)try{let R=await t.cancelTurn(e,o.streamId);await ee(process.stderr,`${vt(R)}
|
|
290
|
+
`)}catch(R){await ee(process.stderr,`Could not confirm server cancellation: ${z(R)}
|
|
291
|
+
`)}else try{let R=await t.cancelRequest(e,n.requestId);await ee(process.stderr,`${vt(R)}
|
|
292
|
+
`)}catch(R){await ee(process.stderr,`Could not confirm server cancellation by request id: ${z(R)}
|
|
293
|
+
`)}},p=()=>{c().catch(()=>{})};process.on("SIGINT",p);let l=async R=>ee(process.stdout,`${JSON.stringify(be(R))}
|
|
294
|
+
`),A=new Mr(e),k=-1,x=async R=>{if(r.streamJson){if(R.idx<=k)return;R.type!=="finalizing"&&(k=R.idx),await l({...R,sessionId:e})}else await A.emit(R)},b;try{b=await wt(t,e,n,{onEvent:x},a.signal,o)}catch(R){if(!i)throw process.off("SIGINT",p),R;b=o.outcome()}finally{process.off("SIGINT",p),s&&await s}let g=Bt(e,b);return r.streamJson?await l({protocolVersion:1,type:"result",result:g}):r.json?await ee(process.stdout,`${JSON.stringify(g,null,2)}
|
|
295
|
+
`):(g.answer&&await ee(process.stdout,`${xo(g.answer,t.endpoint)}
|
|
296
|
+
`),g.status==="awaiting_input"&&await ee(process.stderr,`Oracle needs a response; resume this session interactively to answer the question.
|
|
297
|
+
`),g.status==="interrupted"&&await ee(process.stderr,`Oracle turn ended without a complete final answer.
|
|
298
|
+
`),g.status==="error"&&g.error&&await ee(process.stderr,`Oracle error: ${u(g.error)}
|
|
299
|
+
`),(g.servedBy||g.usage||g.rounds!==void 0)&&await ee(process.stderr,gd(g.servedBy,g.usage,g.rounds))),i?process.exitCode=130:g.status==="error"?process.exitCode=m.API_ERROR:g.status==="interrupted"&&(process.exitCode=m.FAILURE),g}var Mr=class{constructor(e){this.sessionId=e}sessionId;labels=new Map;announced=new Set;async emit(e){let n=this.describe(e);n&&await ee(process.stderr,`${n}
|
|
300
|
+
`)}describe(e){switch(e.type){case"tool_call":{if(lt(e.name))return"";let n=T(typeof e.label=="string"?e.label:ct(T(e.name,240)),120),r=typeof e.id=="string"?e.id:void 0;if(r){if(this.labels.set(r,n),this.announced.has(r))return"";this.announced.add(r)}return`${Se(typeof e.spawnId=="string"?"Oracle \xB7 agent \xB7":"Oracle \xB7")} ${gs(n)}`}case"tool_result":{if(e.ok!==!1)return"";let n=typeof e.toolUseId=="string"?this.labels.get(e.toolUseId):void 0;return Fn(`Oracle \xB7 ${n??"a tool"} failed`)}case"warning":return Fn(`Warning: ${T(e.message,500)}`);case"navigator_file_error":return Fn(`Could not generate ${T(e.filename,200)}: ${T(e.error,500)}`);case"navigator_file_created":{let n=qe(e.file);return Se(`Generated ${n.filename} (${K(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`)}case"provider_fallback":{let n=T(e.from,120),r=T(e.to,120);return Se(`Oracle switched model${n?` from ${n}`:""}${r?` to ${r}`:""}.`)}case"attachment_notice":return Array.isArray(e.notices)?e.notices.map(n=>{let r=n&&typeof n=="object"?n:{};return`${Fn("Attachment")} ${T(r.name,160)}: ${T(r.message,500)}`}).join(`
|
|
301
|
+
`):"";default:return""}}};function gd(t,e,n){let r=[];if(t&&typeof t=="object"){let o=t,i=Kt(o);i&&r.push(`model ${i}`),typeof o.routing=="string"&&r.push(T(o.routing.replaceAll("_"," "),40)),typeof o.configuredModel=="string"&&o.configuredModel!==o.model&&r.push(`configured ${T(o.configuredModel,100)}`),typeof o.provider=="string"&&r.push(T(o.provider,100))}return e?.inputTokens!==void 0||e?.outputTokens!==void 0?r.push(`${e.inputTokens??0} in${e.cachedInputTokens?` (${e.cachedInputTokens} cached)`:""} / ${e.outputTokens??0} out tokens`):e?.totalTokens!==void 0&&r.push(`${e.totalTokens} tokens`),n!==void 0&&r.push(`${n} rounds`),r.length?`${Se(r.join(" \xB7 "))}
|
|
302
|
+
`:""}async function hd(t,e,n){if(e.length!==2)throw new y("oracle download needs exactly a session id and file id.");let[r,o]=e;if(!r||!o)throw new y("oracle download needs a session id and file id.");let i=Qe(n,"output");if(!i)throw new y("oracle download requires --output <existing-directory>.");let s=ed(i),a=await Jl(s);if(!(await Ql(a)).isDirectory())throw new y("--output must point to an existing directory.");let d=await t.download(r,o),p=wd(yd(d.headers.get("content-disposition"))),l=Zl(a,p),A=await vd(d,sd),k;try{k=await Yl(l,"wx",384),await k.writeFile(A)}catch(x){throw x.code==="EEXIST"?new f(`Refusing to overwrite ${l}.`,m.FAILURE):x}finally{await k?.close()}process.stdout.write(`Downloaded ${u(p)} to ${u(l)} (${K(A.byteLength)}).
|
|
303
|
+
`)}function yd(t){if(!t)return"oracle-file.bin";let e=/filename\*=UTF-8''([^;]+)/i.exec(t)?.[1];if(e)try{return decodeURIComponent(e.replace(/^"|"$/g,""))}catch{return"oracle-file.bin"}let n=/filename="?([^";]+)"?/i.exec(t)?.[1];return n?n.trim():"oracle-file.bin"}function wd(t){let e=Xl(t.replace(/\\/g,"/")).replace(/[\u0000-\u001f\u007f/:]/g,"_").replace(/^\.+/,"").trim().slice(0,180);return!e||e==="."||e===".."?"oracle-file.bin":e}async function vd(t,e){if(!t.body)return Buffer.alloc(0);let n=t.body.getReader(),r=[],o=0;try{for(;;){let{value:i,done:s}=await n.read();if(s)break;let a=Buffer.from(i);if(o+=a.byteLength,o>e)throw new f(`Oracle download exceeds the ${K(e)} limit.`,m.API_ERROR);r.push(a)}}catch(i){throw await n.cancel("Oracle download exceeded its byte limit.").catch(()=>{}),i}finally{n.releaseLock()}return Buffer.concat(r)}import{Readable as Sd,Writable as Ed}from"node:stream";import{ndJsonStream as Td}from"@agentclientprotocol/sdk";import{randomUUID as bd}from"node:crypto";import{isAbsolute as Od}from"node:path";import{setTimeout as kd}from"node:timers/promises";import{agent as Ad,RequestError as N}from"@agentclientprotocol/sdk";import{RequestError as Un}from"@agentclientprotocol/sdk";var ys=65536;function ws(t){if(t.length>100)throw Un.invalidParams(void 0,"At most 100 prompt blocks are supported.");let n=t.map(r=>{if(r.type==="text")return r.text;if(r.type==="resource_link")return`[Untrusted resource reference; content has not been read]
|
|
304
|
+
${JSON.stringify({name:r.name,uri:r.uri,description:r.description})}`;throw Un.invalidParams(void 0,"Hosted Oracle accepts text and resource references only. Paste file contents as text; local files, embedded resources, images and audio are unsupported.")}).join(`
|
|
305
|
+
`);if(!n.trim()||Buffer.byteLength(n,"utf8")>ys)throw Un.invalidParams(void 0,`Prompt must contain text and fit within ${ys} UTF-8 bytes.`);return n}function te(){throw Un.invalidParams(void 0,"Answer must match the pending question and its option ids. Use the displayed /answer JSON or /skip command; ordinary prose cannot confirm actions.")}function Dr(t,e){if(!e||typeof e!="object"||Array.isArray(e))return te();let n=e;if(n.questionId!==t.id||Object.keys(n).some(i=>!["questionId","answers","skipped"].includes(i)))return te();if(n.skipped===!0)return!t.optional||!Array.isArray(n.answers)||n.answers.length?te():{questionId:t.id,answers:[],skipped:!0};if(n.skipped!==void 0||!Array.isArray(n.answers)||n.answers.length!==t.questions.length)return te();let r=new Set,o=n.answers.map(i=>{if(!i||typeof i!="object"||Array.isArray(i))return te();let s=i;if(Object.keys(s).some(d=>!["questionId","optionIds","customText"].includes(d)))return te();let a=t.questions.find(d=>d.id===s.questionId);if(!a||r.has(a.id)||!Array.isArray(s.optionIds))return te();r.add(a.id);let c=s.optionIds;return c.some(d=>typeof d!="string"||!a.options.some(p=>p.id===d))||new Set(c).size!==c.length||!a.multiSelect&&c.length>1||s.customText!==void 0&&(typeof s.customText!="string"||!s.customText.trim()||Buffer.byteLength(s.customText)>8192)||!c.length&&s.customText===void 0?te():{questionId:a.id,optionIds:c,...typeof s.customText=="string"?{customText:s.customText}:{}}});return{questionId:t.id,answers:o}}function vs(t,e){if(e===`/skip ${t.id}`)return Dr(t,{questionId:t.id,answers:[],skipped:!0});if(!e.startsWith("/answer "))return te();let n;try{n=JSON.parse(e.slice(8))}catch{return te()}return Dr(t,n)}function It(t){let e=t.questions.map(r=>`${r.header}: ${r.question}
|
|
306
|
+
${r.options.map(o=>` ${JSON.stringify(o.id)}: ${o.label} \u2014 ${o.description}${o.confirms?" (confirms an action)":""}`).join(`
|
|
307
|
+
`)}`).join(`
|
|
308
|
+
|
|
309
|
+
`),n={questionId:t.id,answers:t.questions.map(r=>({questionId:r.id,optionIds:["<choose an option id>"]}))};return`${e}
|
|
310
|
+
|
|
311
|
+
Reply with explicit option ids:
|
|
312
|
+
/answer ${JSON.stringify(n)}${t.optional?`
|
|
313
|
+
Or skip: /skip ${t.id}`:""}`}function bs(t){return{type:"object",properties:Object.fromEntries(t.questions.map((e,n)=>[`answer_${n}`,e.multiSelect?{type:"array",title:e.header,description:e.question,items:{type:"string",enum:e.options.map(r=>r.id)},minItems:1}:{type:"string",title:e.header,description:`${e.question}
|
|
314
|
+
${e.options.map(r=>`${r.id}: ${r.label} \u2014 ${r.description}${r.confirms?" (confirms an action)":""}`).join(`
|
|
315
|
+
`)}`,enum:e.options.map(r=>r.id),enumNames:e.options.map(r=>r.label)}])),required:t.questions.map((e,n)=>`answer_${n}`)}}function Os(t,e){if(!e||typeof e!="object"||Array.isArray(e))return te();let n=e;return Object.keys(n).length!==t.questions.length?te():Dr(t,{questionId:t.id,answers:t.questions.map((r,o)=>({questionId:r.id,optionIds:r.multiSelect?n[`answer_${o}`]:[n[`answer_${o}`]]}))})}function Rd(t){return{client:async()=>new ae(await Pn(t.endpointOverride,t.workspace)),login:async e=>{let n=await xt(t.endpointOverride),r=await Je({endpoint:n.endpoint??$n,workspace:t.workspace,write:t.write,approve:t.approve,signal:e,onAuthorizationUrl:o=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
|
|
316
|
+
${o}
|
|
317
|
+
`)});return new ae(_e(r))}}}var Mn=class{constructor(e,n=Rd(e)){this.options=e;this.dependencies=n}options;dependencies;initialized=!1;formSupported=!1;sessions=new Map;loading=new Set;pendingSetups=0;clientPromise;loginPromise;stopping=!1;lifetime=new AbortController;app(){let e=async n=>{try{return await n()}catch(r){throw Id(r)}};return Ad({name:"anyforge"}).onRequest("initialize",({params:n})=>this.initialize(n)).onRequest("authenticate",({params:n})=>e(async()=>{if(this.ready(),n.methodId!=="oracle-browser")throw N.invalidParams(void 0,"Unknown authentication method. Terminal authentication runs as a separate command.");return await this.authenticate(),{}})).onRequest("session/new",({params:n,client:r})=>e(()=>this.newSession(n,r))).onRequest("session/load",({params:n,client:r})=>e(()=>this.loadSession(n,r))).onRequest("session/prompt",({params:n,client:r})=>e(()=>this.prompt(n,r))).onNotification("session/cancel",({params:n})=>this.cancel(n.sessionId))}initialize(e){if(this.initialized)throw N.invalidRequest(void 0,"This connection is already initialized.");if(!Number.isSafeInteger(e.protocolVersion)||e.protocolVersion<1)throw N.unsupportedProtocolVersion(e.protocolVersion,[1]);return this.initialized=!0,this.formSupported=e.clientCapabilities?.elicitation?.form!=null,{protocolVersion:1,agentInfo:{name:"anyforge",title:"AnyForge Hosted Oracle",version:this.options.version},agentCapabilities:{loadSession:!0,promptCapabilities:{image:!1,audio:!1,embeddedContext:!1},mcpCapabilities:{http:!1,sse:!1}},authMethods:[{id:"oracle-browser",name:"Sign in to AnyForge",description:"Authorize a workspace-scoped Oracle grant in your browser."},...e.clientCapabilities?.auth?.terminal===!0?[{id:"oracle-terminal",name:"Sign in from a terminal",type:"terminal",args:["--login"]}]:[]]}}ready(){if(!this.initialized)throw N.invalidRequest(void 0,"Initialize the ACP connection first.");if(this.stopping)throw N.requestCancelled()}client(){return this.clientPromise??=this.dependencies.client().catch(e=>{throw this.clientPromise=void 0,e}),this.clientPromise}async authenticate(){if(this.sessions.size||this.pendingSetups)throw N.invalidRequest(void 0,"Restart this ACP process to change authentication after opening a session.");return this.loginPromise||(this.loginPromise=this.dependencies.login(this.lifetime.signal).then(async e=>{await e.config(this.lifetime.signal),this.clientPromise=Promise.resolve(e)}).finally(()=>{this.loginPromise=void 0})),this.loginPromise}validateSetup(e){if(this.ready(),!Od(e.cwd))throw N.invalidParams(void 0,"ACP cwd must be an absolute path. Hosted mode does not read this directory.");if(this.loginPromise)throw N.invalidRequest(void 0,"Wait for authentication to finish.");if(this.sessions.size+this.pendingSetups>=128&&(!("sessionId"in e)||!this.sessions.has(e.sessionId)))throw N.invalidRequest(void 0,"This ACP process has reached its 128-session limit. Restart it to open more sessions.")}async newSession(e,n){this.validateSetup(e),this.pendingSetups+=1;try{let r=await this.client();await r.config(this.lifetime.signal);let o=await r.createSession({visibility:"private",...this.options.context?{context:this.options.context}:{}},this.lifetime.signal);if(this.stopping)throw N.requestCancelled();return this.sessions.set(o.sessionId,{client:r,mcpNoticePending:e.mcpServers.length>0}),e.mcpServers.length&&await this.mcpNotice(n,o.sessionId),{sessionId:o.sessionId}}finally{this.pendingSetups-=1}}async loadSession(e,n){if(this.validateSetup(e),this.sessions.get(e.sessionId)?.active||this.loading.has(e.sessionId))throw N.invalidRequest(void 0,"Cannot load a session during an active prompt or load.");this.loading.add(e.sessionId),this.pendingSetups+=1;try{let r=await this.client(),o=await r.getSession(e.sessionId,this.lifetime.signal);if(o.activeStreamId)throw N.invalidRequest(void 0,"The hosted session is still running. Wait for it to settle before loading it.");if(this.stopping)throw N.requestCancelled();this.sessions.set(e.sessionId,{client:r,question:xd(o),mcpNoticePending:e.mcpServers.length>0}),e.mcpServers.length&&await this.mcpNotice(n,e.sessionId);for(let i of o.messages)i.content&&await this.update(n,e.sessionId,{sessionUpdate:i.role==="user"?"user_message_chunk":"agent_message_chunk",content:{type:"text",text:u(i.content)}}),i.question&&await this.text(n,e.sessionId,It(i.question));return{}}finally{this.loading.delete(e.sessionId),this.pendingSetups-=1}}prompt(e,n){this.ready();let r=this.sessions.get(e.sessionId);if(!r)throw N.invalidParams(void 0,"Load or create this session before prompting it.");if(r.active||this.loading.has(e.sessionId))throw N.invalidRequest(void 0,"A prompt or load is already running in this session. Wait for it to settle.");let o=ws(e.prompt),i=r.question?vs(r.question,o):void 0;if(!r.question&&(o.startsWith("/answer ")||o.startsWith("/skip ")))throw N.invalidParams(void 0,"There is no pending question in this session.");let s={cancelled:!1,abort:new AbortController,interaction:new AbortController,done:Promise.resolve({stopReason:"end_turn"})};return r.active=s,s.done=this.runPrompt(r,s,e.sessionId,i?"":o,i,n).finally(()=>{s.requestId=void 0,s.interaction.abort(),s.abort.abort(),s.deadline&&clearTimeout(s.deadline),r.active=void 0}),s.done}async runPrompt(e,n,r,o,i,s){e.mcpNoticePending&&(await this.mcpNotice(s,r),e.mcpNoticePending=!1);let a=await e.client.config(this.lifetime.signal);if(n.cancelled)return{stopReason:"cancelled"};let c=this.options.write&&e.client.scopes.has("write")&&a.capabilities?.write===!0,d=c&&this.options.approve&&e.client.scopes.has("approve")&&a.capabilities?.approve===!0;for(let p=0;p<20;p+=1){if(n.cancelled)return{stopReason:"cancelled"};n.requestId=bd();let l={requestId:n.requestId,text:o,readOnly:!c,...d?{allowApprove:!0}:{},...this.options.context?{context:this.options.context}:{},...this.options.contextText?{contextText:this.options.contextText}:{},...i?{questionResponse:i}:{}},A=new xe,k=new Set,x=await wt(e.client,r,l,{onEvent:async g=>{if(g.type==="delta"&&typeof g.content=="string")await this.text(s,r,u(g.content));else if((g.type==="tool_call"||g.type==="tool_call_start")&&typeof g.id=="string"&&!k.has(g.id))k.add(g.id),await this.update(s,r,{sessionUpdate:"tool_call",toolCallId:g.id,title:typeof g.label=="string"?u(g.label).slice(0,200):ct(typeof g.name=="string"?g.name:""),status:"in_progress"});else if(g.type==="tool_result"&&typeof g.toolUseId=="string"&&k.has(g.toolUseId))await this.update(s,r,{sessionUpdate:"tool_call_update",toolCallId:g.toolUseId,status:g.ok===!1?"failed":"completed"});else if(g.type==="question")e.question=Wt(g);else if(g.type==="navigator_file_created"){let R=qe(g.file);await this.text(s,r,`Generated file: ${R.filename} (${R.mediaType}, ${R.sizeBytes} bytes). Session id: ${r}; file id: ${R.fileId}. Download using anyforge oracle download with an explicit output directory.`)}}},n.abort.signal,A);if(n.requestId=void 0,x.error)throw N.internalError(void 0,x.error);if(i&&(e.question=x.question),n.cancelled||x.done?.exitReason==="cancelled")return{stopReason:"cancelled"};if(!x.question){let g=x.done?.exitReason;if(g==="completed")return{stopReason:"end_turn"};if(g==="max_tokens"||g==="token_budget")return{stopReason:"max_tokens"};if(g==="max_rounds")return{stopReason:"max_turn_requests"};throw N.internalError(void 0,`Oracle stopped before completion (${typeof g=="string"?u(g):"unknown reason"}). Resume the hosted session to inspect it.`)}if(e.question=x.question,!this.formSupported)return await this.text(s,r,It(x.question)),{stopReason:"end_turn"};let b;try{b=await _d(s.request("elicitation/create",{mode:"form",sessionId:r,message:It(x.question),requestedSchema:bs(x.question)},{cancellationSignal:n.interaction.signal}),n.interaction.signal)}catch(g){if(n.cancelled)return{stopReason:"cancelled"};if(!(g instanceof N)||g.code!==-32601)throw g;return await this.text(s,r,It(x.question)),{stopReason:"end_turn"}}if(n.cancelled)return{stopReason:"cancelled"};if(b.action!=="accept")return await this.text(s,r,`No answer was submitted. The question remains pending.
|
|
318
|
+
`+It(x.question)),{stopReason:"end_turn"};i=Os(x.question,b.content),o=""}return{stopReason:"max_turn_requests"}}async cancel(e){let n=this.sessions.get(e),r=n?.active;if(!n||!r||(r.cancelled=!0,r.interaction.abort(),!r.requestId))return;r.deadline??=setTimeout(()=>r.abort.abort(new Error("Oracle cancellation did not settle within 15 seconds. Inspect the hosted session before retrying.")),15e3);let o=r.requestId;r.cancel??=this.cancelUntilClaimed(n.client,e,o,r).catch(i=>{r.requestId&&(process.stderr.write(`Oracle cancellation request failed: ${z(i)}
|
|
319
|
+
`),r.abort.abort(new Error("Oracle cancellation could not be confirmed. Inspect the hosted session before retrying.")))}),await r.cancel}async cancelUntilClaimed(e,n,r,o){for(;o.requestId===r&&!o.abort.signal.aborted;)try{await e.cancelRequest(n,r,o.abort.signal);return}catch(i){if(!(i instanceof le)||i.status!==404)throw i;await kd(100,void 0,{signal:o.abort.signal})}}async shutdown(){if(this.stopping)return;this.stopping=!0,this.lifetime.abort();let e=[...this.sessions.entries()].filter(([,o])=>o.active),n=Promise.allSettled(e.map(async([o,i])=>{await this.cancel(o),await i.active?.done})),r;await Promise.race([n,new Promise(o=>{r=setTimeout(o,3e3)})]),r&&clearTimeout(r);for(let[,o]of e)o.active?.abort.abort();this.sessions.clear()}update(e,n,r){return e.notify("session/update",{sessionId:n,update:r})}text(e,n,r){return this.update(e,n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:r}})}mcpNotice(e,n){return this.text(e,n,"AnyForge is running in hosted mode. Client MCP servers and local tools are omitted: their commands, environment and connections are never used or forwarded. Your authorized AnyForge server tools remain available. Local files and terminals are unavailable.")}};function xd(t){let e=t.messages.at(-1);return e?.role==="assistant"?e.question:void 0}function Id(t){return t instanceof N?t:t instanceof f&&t.exitCode===m.NO_CREDENTIALS||t instanceof le&&t.status===401?N.authRequired(void 0,"Sign in with authenticate or run anyforge oracle login, then retry."):N.internalError(void 0,z(t))}function _d(t,e){return new Promise((n,r)=>{let o=()=>r(N.requestCancelled());e.addEventListener("abort",o,{once:!0}),t.then(n,r).finally(()=>e.removeEventListener("abort",o)).catch(()=>{}),e.aborted&&o()})}var Cd=new Set(["endpoint","workspace","write","approve","read-only","program","initiative","codebase","context","login","help"]);function ks(){return`AnyForge ACP \u2014 hosted Oracle over ACP v1 JSON-RPC stdio.
|
|
320
|
+
|
|
321
|
+
Usage: anyforge acp [options]
|
|
322
|
+
|
|
323
|
+
Configure T3 Code's Local ACP command with executable anyforge and argument acp.
|
|
324
|
+
Use an installed @anyforge/control package; no local coding agent is started.
|
|
325
|
+
|
|
326
|
+
Options:
|
|
327
|
+
--workspace <name|id> Select one saved Oracle workspace
|
|
328
|
+
--endpoint <origin> Use an AnyForge deployment
|
|
329
|
+
--program <id> Scope context to one program
|
|
330
|
+
--initiative <id> Scope context to one initiative
|
|
331
|
+
--codebase <id> Scope context to one codebase
|
|
332
|
+
--context <text> Add up to 8 KiB of untrusted context
|
|
333
|
+
--read-only Keep turns read only (the default)
|
|
334
|
+
--write Request writes within your existing server grant
|
|
335
|
+
--approve Request explicit confirmation actions; implies --write
|
|
336
|
+
--login Sign in, save a workspace grant and exit (terminal auth)
|
|
337
|
+
--help Show this help
|
|
338
|
+
|
|
339
|
+
Authenticate in the client or run anyforge oracle login first. Oracle login is
|
|
340
|
+
separate from Control API credentials. A client mode cannot widen this process's
|
|
341
|
+
startup scope or your server permissions. To change scope, restart the command.
|
|
342
|
+
|
|
343
|
+
Text and resource links are accepted (64 KiB per prompt); references are never
|
|
344
|
+
read or fetched. Embedded resources, file attachments, images and audio are
|
|
345
|
+
unsupported. This adapter never reads local files or runs local terminals.
|
|
346
|
+
Client MCP servers are explicitly omitted with a session notice. Authorized
|
|
347
|
+
AnyForge server tools remain available. This is a hosted ACP profile, not full
|
|
348
|
+
ACP conformance or a local coding agent.
|
|
349
|
+
|
|
350
|
+
Questions use client forms when supported. Otherwise, select explicit option ids
|
|
351
|
+
with the displayed /answer JSON command. Ordinary prose never confirms actions.
|
|
352
|
+
Sessions support new, load, text/tool progress and cancellation. Local modes,
|
|
353
|
+
model selection, session list/fork/delete and local tool execution are not offered.
|
|
354
|
+
Protocol traffic uses stdout; diagnostics use stderr.
|
|
355
|
+
`}function $d(t,e){if(t.positional.length)throw new y("acp does not accept positional arguments. Prompts arrive over ACP stdio.");let n=Object.keys(t.flags).find(d=>!Cd.has(d));if(n)throw new y(`--${n} is not supported by acp. Run anyforge acp --help.`);let r=t.flags.write===!0||t.flags.approve===!0;if(t.flags["read-only"]===!0&&r)throw new y("--read-only cannot be combined with --write or --approve.");let o=["program","initiative","codebase"].filter(d=>t.flags[d]!==void 0);if(o.length>1)throw new y("Choose only one of --program, --initiative or --codebase.");let i=o[0],s=i?t.flags[i]:void 0;if(i&&(typeof s!="string"||!s.trim()||Buffer.byteLength(s)>256))throw new y("Context id must contain 1\u2013256 UTF-8 bytes.");let a=t.flags.context;if(a!==void 0&&(typeof a!="string"||Buffer.byteLength(a)>8192))throw new y("--context must fit within 8 KiB.");let c=t.flags.workspace;if(c!==void 0&&(typeof c!="string"||!c.trim()||Buffer.byteLength(c)>256))throw new y("--workspace must contain 1\u2013256 UTF-8 bytes.");return{version:e,endpointOverride:t.endpointOverride,...typeof c=="string"?{workspace:c}:{},write:r,approve:t.flags.approve===!0,...i&&typeof s=="string"?{context:{kind:i,id:s}}:{},...typeof a=="string"?{contextText:a}:{}}}async function As(t,e){let n=$d(t,e);if(t.flags.login===!0){let a=new AbortController,c=()=>a.abort();process.once("SIGTERM",c),process.once("SIGINT",c);try{let d=await xt(n.endpointOverride),p=await Je({endpoint:d.endpoint??$n,workspace:n.workspace,write:n.write,approve:n.approve,signal:a.signal});await new ae(_e(p)).config(),process.stderr.write(`AnyForge Oracle login saved. Restart or retry your ACP session.
|
|
356
|
+
`)}finally{process.removeListener("SIGTERM",c),process.removeListener("SIGINT",c)}return}let r=new Mn(n),o=Td(Ed.toWeb(process.stdout),Sd.toWeb(process.stdin),{maxMessageBytes:1048576}),i=r.app().connect(o),s=()=>i.close();process.once("SIGTERM",s),process.once("SIGINT",s);try{await i.closed}finally{process.removeListener("SIGTERM",s),process.removeListener("SIGINT",s),await r.shutdown(),process.stdin.destroy()}}var Is="https://crew.anyforge.ai",Ld=new Set(["endpoint","limit","status","type","priority","initiative","program","search","assignee","notes","input","context","messages","scope","client","days","profile","interval","codebase","file","output","workspace","print","event","actor","from","to","cursor","role","team"]),Nd=new Set(["help","version","json","active","archived","all","follow","3way","dry-run","force","shared","write","approve","read-only","stream-json","verify","no-email","login"]);function qd(t){let e={positional:[],flags:{}};for(let n=0;n<t.length;n+=1){let r=t[n];if(!r.startsWith("-")){e.command?e.positional.push(r):e.command=r;continue}let o=r==="-h"?"help":r==="-V"?"version":r==="-f"?"force":r==="-p"||r.startsWith("-p=")?r.replace(/^-p/,"print"):r.replace(/^--?/,""),[i,s]=o.includes("=")?[o.slice(0,o.indexOf("=")),o.slice(o.indexOf("=")+1)]:[o,void 0];if(Ld.has(i)){let a=s??t[++n];if(a===void 0)throw new y(`--${i} needs a value.`);if(i==="file"){let c=e.flags.file;e.flags.file=[...typeof c=="string"?[c]:Array.isArray(c)?c:[],a]}else e.flags[i]=a;continue}if(Nd.has(i)){e.flags[i]=s===void 0?!0:s!=="false";continue}throw new y(`Unknown option: ${r}`,"Run `anyforge --help` for the command list.")}return e}function ge(t,e){if(t===void 0||typeof t=="boolean")return;let n=Number(t);if(!Number.isFinite(n)||n<=0)throw new y(`--${e} must be a positive number, got "${t}".`);return n}function q(t){return typeof t=="string"?t:void 0}function jn(t){return t===!0?!0:void 0}function Fd(){console.log(`
|
|
357
|
+
${W("AnyForge Control")} \u2014 governed LLM proxy, and a scriptable AnyForge client.
|
|
358
|
+
|
|
359
|
+
${W("USAGE")}
|
|
307
360
|
npx @anyforge/control <command> [options]
|
|
308
361
|
|
|
309
|
-
${
|
|
362
|
+
${W("SETUP")}
|
|
310
363
|
init Authorise this device and patch local agent configs
|
|
311
364
|
whoami Show the company, scopes and endpoint this key acts as
|
|
312
365
|
status Check identity, local setup and recorded activity
|
|
313
366
|
logout Clear the saved API key and revert patched configs
|
|
314
367
|
apply-patch [file] Apply a unified diff (file or stdin) via git apply
|
|
315
368
|
|
|
316
|
-
${
|
|
369
|
+
${W("SKILLS")}
|
|
317
370
|
skill install anyforge-platform
|
|
318
371
|
Install the portable AnyForge platform guide
|
|
319
372
|
(--scope project|user, --client agents|claude|codex)
|
|
320
373
|
|
|
321
|
-
${
|
|
374
|
+
${W("APPROVALS")} ${Xe("(the gate that blocks a human)")}
|
|
322
375
|
approvals List HIL approvals awaiting a decision
|
|
323
376
|
approve <id> Approve one, optionally with --notes
|
|
324
377
|
reject <id> Reject one, optionally with --notes
|
|
325
378
|
|
|
326
|
-
${
|
|
379
|
+
${W("CREWS")}
|
|
327
380
|
crews List crews (--active for running/paused only)
|
|
328
381
|
crew <id> Show one crew, its status and recent output
|
|
329
382
|
crew start <item> Dispatch a crew on a work item (--profile, --follow)
|
|
@@ -331,14 +384,16 @@ ${z("CREWS")}
|
|
|
331
384
|
halt <id> Stop a crew
|
|
332
385
|
resume <id> Resume a paused or halted crew
|
|
333
386
|
|
|
334
|
-
${
|
|
387
|
+
${W("WORK & WORKFLOWS")}
|
|
335
388
|
work List work items (--status, --type, --priority, --search).
|
|
336
389
|
Paged: one page by default, --all walks every page
|
|
337
390
|
workflows List operator workflows
|
|
338
391
|
run <id> Dispatch a workflow run, with optional --input JSON
|
|
339
392
|
ask <question> Ask the Oracle one question
|
|
340
393
|
|
|
341
|
-
${
|
|
394
|
+
${W("HOSTED ORACLE")}
|
|
395
|
+
acp Serve the hosted Oracle over ACP v1 stdio (T3 Code and editors)
|
|
396
|
+
acp --help Show hosted ACP capabilities, limits and options
|
|
342
397
|
oracle [prompt] Start an interactive hosted Oracle session
|
|
343
398
|
oracle exec <text> Submit one read-only turn (--json or --stream-json)
|
|
344
399
|
oracle resume Resume an Oracle session
|
|
@@ -349,7 +404,7 @@ ${z("HOSTED ORACLE")}
|
|
|
349
404
|
oracle logout Revoke and clear the active workspace's Oracle grant (--all for every one)
|
|
350
405
|
oracle --help Show Oracle options and slash commands
|
|
351
406
|
|
|
352
|
-
${
|
|
407
|
+
${W("MEASUREMENT")}
|
|
353
408
|
backtest-corpus Can this workspace be back-tested? Counts the incidents
|
|
354
409
|
that could become graded replay cases (--days 30|90|180|365)
|
|
355
410
|
review-interception Serious findings the reviewer raised on your pull requests,
|
|
@@ -357,9 +412,22 @@ ${z("MEASUREMENT")}
|
|
|
357
412
|
review-coverage How many recorded pull requests got reviewed, how fast the
|
|
358
413
|
author heard back, and what a limit stopped (--days 7|30|90|180)
|
|
359
414
|
|
|
360
|
-
${
|
|
415
|
+
${W("AUDIT LOG")} ${Xe("(needs a key with the audit:read scope)")}
|
|
416
|
+
audit Page through the hash-chained audit trail, oldest first
|
|
417
|
+
(--event, --actor, --from, --to, --limit, --cursor, --all).
|
|
418
|
+
--verify recomputes every hash and link across the pages read
|
|
419
|
+
|
|
420
|
+
${W("PEOPLE")} ${Xe("(needs a key with the people:invite scope, minted by an owner)")}
|
|
421
|
+
people invite <email> --role <role>
|
|
422
|
+
Invite one person: operator, editor, viewer or filer, on a
|
|
423
|
+
verified company domain (--team <ids>, --no-email)
|
|
424
|
+
people invitations List the invitations this key could have written
|
|
425
|
+
people withdraw <email>
|
|
426
|
+
Withdraw a pending invitation
|
|
427
|
+
|
|
428
|
+
${W("OPTIONS")}
|
|
361
429
|
--json Print the raw API payload instead of a table
|
|
362
|
-
--endpoint <url> Override the base URL (default: ${
|
|
430
|
+
--endpoint <url> Override the base URL (default: ${Xe(Is)})
|
|
363
431
|
--limit <n> Page size on list commands (work: 1-200, default 50)
|
|
364
432
|
--all work: follow the cursor until the backlog is exhausted
|
|
365
433
|
--notes <text> approve/reject: reviewer notes, recorded in the audit trail
|
|
@@ -371,16 +439,24 @@ ${z("OPTIONS")}
|
|
|
371
439
|
--days <n> backtest-corpus: window (30, 90, 180, 365; default all time)
|
|
372
440
|
review-interception: window (7, 30, 90, 180; default all time)
|
|
373
441
|
review-coverage: window (7, 30, 90, 180; default all time)
|
|
442
|
+
--event <types> audit: event types, comma-separated (e.g. HIL_APPROVED,CREW_HALTED)
|
|
443
|
+
--actor <uid> audit: only entries by this actor
|
|
444
|
+
--from / --to <time> audit: window, ISO 8601 with a time zone or epoch ms
|
|
445
|
+
--cursor <c> audit: resume after a previous page's nextCursor
|
|
446
|
+
--verify audit: verify the chain; exits 5 on a break (unfiltered reads only)
|
|
447
|
+
--role <role> people invite: operator, editor, viewer or filer (never owner)
|
|
448
|
+
--team <ids> people invite: existing team ids, comma-separated
|
|
449
|
+
--no-email people invite: record the invitation without sending the email
|
|
374
450
|
--scope <scope> skill install: project (default) or user
|
|
375
451
|
--client <client> skill install: agents (default), claude, or codex
|
|
376
452
|
--force skill install: replace AnyForge-managed files
|
|
377
453
|
--help, -h Show this message
|
|
378
454
|
--version, -V Print CLI version
|
|
379
455
|
|
|
380
|
-
${
|
|
381
|
-
0 ok 1 failure 2 usage 3 not authenticated 4 API error
|
|
456
|
+
${W("EXIT CODES")}
|
|
457
|
+
0 ok 1 failure 2 usage 3 not authenticated 4 API error 5 audit verification failed
|
|
382
458
|
|
|
383
|
-
${
|
|
459
|
+
${W("EXAMPLES")}
|
|
384
460
|
anyforge approvals
|
|
385
461
|
anyforge approve hil_123 --notes "ship it"
|
|
386
462
|
anyforge crews --active --json | jq -r '.[].id'
|
|
@@ -394,10 +470,14 @@ ${z("EXAMPLES")}
|
|
|
394
470
|
anyforge review-interception --days 30
|
|
395
471
|
anyforge review-coverage --days 30
|
|
396
472
|
anyforge ask "which initiatives slipped this cycle?"
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
473
|
+
anyforge audit --from 2026-10-01T00:00:00Z --all --verify
|
|
474
|
+
anyforge people invite person@example.com --role editor --team platform --no-email
|
|
475
|
+
anyforge people invitations --json
|
|
476
|
+
anyforge people withdraw person@example.com
|
|
477
|
+
`)}function Ud(){console.log(`
|
|
478
|
+
${W("AnyForge Oracle")} \u2014 chat with your hosted, workspace-governed Oracle.
|
|
479
|
+
|
|
480
|
+
${W("USAGE")}
|
|
401
481
|
anyforge oracle [initial prompt] [options] Start an interactive session
|
|
402
482
|
anyforge oracle -p <prompt> [options] Print one read-only answer and exit
|
|
403
483
|
anyforge oracle exec <prompt|stdin> [options] Submit one read-only turn
|
|
@@ -411,7 +491,7 @@ ${z("USAGE")}
|
|
|
411
491
|
Revoke and remove the active (or named, or every) login
|
|
412
492
|
anyforge oracle download <session> <file> --output <directory>
|
|
413
493
|
|
|
414
|
-
${
|
|
494
|
+
${W("OPTIONS")}
|
|
415
495
|
-p, --print <prompt> Print one answer to stdout and exit (like oracle exec)
|
|
416
496
|
--json Print one final JSON object (exec)
|
|
417
497
|
--stream-json Print event JSONL followed by a final result (exec)
|
|
@@ -434,7 +514,7 @@ CLI attachment limits are 10 MiB per file and 20 MiB total per turn. Files are
|
|
|
434
514
|
uploaded only when named with --file or /attach. Generated files require an
|
|
435
515
|
explicit \`oracle download\` destination; existing files are never overwritten.
|
|
436
516
|
|
|
437
|
-
${
|
|
517
|
+
${W("IN THE CONVERSATION")}
|
|
438
518
|
/help Commands and keys /status Session, model, limits
|
|
439
519
|
/new [shared] Start a conversation /clear Clear and start afresh
|
|
440
520
|
/resume [id] Open a saved conversation /usage Turns and tokens so far
|
|
@@ -459,8 +539,8 @@ Examples:
|
|
|
459
539
|
anyforge oracle login --workspace "Acme Engineering"
|
|
460
540
|
anyforge oracle exec "What is blocked?" --workspace "Beta Labs"
|
|
461
541
|
cat incident.log | anyforge oracle exec "Explain this log" --stream-json
|
|
462
|
-
`)}async function
|
|
463
|
-
`),t.hint?process.stderr.write(
|
|
464
|
-
`):t instanceof y&&process.stderr.write(
|
|
465
|
-
`),process.exit(t.exitCode)),process.stderr.write(
|
|
466
|
-
`),process.exit(m.FAILURE)}
|
|
542
|
+
`)}async function Md(t,e,n,r){let o=e.flags.json===!0,[i]=e.positional;if(t==="oracle")return hs({positional:e.positional,flags:e.flags,endpointOverride:r,endpoint:n});if(t==="acp"){let a=await Promise.resolve().then(()=>(qn(),Nn));return As({positional:e.positional,flags:e.flags,endpointOverride:r},a.default.version)}switch(t){case"init":return uo({endpoint:n,force:e.flags.force===!0});case"logout":return Oo();case"status":return _o({endpoint:r,json:o});case"whoami":return vo({endpoint:r,json:o});case"apply-patch":return Eo({patchFile:i,threeWay:e.flags["3way"]===!0,dryRun:e.flags["dry-run"]===!0});case"proxy":console.log(Pd("Local proxy mode is coming in a future release.")),console.log(Xe(" Until then, your agents talk directly to the AnyForge proxy."));return;case"skill":return ci({subcommand:i,name:e.positional[1],scope:q(e.flags.scope),client:q(e.flags.client),force:e.flags.force===!0,json:o})}let s=await $e(r);switch(t){case"approvals":return To({creds:s,json:o,limit:ge(e.flags.limit,"limit")});case"approve":return ir({creds:s,json:o,approvalId:i,decision:"APPROVED",notes:q(e.flags.notes)});case"reject":return ir({creds:s,json:o,approvalId:i,decision:"REJECTED",notes:q(e.flags.notes)});case"crews":return Co({creds:s,json:o,status:q(e.flags.status),activeOnly:jn(e.flags.active),limit:ge(e.flags.limit,"limit")});case"crew":{let[a,c]=e.positional;return a==="start"?qo({creds:s,json:o,workItemId:c,profileId:q(e.flags.profile),follow:e.flags.follow===!0,intervalSeconds:ge(e.flags.interval,"interval")}):a==="follow"?cr({creds:s,json:o,crewId:c,intervalSeconds:ge(e.flags.interval,"interval")}):$o({creds:s,json:o,crewId:i,messages:ge(e.flags.messages,"messages")})}case"halt":return ar({creds:s,json:o,crewId:i,action:"halt"});case"resume":return ar({creds:s,json:o,crewId:i,action:"resume"});case"work":return Mo({creds:s,json:o,status:q(e.flags.status),type:q(e.flags.type),priority:q(e.flags.priority),initiative:q(e.flags.initiative),program:q(e.flags.program),search:q(e.flags.search),assignee:q(e.flags.assignee),includeArchived:jn(e.flags.archived),limit:ge(e.flags.limit,"limit"),all:jn(e.flags.all)});case"workflows":return jo({creds:s,json:o});case"run":return Do({creds:s,json:o,workflowId:i,input:q(e.flags.input)});case"ask":return Bo({creds:s,json:o,question:e.positional.join(" "),context:q(e.flags.context)});case"backtest-corpus":return Go({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-interception":return Ko({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-coverage":return zo({creds:s,json:o,days:ge(e.flags.days,"days")});case"audit":return ei({creds:s,json:o,eventType:q(e.flags.event),actor:q(e.flags.actor),from:q(e.flags.from),to:q(e.flags.to),limit:ge(e.flags.limit,"limit"),cursor:q(e.flags.cursor),all:jn(e.flags.all),verify:e.flags.verify===!0});case"people":return ri({creds:s,json:o,subcommand:i,email:e.positional[1],role:q(e.flags.role),team:q(e.flags.team),notify:e.flags["no-email"]!==!0});default:throw new y(`Unknown command: ${t}`,"Run `anyforge --help` for the command list.")}}async function jd(){let t;try{t=qd(process.argv.slice(2))}catch(r){xs(r);return}if(t.flags.version===!0){let r=await Promise.resolve().then(()=>(qn(),Nn));console.log(r.default.version);return}if(t.command==="oracle"&&t.flags.help===!0){Ud();return}if(t.command==="acp"&&t.flags.help===!0){console.log(ks());return}if(t.flags.help===!0||!t.command){Fd();return}let e=q(t.flags.endpoint)??process.env.ANYFORGE_ENDPOINT,n=e??Is;try{if(t.command!=="oracle"&&t.command!=="acp"){let o=["approve","codebase","file","output","print","read-only","shared","stream-json","workspace","write"].find(i=>t.flags[i]!==void 0);if(o)throw new y(`--${o} is only supported by the oracle command.`)}if(t.command!=="acp"&&t.flags.login!==void 0)throw new y("--login is only supported by the acp command.");go(t.command),await Md(t.command,t,n,e)}catch(r){xs(r)}}function xs(t){t instanceof f&&(process.stderr.write(Rs("Error: ")+u(t.message)+`
|
|
543
|
+
`),t.hint?process.stderr.write(Xe(u(t.hint))+`
|
|
544
|
+
`):t instanceof y&&process.stderr.write(Xe("Run `anyforge --help`.")+`
|
|
545
|
+
`),process.exit(t.exitCode)),process.stderr.write(Rs("Error: ")+u(t instanceof Error?t.message:String(t))+`
|
|
546
|
+
`),process.exit(m.FAILURE)}jd();
|