@anyforge/control 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +4 -0
- package/dist/cli.js +99 -49
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var zs=Object.defineProperty;var Vs=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var Js=(t,e)=>{for(var n in e)zs(t,n,{get:e[n],enumerable:!0})};var qn={};Js(qn,{default:()=>sd});var sd,Fn=Vs(()=>{sd={name:"@anyforge/control",version:"0.9.
|
|
3
|
-
`),console.log(Vn(" \u2714")+" Claude Code"),{label:t.scope==="repo"?"Claude Code (repo)":"Claude Code (global)",path:n})}async function lo(t){let e=J(
|
|
2
|
+
var zs=Object.defineProperty;var Vs=(t,e,n)=>()=>{if(n)throw n[0];try{return t&&(e=t(t=0)),e}catch(r){throw n=[r],r}};var Js=(t,e)=>{for(var n in e)zs(t,n,{get:e[n],enumerable:!0})};var qn={};Js(qn,{default:()=>sd});var sd,Fn=Vs(()=>{sd={name:"@anyforge/control",version:"0.9.2",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 Ss,yellow as Kd,dim as Ze,bold as D}from"kleur/colors";import{execFileSync as Jn}from"child_process";import ia from"open";import Yn from"prompts";import{green as Tt,dim as M,bold as Z,yellow as Ce,cyan as cn}from"kleur/colors";import{promises as on}from"fs";import{homedir as Ys}from"os";import{join as eo}from"path";var to=eo(Ys(),".anyforge"),Kn=eo(to,"credentials");async function no(t){await on.mkdir(to,{recursive:!0,mode:448}),await on.writeFile(Kn,JSON.stringify(t,null,2),{mode:384})}async function st(){try{let t=await on.readFile(Kn,"utf8");return JSON.parse(t)}catch(t){if(t.code==="ENOENT")return null;throw t}}async function ro(){try{return await on.unlink(Kn),!0}catch(t){if(t.code==="ENOENT")return!1;throw t}}import{promises as U}from"fs";import{execFileSync as Qs}from"child_process";import{homedir as ye}from"os";import{join as J}from"path";import sn from"prompts";import{dim as ce,green as Vn,yellow as an}from"kleur/colors";var Te="# === AnyForge Control \u2014 added by `anyforge init` ===",Et="# === end AnyForge Control ===",De=".anyforge-bak",Xs=/claude_(max|pro|team|enterprise)/i,Zs=["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 co(t){let e=[],n=await ea(t);if(n&&e.push(n),t.scope==="repo"){let r=await ta(t);return r&&e.push(r),e}for(let r of ra){let o=await r.detect();if(!(!o||!(await sn({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(Vn(" \u2714")+" "+r.label)}catch(s){console.warn(" \u26A0 Skipped "+r.label+": "+s.message)}}return e}async function ea(t){let e=t.scope==="repo"&&t.repoRoot?{dir:J(t.repoRoot,".claude"),file:"settings.local.json"}:{dir:J(ye(),".claude"),file:"settings.json"},n=J(e.dir,e.file);if(!(await sn({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(an(" \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 sn({type:"confirm",name:"go",message:"Overwrite existing local LLM proxy config?",initial:!1})).go)?null:(await pe(n)&&!await pe(n+De)&&await U.copyFile(n,n+De),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(Vn(" \u2714")+" Claude Code"),{label:t.scope==="repo"?"Claude Code (repo)":"Claude Code (global)",path:n})}async function lo(t){let e=J(ye(),".claude"),n=J(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&&!zn(s,fo(t.endpoint))?(console.log(an(" \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+De)&&await U.copyFile(n,n+De),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
4
|
`),{label:"Claude Code usage telemetry (global)",path:n})}async function ta(t){if(!t.repoRoot)return null;let e=J(t.repoRoot,".env");if(!(await sn({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}`,Et,""].join(`
|
|
5
5
|
`),o="";try{o=await U.readFile(e,"utf8")}catch{}let i;return o.includes(Te)?i=o.replace(new RegExp(`${Te}[\\s\\S]*?${Et}\\n?`),r):o.length===0?i=r:i=o.endsWith(`
|
|
6
6
|
`)?`${o}${r}`:`${o}
|
|
7
7
|
${r}`,i!==o&&await U.writeFile(e,i),await na(t.repoRoot),console.log(Vn(" \u2714")+" .env"),{label:".env (repo)",path:e}}async function na(t){let e=J(t,".gitignore");try{(await U.readFile(e,"utf8")).split(`
|
|
8
|
-
`).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(an(" \u26A0 .env is not in .gitignore \u2014 add it before committing."))}catch{console.log(an(" \u26A0 No .gitignore found \u2014 make sure .env is excluded before committing."))}}var ra=[{label:"Cursor (shell hint)",async detect(){let t=J(
|
|
9
|
-
`);for(let n of[".zshrc",".bashrc"]){let r=J(
|
|
8
|
+
`).map(i=>i.trim()).some(i=>i===".env"||i==="/.env"||i==="*.env")||console.log(an(" \u26A0 .env is not in .gitignore \u2014 add it before committing."))}catch{console.log(an(" \u26A0 No .gitignore found \u2014 make sure .env is excluded before committing."))}}var ra=[{label:"Cursor (shell hint)",async detect(){let t=J(ye(),".cursor");return await pe(t)?{path:".cursor (shell hint)"}:null},async patch(t,e){await oo(e)}},{label:"Cline / Continue (shell hint)",async detect(){let t=await pe(J(ye(),".cline")),e=await pe(J(ye(),".continue"));return t||e?{path:"~/.cline or ~/.continue (shell hint)"}:null},async patch(t,e){await oo(e)}}];async function oo(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",Et,""].join(`
|
|
9
|
+
`);for(let n of[".zshrc",".bashrc"]){let r=J(ye(),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]*?${Et}\\n?`),e):i=o.endsWith(`
|
|
10
10
|
`)?`${o}
|
|
11
11
|
${e}`:`${o}
|
|
12
12
|
|
|
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 uo(){try{let t=await U.readFile(J(
|
|
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 uo(){try{let t=await U.readFile(J(ye(),".claude.json"),"utf8"),n=JSON.parse(t).oauthAccount,r=n?.organizationType??"";if(n&&Xs.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 po(t={}){let e=t.endpoint?fo(t.endpoint):null,n=[],r=J(ye(),".claude","settings.json");await io(r,e)&&n.push(r);for(let i of[".zshrc",".bashrc"]){let s=J(ye(),i);await so(s)&&n.push(s)}let o=t.repoRoot??oa(process.cwd());if(o){let i=J(o,".claude","settings.local.json");await io(i,e)&&n.push(i);let s=J(o,".env");await so(s)&&n.push(s)}return{reverted:n}}function zn(t,e){return t?t.includes("anyforge")?!0:e?t.includes(e):!1:!1}async function io(t,e){if(!await pe(t))return!1;if(await pe(t+De))return await U.copyFile(t+De,t),await U.unlink(t+De).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)zn(r[s],e)&&(delete r[s],delete r[a],o=!0);if(r.ANYFORGE_API_KEY&&(delete r.ANYFORGE_API_KEY,o=!0),zn(r.OTEL_EXPORTER_OTLP_ENDPOINT,e))for(let s of Zs)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
14
|
`),!0):!1}async function so(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?${ao(Te)}[\\s\\S]*?${ao(Et)}\\n?`),`
|
|
15
15
|
`).replace(/\n{3,}/g,`
|
|
16
16
|
|
|
17
|
-
`);return await U.writeFile(t,n),!0}function ao(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function fo(t){try{return new URL(t).host}catch{return null}}function oa(t){try{return Qs("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}var sa=2e3,aa=900*1e3;async function go(t){let{endpoint:e}=t;console.log(Z("AnyForge Control \u2014 device authorisation")),console.log(M(` endpoint: ${e}`));let n=process.cwd(),r=mo(n),o=r?ma(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(` ${cn(c)}`),console.log(M(` Code: ${a}`)),console.log();try{await ia(c)}catch{}let{credentials:p,modelRouting:l}=await ca({pollUrl:d,endpoint:e});if(await no(p),console.log(Tt("\u2714")+" API key saved to "+M("~/.anyforge/credentials")),!l){await ua(e,p.apiKey);return}let
|
|
17
|
+
`);return await U.writeFile(t,n),!0}function ao(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function fo(t){try{return new URL(t).host}catch{return null}}function oa(t){try{return Qs("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}var sa=2e3,aa=900*1e3;async function go(t){let{endpoint:e}=t;console.log(Z("AnyForge Control \u2014 device authorisation")),console.log(M(` endpoint: ${e}`));let n=process.cwd(),r=mo(n),o=r?ma(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(` ${cn(c)}`),console.log(M(` Code: ${a}`)),console.log();try{await ia(c)}catch{}let{credentials:p,modelRouting:l}=await ca({pollUrl:d,endpoint:e});if(await no(p),console.log(Tt("\u2714")+" API key saved to "+M("~/.anyforge/credentials")),!l){await ua(e,p.apiKey);return}let k=await uo();if(k.isSubscription&&!t.force){await da(k,e,p.apiKey);return}let O=process.cwd(),R=mo(O),b=await pa(R),g=await co({endpoint:e,apiKey:p.apiKey,scope:b,repoRoot:R});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."))):fa(g),console.log(),k.isSubscription&&t.force&&(console.log(Ce(` \u26A0 --force: your Claude ${k.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: "+cn(`${e}/usage`))}async function ca(t){let e=Date.now();process.stdout.write("Waiting for browser authorisation\u2026 ");let n=0;for(;Date.now()-e<aa;){await new Promise(i=>setTimeout(i,sa)),process.stdout.write("."),n+=1,n>40&&(process.stdout.write(`
|
|
18
18
|
`),n=0);let o=await(await fetch(t.pollUrl)).json();if(o.status==="authorized"&&o.data)return process.stdout.write(" "+Tt("done")+`
|
|
19
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 la(t,e){return["mcp","add","anyforge","--env",`ANYFORGE_API_TOKEN=${e}`,"--env",`ANYFORGE_API_URL=${t}`,"--","npx","-y","@anyforge/mcp-server"]}async function da(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 ho(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 lo({endpoint:e,apiKey:n});i&&(console.log(Tt(" \u2714")+" Usage telemetry enabled \u2014 view it at "+cn(`${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: "+cn(`${e}/usage`))}async function ua(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 ho(t,e),console.log(),console.log(Z("Done.")+" Run `anyforge status` to check identity and local setup.")}async function ho(t,e){let n=la(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{Jn("claude",n,{stdio:"inherit"}),console.log(Tt(" \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 mo(t){try{return Jn("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}async function pa(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 fa(t){console.log(Tt("\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 ma(t){try{let e=Jn("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 Zn,yellow as
|
|
21
|
-
${i}`}function ha(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
|
|
22
|
-
`)}function S(t){return JSON.stringify(t,null,2)}function
|
|
20
|
+
`),new Error(`Authorisation ${o.status}. Re-run \`anyforge init\`.`)}throw new Error("Authorisation timed out (15 min). Re-run `anyforge init`.")}function la(t,e){return["mcp","add","anyforge","--env",`ANYFORGE_API_TOKEN=${e}`,"--env",`ANYFORGE_API_URL=${t}`,"--","npx","-y","@anyforge/mcp-server"]}async function da(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 ho(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 lo({endpoint:e,apiKey:n});i&&(console.log(Tt(" \u2714")+" Usage telemetry enabled \u2014 view it at "+cn(`${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: "+cn(`${e}/usage`))}async function ua(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 ho(t,e),console.log(),console.log(Z("Done.")+" Run `anyforge status` to check identity and local setup.")}async function ho(t,e){let n=la(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{Jn("claude",n,{stdio:"inherit"}),console.log(Tt(" \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 mo(t){try{return Jn("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return null}}async function pa(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 fa(t){console.log(Tt("\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 ma(t){try{let e=Jn("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 Zn,yellow as ka,bold as Oa}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}},w=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"}},wo=4e3,le=class extends f{code;status;details;constructor(e,n,r,o,i){super(ga(e,r,o),m.API_ERROR,ha(e,n,i)),this.name="ControlApiError",this.code=e,this.status=n,o&&(this.details=o)}};function ga(t,e,n){let r=e?`${t}: ${e}`:t;if(!n)return r;let o=JSON.stringify(n),i=o.length>wo?`${o.slice(0,wo)}\u2026(truncated)`:o;return`${r}
|
|
21
|
+
${i}`}function ha(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 yo="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||yo};let r=await st();if(r?.apiKey)return{apiKey:r.apiKey,endpoint:n||r.endpoint||yo};throw new fe}var Qn=null;function vo(t){Qn=t&&/^[a-z][a-z0-9-]*$/.test(t)?`anyforge ${t}`:null}async function I(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",...Qn?{"X-AnyForge-Tool":Qn}:{}},...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,ya(s),t.endpoint)}return s.data}var wa=new Set(["ok","error","message","detail"]);function ya(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=Object.entries(t).filter(([n,r])=>!wa.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 Ct(t){return I(t,"GET","/me")}function Xn(t,e,n,r){let o=t?.entityLabels?.[e]?.[n];return typeof o=="string"&&o.trim()?o:r}async function bo(t){try{return await Ct(t)}catch{return null}}function ko(t){return[...t].length}function at(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 B(t,e){if(t.length===0)return"";let n=t.map(i=>e.map(s=>{let a=s.value(i)??"";return s.max?at(a,s.max):a})),r=e.map((i,s)=>Math.max(ko(i.header),...n.map(a=>ko(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 S(t){return JSON.stringify(t,null,2)}function Oo(t,e=Date.now()){let n=va(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 va(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"?ba(t):null;return e?e.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)):[]}function ba(t){for(let e of["items","results","rows","data"])if(Array.isArray(t[e]))return t[e];return null}function ln(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 Ao(t){let e=await $e(t.endpoint),n=await Ct(e);if(t.json){console.log(S(n));return}console.log(`Company: ${Oa(n.companyName??n.companyId)}${n.companyName?Zn(` (${n.companyId})`):""}`),console.log(`Endpoint: ${e.endpoint}`),console.log(`Scopes: ${n.scopes.length?n.scopes.join(", "):ka("none")}`),n.userId&&console.log(`User: ${n.userId}`);let r=await st();console.log(r?Zn(`Key tail: \u2026${r.apiKey.slice(-4)} \xB7 saved ${new Date(r.savedAt).toLocaleString()}`):Zn("Key from: ANYFORGE_API_TOKEN (no saved credential file)"))}import{green as xo,dim as dn,yellow as Aa}from"kleur/colors";async function Ro(){let t=await st(),e=await ro(),{reverted:n}=await po({endpoint:t?.endpoint??null});if(console.log(e?xo("\u2714")+" Local credentials cleared.":Aa("No credentials were saved on this machine.")),n.length){console.log(xo("\u2714")+" Reverted patched configs:");for(let r of n)console.log(` ${dn("\u2022")} ${r}`);console.log(dn(" Your previous Claude Code login (if any) is restored."))}else console.log(dn(" No AnyForge config patches found to revert."));console.log(dn(" Revoke the key server-side at /settings \u2192 API keys."))}import{readFile as un}from"node:fs/promises";import{homedir as xa}from"node:os";import{dirname as er,join as ve}from"node:path";function Pe(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)?t:{}}async function _o(t,e={home:xa(),cwd:process.cwd(),env:process.env}){let n=t.endpoint.replace(/\/$/,""),r=[],o={};try{o=Pe(JSON.parse(await un(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(k=>{let O=Pe(k),R=Pe(O.env),b=Array.isArray(O.args)?O.args:[],g=R.ANYFORGE_API_TOKEN??e.env.ANYFORGE_API_TOKEN,x=R.ANYFORGE_API_URL??e.env.ANYFORGE_API_URL,L=g??o.apiKey,j=x??(g?"https://crew.anyforge.ai":o.endpoint)??"https://crew.anyforge.ai";return b.includes("@anyforge/mcp-server")&&L===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(;er(s)!==s;){try{await un(ve(s,".git","HEAD"));break}catch{}try{await un(ve(s,".git"));break}catch{}s=er(s)}er(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 un(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 Ra=/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\|[@-_])/g,_a=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g;function u(t){return t.replace(Ra,"").replace(_a,"")}function tr(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 Io(t){return t.replace(/\x1B\]8;;([^\x1B\x07]*)(?:\x07|\x1B\\)/g,(e,n)=>tr(n)?e:"\x1B]8;;\x07")}function So(t,e){if(!e||!t.startsWith("/")||t.startsWith("//"))return t;try{return new URL(t,e).toString()}catch{return t}}function Eo(t,e){return e?t.replace(/(\]\()(<?)(\/(?!\/)[^\s)>]*)/g,(n,r,o,i)=>`${r}${o}${So(i,e)}`):t}function nr(t,e){let r=u(t).replace(/(!?)\[([^\]\n]+)\]\((<[^>\n]*>|[^\s)]+)(?:\s+[^)]*)?\)/g,(s,a,c,d)=>{let p=So(d.startsWith("<")&&d.endsWith(">")?d.slice(1,-1):d,e),l=tr(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=tr(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 To(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 Co(t){let e=await $e(t.endpoint),n=await I(e,"GET","/connection"),r=await _o(e),o={...n,endpoint:e.endpoint,localConfiguration:r};if(t.json)console.log(S(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: ${To(n.proxy)}`,`Latest recorded subscription telemetry: ${To(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
23
|
`))}[n.proxy,n.subscriptionTelemetry].some(i=>i.state==="unavailable")&&(process.exitCode=1)}import{spawn as Ia}from"node:child_process";import{readFile as Sa}from"node:fs/promises";import{red as $t,green as rr,yellow as Ea,dim as Pt,bold as $o}from"kleur/colors";async function Po(t){let e=await Ta(t.patchFile);e.trim()||(process.stderr.write($t(`Empty patch \u2014 nothing to apply.
|
|
24
24
|
`)),process.exit(1));let n=Ca(e);process.stderr.write(Pt(`Patch: ${n.files} ${n.files===1?"file":"files"}, `)+rr(`+${n.added}`)+" "+$t(`-${n.removed}`)+`
|
|
25
25
|
`);let r=await or(["apply","--check"],e);if(r.code!==0){if(t.threeWay){let i=await or(["apply","--3way"],e);i.code!==0&&(process.stderr.write($t(`3-way apply failed:
|
|
@@ -34,18 +34,18 @@ ${i}`}function ha(t,e,n){if(t==="api_key_expired")return`This API key has expire
|
|
|
34
34
|
`),process.exit(1)),process.stderr.write(rr(`\u2713 Applied.
|
|
35
35
|
`)),n.files>0&&process.stderr.write(Pt(" Run ")+$o("git diff --stat")+Pt(` to review.
|
|
36
36
|
`))}async function Ta(t){if(t)return await Sa(t,"utf8");let e=[];for await(let n of process.stdin)e.push(n);return Buffer.concat(e).toString("utf8")}function or(t,e){return new Promise((n,r)=>{let o=Ia("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 Ca(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 $a,dim as ir,green as Pa,red as La}from"kleur/colors";async function Lo(t){let e=await I(t.creds,"GET",`/hil${H({limit:t.limit})}`);if(t.json){console.log(S(e));return}let n=se(e);if(n.length===0){console.log(ir("No approvals pending."));return}console.log(B(n,[{header:"ID",value:r=>E(r,"id")},{header:"CREW",value:r=>E(r,"crewId"),max:24},{header:"AGE",value:r=>
|
|
38
|
-
${n.length} pending \xB7 approve with `)+$a("anyforge approve <id>"))}async function sr(t){if(!t.approvalId)throw new
|
|
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(`${ar(`[${a}]`)} ${at(c.replace(/\s+/g," "),160)}`)}}}async function cr(t){if(!t.crewId)throw new
|
|
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 $a,dim as ir,green as Pa,red as La}from"kleur/colors";async function Lo(t){let e=await I(t.creds,"GET",`/hil${H({limit:t.limit})}`);if(t.json){console.log(S(e));return}let n=se(e);if(n.length===0){console.log(ir("No approvals pending."));return}console.log(B(n,[{header:"ID",value:r=>E(r,"id")},{header:"CREW",value:r=>E(r,"crewId"),max:24},{header:"AGE",value:r=>Oo(r.requestedAt)},{header:"SUMMARY",value:r=>X(r,["planSummary","summary","title","description"]),max:60}])),console.log(ir(`
|
|
38
|
+
${n.length} pending \xB7 approve with `)+$a("anyforge approve <id>"))}async function sr(t){if(!t.approvalId)throw new w(`${t.decision==="APPROVED"?"approve":"reject"} requires an approval id.`,"List them with `anyforge approvals`.");let e=await I(t.creds,"POST",`/hil/${encodeURIComponent(t.approvalId)}`,{decision:t.decision,...t.notes?{notes:t.notes}:{}});if(t.json){console.log(S(e));return}let n=t.decision==="APPROVED"?Pa("Approved"):La("Rejected");console.log(`${n} ${t.approvalId}`);let r=e&&typeof e=="object"?e.signature:void 0;typeof r=="string"&&r&&console.log(ir(`Audit signature: ${r}`))}import{bold as Na,dim as ar}from"kleur/colors";async function No(t){let e=await I(t.creds,"GET",`/crews${H({status:t.status,activeOnly:t.activeOnly,limit:t.limit})}`);if(t.json){console.log(S(e));return}let n=se(e);if(n.length===0){console.log(ar(t.activeOnly?"No active crews.":"No crews."));return}console.log(B(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 qo(t){if(!t.crewId)throw new w("crew requires a crew id.","List them with `anyforge crews`.");let e=await I(t.creds,"GET",`/crews/${encodeURIComponent(t.crewId)}${H({messages:t.messages})}`);if(t.json){console.log(S(e));return}let n=e?.crew&&typeof e.crew=="object"?e.crew:e;console.log(`${Na(qa(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(ar(`
|
|
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(`${ar(`[${a}]`)} ${at(c.replace(/\s+/g," "),160)}`)}}}async function cr(t){if(!t.crewId)throw new w(`${t.action} requires a crew id.`,"List them with `anyforge crews`.");let e=await I(t.creds,"POST",`/crews/${encodeURIComponent(t.crewId)}/${t.action}`);if(t.json){console.log(S(e));return}console.log(`${t.action==="halt"?"Halted":"Resumed"} ${t.crewId}`)}function qa(t){return at(t.split(/\r?\n/).find(e=>e.trim())?.trim()??t,80)}import{bold as Fa,dim as ct,red as Fo,yellow as Mo}from"kleur/colors";var Ua=new Set(["COMPLETE","HALTED","REJECTED","CANCELLED"]),Ma=new Set(["HIL_PENDING","HIL_SHIP","PERMISSION_PENDING","PAUSED"]),ja={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 jo(t){if(!t.workItemId)throw new w("crew start requires a work item id.","Find one with `anyforge work --status READY`.");let e=await I(t.creds,"PATCH",`/work/${encodeURIComponent(t.workItemId)}`,{action:"dispatch",...t.profileId?{runProfileId:t.profileId}:{}});if(t.json&&!t.follow){console.log(S(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(Mo(`warning: ${r}`));if(!t.follow){console.log(`Started ${Fa(n)} on ${t.workItemId}`),console.log(ct(`Follow it with \`anyforge crew follow ${n}\``));return}await lr({creds:t.creds,json:t.json,crewId:n,intervalSeconds:t.intervalSeconds})}var Da=5,Ba=10,Wa=120,Uo=30;async function lr(t){if(!t.crewId)throw new w("crew follow requires a crew id.","List them with `anyforge crews --active`.");let e=Math.min(Math.max(t.intervalSeconds??Ba,Da),Wa)*1e3,n=Date.now()+Uo*6e4,r=new Set,o="",i=!1;for(;;){let s=await I(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&&Ga(d))}if(i=!0,c&&c!==o&&(t.json||console.log(ct(`\u2500\u2500 ${c}`)),o=c),Ma.has(c)){if(t.json)console.log(S({crewId:t.crewId,status:c,waiting:!0,url:E(a,"url","")}));else{console.log(Mo(`
|
|
40
40
|
${c} \u2014 this crew is waiting on a person.`));let d=ja[c];d&&console.log(ct(d));let p=E(a,"url","");p&&console.log(ct(p))}return}if(Ua.has(c)){if(t.json)console.log(S({crewId:t.crewId,status:c,waiting:!1,url:E(a,"url","")}));else{let d=`
|
|
41
|
-
${c}`;console.log(c==="COMPLETE"?d:Fo(d));let p=E(a,"prUrl","");p&&console.log(ct(p));let l=E(a,"errorMessage","");l&&console.log(Fo(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 ${Uo} minutes \u2014 giving up watching.`,m.FAILURE,`The crew is unaffected. Check it with \`anyforge crew ${t.crewId}\`.`);await Ka(e)}}function Ga(t){let e=E(t,"agentRole","")||E(t,"type","?"),n=Ha(t,["content","text","summary"]);n&&console.log(`${ct(`[${e}]`)} ${at(n.replace(/\s+/g," "),160)}`)}function Ha(t,e){for(let n of e){let r=t[n];if(typeof r=="string"&&r.trim())return r.trim()}return""}function Ka(t){return new Promise(e=>{setTimeout(e,t)})}import{randomUUID as dr}from"node:crypto";import{promises as ur}from"node:fs";import{relative as za,resolve as Va,isAbsolute as Ja}from"node:path";import{execFile as Ya}from"node:child_process";import{promisify as Qa}from"node:util";import Xa from"open";var Za=Qa(Ya);function ec(t){if(!t)return{};try{let e=JSON.parse(t);if(!e||typeof e!="object"||Array.isArray(e))throw new Error("not an object");return e}catch{throw new
|
|
41
|
+
${c}`;console.log(c==="COMPLETE"?d:Fo(d));let p=E(a,"prUrl","");p&&console.log(ct(p));let l=E(a,"errorMessage","");l&&console.log(Fo(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 ${Uo} minutes \u2014 giving up watching.`,m.FAILURE,`The crew is unaffected. Check it with \`anyforge crew ${t.crewId}\`.`);await Ka(e)}}function Ga(t){let e=E(t,"agentRole","")||E(t,"type","?"),n=Ha(t,["content","text","summary"]);n&&console.log(`${ct(`[${e}]`)} ${at(n.replace(/\s+/g," "),160)}`)}function Ha(t,e){for(let n of e){let r=t[n];if(typeof r=="string"&&r.trim())return r.trim()}return""}function Ka(t){return new Promise(e=>{setTimeout(e,t)})}import{randomUUID as dr}from"node:crypto";import{promises as ur}from"node:fs";import{relative as za,resolve as Va,isAbsolute as Ja}from"node:path";import{execFile as Ya}from"node:child_process";import{promisify as Qa}from"node:util";import Xa from"open";var Za=Qa(Ya);function ec(t){if(!t)return{};try{let e=JSON.parse(t);if(!e||typeof e!="object"||Array.isArray(e))throw new Error("not an object");return e}catch{throw new w("--input must be a JSON object.")}}function Le(t){if(!t||!/^[A-Za-z0-9_-]+$/.test(t))throw new w("A valid Studio session or run ID is required.");return encodeURIComponent(t)}async function tc(t){let[e,n]=await Promise.all([ur.realpath(process.cwd()),ur.realpath(Va(t))]),r=za(e,n).replaceAll("\\","/");if(!r||Ja(r)||r.split("/").includes(".."))throw new w("--file must be inside the current directory, including symlink targets.");let o=await ur.open(n,"r"),i;try{let p=await o.stat();if(!p.isFile()||p.size>2e4)throw new w("--file must be a regular file of 20 KB or smaller; select a smaller context file.");let l=Buffer.alloc(20001),k=0;for(;k<l.length;){let{bytesRead:O}=await o.read(l,k,l.length-k,k);if(!O)break;k+=O}if(k>2e4)throw new w("--file grew beyond 20 KB; select a smaller context file.");i=l.subarray(0,k).toString("utf8")}finally{await o.close()}let s=async p=>{try{return(await Za("git",p,{cwd:process.cwd(),timeout:5e3,maxBuffer:64e3})).stdout.trim()}catch{return null}},[a,c,d]=await Promise.all([s(["branch","--show-current"]),s(["rev-parse","HEAD"]),s(["status","--porcelain","--",r])]);return{content:`Local reference file: ${r}
|
|
42
42
|
|
|
43
|
-
${i}`,localReference:{path:r,branch:a,commit:c,uncommitted:d===null||d.length>0||c===null}}}async function Do(t){let e=ec(t.input),n=t.command??"list",r="GET",o;if(n==="list")o="/studio/sessions";else if(n==="create")r="POST",o="/studio/sessions",e.idempotencyKey??=dr(),t.repo&&(e.repoFullName=t.repo),t.branch&&(e.branch=t.branch),t.initiative&&(e.initiativeId=t.initiative),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.context=t.context);else if(n==="continue")r="POST",o=`/studio/sessions/${Le(t.id)}/context`,e.idempotencyKey??=dr(),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.content=t.context);else if(["get","open","context","usage","changes","artifacts","runs"].includes(n))o=`/studio/sessions/${Le(t.id)}${n==="get"||n==="open"?"":`/${n}`}`;else if(n==="run")r="POST",o=`/studio/sessions/${Le(t.id)}/runs`,e.idempotencyKey??=dr(),t.context&&(e.content=t.context);else if(n==="events")o=`/studio/runs/${Le(t.id)}/events${t.cursor?`?after=${encodeURIComponent(t.cursor)}`:""}`;else if(n==="status")o=`/studio/runs/${Le(t.id)}`;else if(["cancel","pause","resume","steer","answer"].includes(n))r="POST",o=`/studio/runs/${Le(t.id)}/control`,e.action=n,t.context&&(e.content=t.context);else if(["checkpoint","commit","stage","push"].includes(n))r="POST",o=`/studio/sessions/${Le(t.id)}/${n==="checkpoint"?"checkpoints":n}`;else throw new
|
|
43
|
+
${i}`,localReference:{path:r,branch:a,commit:c,uncommitted:d===null||d.length>0||c===null}}}async function Do(t){let e=ec(t.input),n=t.command??"list",r="GET",o;if(n==="list")o="/studio/sessions";else if(n==="create")r="POST",o="/studio/sessions",e.idempotencyKey??=dr(),t.repo&&(e.repoFullName=t.repo),t.branch&&(e.branch=t.branch),t.initiative&&(e.initiativeId=t.initiative),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.context=t.context);else if(n==="continue")r="POST",o=`/studio/sessions/${Le(t.id)}/context`,e.idempotencyKey??=dr(),e.provenance={surface:"cli",workspace:"remote"},t.context&&(e.content=t.context);else if(["get","open","context","usage","changes","artifacts","runs"].includes(n))o=`/studio/sessions/${Le(t.id)}${n==="get"||n==="open"?"":`/${n}`}`;else if(n==="run")r="POST",o=`/studio/sessions/${Le(t.id)}/runs`,e.idempotencyKey??=dr(),t.context&&(e.content=t.context);else if(n==="events")o=`/studio/runs/${Le(t.id)}/events${t.cursor?`?after=${encodeURIComponent(t.cursor)}`:""}`;else if(n==="status")o=`/studio/runs/${Le(t.id)}`;else if(["cancel","pause","resume","steer","answer"].includes(n))r="POST",o=`/studio/runs/${Le(t.id)}/control`,e.action=n,t.context&&(e.content=t.context);else if(["checkpoint","commit","stage","push"].includes(n))r="POST",o=`/studio/sessions/${Le(t.id)}/${n==="checkpoint"?"checkpoints":n}`;else throw new w(`Unknown Studio command: ${n}.`,"Use studio list, create, continue, open, get, context, usage, run, runs, status, events, pause, resume, cancel, steer, answer, changes, artifacts, checkpoint, stage, commit, or push.");if(t.file){if(!["create","continue"].includes(n))throw new w("--file is only supported for studio create or continue.");let s=await tc(t.file),a=n==="create"?"context":"content";e[a]=[e[a],s.content].filter(Boolean).join(`
|
|
44
44
|
|
|
45
45
|
`),e.provenance={surface:"cli",workspace:"remote",localReference:s.localReference}}let i=await I(t.creds,r,o,r==="POST"?e:void 0);n==="open"&&await Xa(`${t.creds.endpoint.replace(/\/$/,"")}/studio?session=${Le(t.id)}`),process.stdout.write(`${t.json?JSON.stringify(i):u(JSON.stringify(i,null,2))}
|
|
46
46
|
`)}import{dim as pn}from"kleur/colors";var Wo=100;function Bo(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 nc(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 I(t.creds,"GET",e()),r=se(n),o=Bo(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<Wo;){let s=await I(t.creds,"GET",e(o.cursor));r.push(...se(s)),o=Bo(s),i+=1}return{rows:r,total:o.total,hasMore:o.hasMore,raw:n,pagesFetched:i}}async function Go(t){let e=await nc(t);if(t.json){console.log(S(t.all?{items:e.rows,total:e.total,hasMore:e.hasMore,nextCursor:null}:e.raw));return}let n=e.rows,r=await bo(t.creds),o=Xn(r,"work_item","plural","Work items");if(n.length===0){console.log(pn(`No ${o.toLowerCase()} match that filter.`));return}console.log(B(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?Xn(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?pn(` \u2014 stopped after ${Wo} pages; narrow the filter`):pn(" \u2014 pass --all for the rest"):"";console.log(`${pn(`
|
|
47
47
|
${s}`)}${a}`)}import{bold as rc,dim as fn}from"kleur/colors";async function Ho(t){let e=await I(t.creds,"GET","/workflows");if(t.json){console.log(S(e));return}let n=se(e);if(n.length===0){console.log(fn("No workflows defined."));return}console.log(B(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(fn(`
|
|
48
|
-
Dispatch with `)+rc("anyforge run <id>"))}async function Ko(t){if(!t.workflowId)throw new
|
|
48
|
+
Dispatch with `)+rc("anyforge run <id>"))}async function Ko(t){if(!t.workflowId)throw new w("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 w(`--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 w("--input must be a JSON object.",`Example: --input '{"repo":"acme/web"}'`)}let n=await I(t.creds,"POST",`/workflows/${encodeURIComponent(t.workflowId)}/run`,e===void 0?void 0:{inputs:e});if(t.json){console.log(S(n));return}let r=X(n??{},["id","runId"],"");console.log(`Queued run ${r||fn("(id not returned)")}`),console.log(fn("Runs pause at gate steps for an operator decision."))}import{dim as oc}from"kleur/colors";async function zo(t){let e=t.question?.trim();if(!e)throw new w("ask requires a question.",'Example: anyforge ask "which initiatives slipped this cycle?"');!t.json&&process.stderr.isTTY&&process.stderr.write(oc(`Asking the Oracle (this can take a few minutes)\u2026
|
|
49
49
|
`));let n=await I(t.creds,"POST","/oracle",{question:e,...t.context?{context:t.context}:{}});if(t.json){console.log(S(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:S(n))}import{bold as pr,dim as Vo,green as ic,yellow as sc}from"kleur/colors";function ac(t){return typeof t=="number"?`${Math.round(t*100)}%`:"\u2014"}async function Jo(t){let e=await I(t.creds,"GET",`/backtest-corpus${H({days:t.days})}`);if(t.json){console.log(S(e));return}let n=e.incidentsInWindow??0,r=e.backTestable??0,o=e.windowDays?`last ${e.windowDays}d`:"all time";console.log(`${pr(String(r))} of ${pr(String(n))} incidents are back-testable `+Vo(`(${ac(e.coverage)} \xB7 ${o})`)),e.viable?console.log(ic("Enough to run a back-test.")):console.log(sc(`Not yet viable \u2014 ${e.shortfall??0} more needed.`));let i=(e.excluded??[]).filter(s=>s.count>0);i.length>0&&console.log(B(i,[{header:"EXCLUDED",value:s=>String(s.count)},{header:"REASON",value:s=>s.label,max:56}])),e.biggestGap&&console.log(Vo(`
|
|
50
50
|
Biggest fixable gap: `)+pr(`${e.biggestGap.label} (${e.biggestGap.count})`))}import{bold as mn,dim as Lt,green as cc,yellow as Yo}from"kleur/colors";var fr=t=>typeof t=="number"?`${Math.round(t*1e3)/10}%`:"\u2014";async function Qo(t){let e=await I(t.creds,"GET",`/review-interception${H({days:t.days})}`);if(t.json){console.log(S(e));return}let n=ln(e.windowDays,e.truncated,e.oldestReviewMs),r=i=>{console.log(i)};if(r(`${mn(String(e.seriousRaised??0))} serious findings raised across ${mn(String(e.prsReviewed??0))} pull requests ${Lt(`(${n})`)}`),e.verdict==="measured")r(`${cc(String(e.disappearedSerious??0))} no longer raised by the next round \u2014 ${mn(fr(e.interceptionFloor))} to ${mn(fr(e.interceptionCeiling))}`),r(Lt(`floor counts every unmeasured finding as unresolved \xB7 ceiling is over the ${e.measurableSerious??0} that had a later round`));else{r(Yo("Not enough findings had a later round to measure a rate."));for(let i of e.inconclusiveReasons??[])r(Lt(` ${i}`))}let o=(e.byLens??[]).filter(i=>i.serious>0);if(o.length>0&&r(B(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(Yo(`
|
|
51
51
|
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(Lt(`
|
|
@@ -56,7 +56,7 @@ Not enough timed reviews to report a latency figure.`));let o=e.capacityGapPullR
|
|
|
56
56
|
${dc(String(o))} pull requests went unreviewed because a limit fired `+Be("\u2014 a cap or a diff over the size ceiling, not a decision anyone made"));let i=e.bySkipClass??[];i.length>0&&n(B(i,[{header:"WHY UNREVIEWED",value:s=>pc[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(Be(`${e.failedOnlyPullRequests} pull requests had every review attempt error out.`));for(let s of e.inconclusiveReasons??[])n(mr(`
|
|
57
57
|
${s}`));if(e.truncated){let s=e.oldestRecordMs?new Date(e.oldestRecordMs).toISOString().slice(0,10):null;n(mr(`
|
|
58
58
|
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(Be(`
|
|
59
|
-
${s}`))}import{createHash as vc}from"node:crypto";import{dim as oi,green as bc,red as
|
|
59
|
+
${s}`))}import{createHash as vc}from"node:crypto";import{dim as oi,green as bc,red as kc,yellow as Oc}from"kleur/colors";function ei(t){return fc({eventType:t.eventType,actor:t.actor,payload:t.payload,timestamp:t.timestampMs})}function fc(t){return JSON.stringify(hr(t))}function hr(t){if(Array.isArray(t))return t.map(hr);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]=hr(e[r]);return n}var mc="";function ti(t,e,n){return t(`${n??""}${mc}${e}`)}function gc(t){let e=t.findIndex(n=>(n.sealVersion??1)>=2);return e===-1?t.length:e}function ni(t,e){let n=gc(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 ${Zo(i.previousHash)}, but the prior row hashes to ${Zo(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 Zo(t){return t?`${t.slice(0,12)}\u2026`:"nothing"}function wc(t){return t?JSON.parse(JSON.stringify(t)):{}}function yc(t){return{id:t.id,eventType:t.eventType,actor:t.actor,payload:wc(t.payload),timestampMs:t.timestampMs,hash:t.hash,previousHash:t.prevHash,...typeof t.sealVersion=="number"?{sealVersion:t.sealVersion}:{}}}function ri(t,e,n={}){let r=t.map(yc),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}:{...ni(r,a=>ti(e,ei({eventType:a.eventType,actor:a.actor,payload:a.payload,timestampMs:a.timestampMs}),a.previousHash??null)),lastHash:o}}var Ac=200,xc=t=>vc("sha256").update(t).digest("hex");function Rc(t,e=xc){let n=null,r=0,o=0;for(let i of t){if(i.length===0)continue;let s=ri(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 ii(t){if(t.verify&&(t.eventType||t.actor))throw new w("--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 I(t.creds,"GET",e(r));n.push(c),r=c.hasMore&&c.nextCursor?c.nextCursor:void 0}while(t.all&&r&&n.length<Ac);let o=n.flatMap(c=>c.entries),i=n[n.length-1],s=t.verify?Rc(n.map(c=>c.entries)):null,a=c=>{console.log(c)};if(t.json)a(S(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(B(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(oi(`${o.length} entries from ${n.length} ${c}.`)),i.hasMore&&i.nextCursor&&a(Oc(`More entries exist. Continue with --cursor ${i.nextCursor}${t.all?"":" or pass --all"}.`)),s&&(s.valid?a(bc(`Chain verified: ${s.verified} sealed entries link and recompute.`)+(s.unverifiable?oi(` ${s.unverifiable} earlier entries predate sealing and cannot be verified.`):"")):a(kc(`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 si}from"kleur/colors";var _c=["invite","invitations","withdraw"];function ai(t,e){if(!t.email)throw new w(`people ${e} requires an email address.`,`Example: anyforge people ${e} person@example.com`);return t.email}function Ic(t){if(t===void 0)return;let e=t.split(",").map(n=>n.trim()).filter(Boolean);return e.length?e:void 0}function We(t){console.log(t)}async function ci(t){switch(t.subcommand){case"invite":{let e=ai(t,"invite");if(!t.role)throw new w("people invite requires --role.","One of operator, editor, viewer or filer.");let n=Ic(t.team),r=await I(t.creds,"POST","/people/invitations",{email:e,role:t.role,...n?{teamIds:n}:{},...t.notify?{}:{notify:!1}});if(t.json){We(S(r));return}let o=r.created?"Invited":"Refreshed the pending invitation for",i=r.emailSent?"Invitation email sent.":"No invitation email sent.";We(`${o} ${r.email} as ${r.role}. ${si(i)}`);return}case"invitations":{let e=await I(t.creds,"GET","/people/invitations");if(t.json){We(S(e));return}let n=se(e);if(n.length===0){We(si("No invitations."));return}We(B(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=ai(t,"withdraw"),n=await I(t.creds,"DELETE",`/people/invitations${H({email:e})}`);if(t.json){We(S(n));return}We(`Withdrew the invitation for ${n.email}.`);return}default:throw new w(t.subcommand?`Unknown people command: ${t.subcommand}`:"people needs a command.",`One of: ${_c.join(", ")}.`)}}import{promises as qt}from"node:fs";import{homedir as Cc}from"node:os";import{dirname as di,join as Ge}from"node:path";import{dim as ui,green as $c,yellow as Pc}from"kleur/colors";var me="anyforge-platform",Sc="Orient an agent to AnyForge's structure, delivery model, surfaces, live control tools, and safety boundaries.",li="anyforge://guide/platform",Ec="text/markdown",gn={"SKILL.md":`---
|
|
60
60
|
name: anyforge-platform
|
|
61
61
|
description: >
|
|
62
62
|
Orient an agent to AnyForge's structure, delivery model, surfaces, live control
|
|
@@ -123,6 +123,16 @@ report is easier to inspect there.
|
|
|
123
123
|
|
|
124
124
|
## How an external agent should work
|
|
125
125
|
|
|
126
|
+
For a hosted OAuth connection in explicit workspace mode, first call
|
|
127
|
+
\`anyforge_list_workspaces\`. It returns the currently authorized, consented IDs,
|
|
128
|
+
names, roles and effective scopes. Resolve the user's intended workspace and
|
|
129
|
+
pass its \`workspaceId\` on every tenant tool call, including reads. Ask when the
|
|
130
|
+
target is ambiguous; never guess from an entity ID, the Console's current
|
|
131
|
+
workspace or a previous call. There is no active-workspace switch. Existing
|
|
132
|
+
single-workspace connections and stdio keep their original contract; use the
|
|
133
|
+
live tool schema to identify the mode. A denied target needs authorization,
|
|
134
|
+
not a fallback to another workspace. Adding a workspace requires fresh consent.
|
|
135
|
+
|
|
126
136
|
1. **Orient.** Read this guide only when the task needs platform context. Use
|
|
127
137
|
the references for deeper details.
|
|
128
138
|
2. **Discover.** Use read tools before forming claims. Typical starting points
|
|
@@ -196,15 +206,22 @@ editor publishing uses only their own GitHub identity. Human credentials are pin
|
|
|
196
206
|
to their company and session owner. Service accounts cannot own these interactive
|
|
197
207
|
sessions. Other roles do not gain crew authority by calling a Studio endpoint.
|
|
198
208
|
|
|
199
|
-
- \`studio:read\`: owned sessions
|
|
200
|
-
|
|
201
|
-
|
|
209
|
+
- \`studio:read\`: owned sessions and their conversation, decisions waiting on you,
|
|
210
|
+
context manifest, run events, changes, artifacts, verification reports,
|
|
211
|
+
environment readiness, outcomes, the task list, live workspace files and
|
|
212
|
+
language-service queries, the model list and your subscription quota.
|
|
213
|
+
- \`studio:write\`: prepare sessions and context, select a repository, additional
|
|
214
|
+
repositories or initiative, and archive sessions.
|
|
215
|
+
- \`studio:execute\`: start/control runs, compact a session's history, edit live
|
|
216
|
+
workspace files and apply renames, and perform local workspace/Git operations.
|
|
202
217
|
- \`studio:publish\`: push with your connected GitHub identity, create draft PRs,
|
|
203
218
|
update metadata and mark PRs as draft. Execution still needs \`studio:execute\`.
|
|
204
219
|
|
|
205
220
|
External Control/MCP runs permit audited local coding, reads and reviews. They
|
|
206
221
|
cannot invoke hosted shell commands, browser automation, external connectors,
|
|
207
|
-
Oracle escalation, crew/workflow dispatch or approvals.
|
|
222
|
+
Oracle escalation, crew/workflow dispatch or approvals. Running a verification,
|
|
223
|
+
preparing an environment, the follow-up queue, parallel tasks and recording an
|
|
224
|
+
outcome stay in browser Studio too; their results are readable from MCP. Use browser Studio for
|
|
208
225
|
those actions with its own role and confirmation gates. A publish scope does not
|
|
209
226
|
remove this boundary. Ready/merge require review and confirmation in the browser;
|
|
210
227
|
model arguments never become a trusted publication grant.
|
|
@@ -224,9 +241,18 @@ not grant authority; connector hints do not make a mutation safe in Plan mode.
|
|
|
224
241
|
3. Start \`anyforge_start_studio_run\` with an explicit task and bounded token
|
|
225
242
|
budget. Use \`planned: true\` for analysis. Save its run ID.
|
|
226
243
|
4. Poll \`anyforge_get_studio_run_events\` with the previous \`nextCursor\`; inspect
|
|
227
|
-
status with \`anyforge_get_studio_run\`.
|
|
228
|
-
|
|
244
|
+
status with \`anyforge_get_studio_run\`. \`anyforge_list_studio_decisions\` lists
|
|
245
|
+
every run waiting on you. Answer only the current pending question ID. Pause,
|
|
246
|
+
steer, resume and cancel through \`anyforge_control_studio_run\`. A session
|
|
247
|
+
started in the browser is readable with \`anyforge_get_studio_conversation\`;
|
|
248
|
+
find one with \`anyforge_search_studio_sessions\`.
|
|
229
249
|
5. Inspect \`anyforge_get_studio_changes\` and \`anyforge_list_studio_artifacts\`.
|
|
250
|
+
Read live files with \`anyforge_read_studio_workspace_file\` and check them with
|
|
251
|
+
\`anyforge_inspect_studio_code\`; neither creates a checkout. Small fixes can be
|
|
252
|
+
saved without a model run through \`anyforge_write_studio_workspace_file\` or
|
|
253
|
+
\`anyforge_rename_studio_symbol\`, which name the revision they read. Read
|
|
254
|
+
evidence with \`anyforge_list_studio_verifications\` and
|
|
255
|
+
\`anyforge_list_studio_outcomes\`.
|
|
230
256
|
Use \`anyforge_request_studio_review\` for a durable specialist review; inspect structured findings
|
|
231
257
|
with \`anyforge_get_studio_review\`. Record acceptance criteria and resolve
|
|
232
258
|
findings against the reviewed revision.
|
|
@@ -266,6 +292,11 @@ Retry creates a new proposal. \`anyforge_branch_studio_session\` and
|
|
|
266
292
|
\`anyforge_rewind_studio_session\` distinguish conversation, files and both; file
|
|
267
293
|
changes require execute scope and checkpoints, and rewind requires current revisions.
|
|
268
294
|
|
|
295
|
+
A crew's Build view recovery actions are available as \`anyforge_fix_crew_ci\`,
|
|
296
|
+
\`anyforge_resume_crew_with_pr\` and \`anyforge_decide_crew_permission\` (approve
|
|
297
|
+
scope). Fix CI's \`outOfScope\` grant covers one Engineer round and is audited
|
|
298
|
+
under the key holder.
|
|
299
|
+
|
|
269
300
|
Branch calls require a stable idempotency key in MCP. Repeat the same request
|
|
270
301
|
with that key to retrieve its session receipt; changed arguments are refused.
|
|
271
302
|
An incomplete file fork stays hidden and returns an explicit preparation error.
|
|
@@ -326,6 +357,21 @@ company, role, ownership, and scope checks. A skill or prompt cannot widen
|
|
|
326
357
|
those checks. A client that receives a tool description still cannot assume it
|
|
327
358
|
can call the tool successfully.
|
|
328
359
|
|
|
360
|
+
Hosted OAuth connections can explicitly authorize several workspaces. In that
|
|
361
|
+
mode, use \`anyforge_list_workspaces\` to discover only the workspaces currently
|
|
362
|
+
authorized by consent and membership, with their IDs, names, roles and effective
|
|
363
|
+
scopes. Pass the intended \`workspaceId\` on every tenant tool call. Each call is
|
|
364
|
+
checked against that workspace's current authority. Missing, unconsented or
|
|
365
|
+
revoked targets are denied; scopes from another workspace cannot substitute.
|
|
366
|
+
There is no mutable active workspace. Ask the user when the target is ambiguous,
|
|
367
|
+
and keep the target explicit when parallel work spans workspaces.
|
|
368
|
+
|
|
369
|
+
Existing single-workspace grants and stdio API keys retain their original
|
|
370
|
+
boundary. Explicit mode requires reauthorization for the exact hosted
|
|
371
|
+
\`/api/mcp\` resource; adding a workspace requires fresh consent. A workspace's
|
|
372
|
+
Connected clients row controls only that workspace's access. Client token
|
|
373
|
+
revocation and refresh-token reuse detection end the whole connection.
|
|
374
|
+
|
|
329
375
|
## Evidence and state
|
|
330
376
|
|
|
331
377
|
Static platform guidance explains concepts. Live tool responses establish
|
|
@@ -356,40 +402,40 @@ these sources can grant network, filesystem, tool, or approval authority.
|
|
|
356
402
|
Do not place API keys, tokens, credentials, private tenant records, or customer
|
|
357
403
|
data in a reusable skill, prompt, log, or generated example. Use the client's
|
|
358
404
|
credential mechanism and let the server enforce access.
|
|
359
|
-
`,"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'},
|
|
360
|
-
`);return}console.log($c("Installed ")+`${r.name} ${ui(`(${r.scope}, ${r.client})`)}`),console.log(` ${r.directory}`),console.log(ui(` ${r.files.length} files installed. Re-run with --force to refresh them.`)),r.scope==="project"&&r.client==="agents"&&console.log(Pc(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as ad}from"node:crypto";import{open as cd,realpath as ld,stat as dd}from"node:fs/promises";import{basename as ud,join as pd,resolve as fd}from"node:path";import{bold as Gr,cyan as vs,dim as Se,yellow as Un}from"kleur/colors";import{ProcessTerminal as md,SelectList as gd,Text as hd,TuiAltScreen as yd}from"@earendil-works/pi-tui";var Fc=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function Uc(t){return Fc.has(t)?"querying the code graph":t==="search_code_semantic"?"searching the code index":t==="search_codebase_docs"?"searching codebase docs":null}function lt(t){let e=Uc(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 Mc=new Set(["ask_user","present_catch_up_decision","present_memory_candidates"]);function dt(t){return typeof t=="string"&&Mc.has(t)}import{truncateToWidth as Kt,wrapTextWithAnsi as Mi}from"@earendil-works/pi-tui";import{cyan as Cr,dim as Ve,green as ji,italic as wl,red as $r,yellow as Pr}from"kleur/colors";import{truncateToWidth as Ft,visibleWidth as wr,wrapTextWithAnsi as jc}from"@earendil-works/pi-tui";import{bold as Dc,dim as Bc}from"kleur/colors";function K(t){return t<1024?`${t} B`:t<1024*1024?`${Ut(t/1024)} KiB`:`${Ut(t/(1024*1024))} MiB`}function Mt(t){let e=Math.max(0,t);if(e<1e4)return`${Ut(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 vr(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?`${Ut(t/1e3,t<1e5?1:0)}k`:`${Ut(t/1e6)}M`}function Ut(t,e=1){return t.toFixed(e).replace(/\.0+$/,"")}function Wc(t){return u(t).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function Gc(t,e){return typeof e=="boolean"?e?"on":"off":typeof e=="string"?u(e):/Ms$/.test(t)?Mt(e):/Bytes$/.test(t)?K(e):/tokens?/i.test(t)?de(e):String(e)}function mi(t){return t?Object.entries(t).filter(e=>e[1]!==null).map(([e,n])=>`${Wc(e)} ${Gc(e,n)}`):[]}function br(t,e,n=2){let r=Math.min(16,Math.max(...t.map(([s])=>wr(s)),0)+2),o=Math.max(10,e-n-r),i=[];for(let[s,a]of t)jc(a,o).forEach((d,p)=>{let l=p===0?Bc(Ft(s,r-1).padEnd(r)):" ".repeat(r);i.push(Ft(`${" ".repeat(n)}${l}${d}`,e))});return i}function gi(t,e,n){return[Ft(Dc(t),n),...br(e,n)]}function hi(t,e,n){let r=wr(n),o=e.filter(Boolean).join(" \xB7 ");if(r+4>t)return Ft(o||n,t);let i=t-r-2,s=Ft(o,i),a=Math.max(2,t-wr(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as pt,cyan as ut,dim as Ne,green as Hc,italic as yi,strikethrough as Kc,underline as wi,yellow as zc}from"kleur/colors";import{Markdown as Vc}from"@earendil-works/pi-tui";var Jc={heading:t=>pt(ut(t)),link:t=>wi(ut(t)),linkUrl:t=>Ne(t),code:t=>zc(t),codeBlock:t=>t,codeBlockBorder:t=>Ne(t),quote:t=>yi(t),quoteBorder:t=>Ne(t),hr:t=>Ne(t),listBullet:t=>ut(t),bold:t=>pt(t),italic:t=>yi(t),strikethrough:t=>Kc(t),underline:t=>wi(t)},mt={selectedPrefix:t=>ut(t),selectedText:t=>pt(t),description:t=>Ne(t),scrollInfo:t=>Ne(t),noMatch:t=>Ne(t)},vi={borderColor:t=>Ne(t),selectList:mt};var ft=class{constructor(e,n){this.linkOrigin=n;this.content=u(e),this.markdown=new Vc(nr(this.content,n),0,0,Jc)}linkOrigin;content;markdown;cache;getText(){return this.content}setText(e){let n=u(e);n!==this.content&&(this.content=n,this.markdown.setText(nr(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(Io):[];return this.cache={width:n,lines:r},r}};function jt(t,e){return t==="user"?`${pt(Hc(e?u(e):"You"))}`:`${pt(ut("Oracle"))}`}function bi(t,e,n){let r=ut("\u2502"),o=Math.max(1,e-4);return["",`${r} ${jt("user",n)}`,...t.render(o).map(i=>`${r} ${pt(i)}`),""]}function Oe(t){return t.shared||t.visibility==="company"?`Shared with the company${t.entityKey?` \xB7 ${u(t.entityKey)}`:""}`:"Private to you"}function Oi(t){return t.shared||t.visibility==="company"?"shared":"private"}import{createReadStream as ul}from"node:fs";import{stat as pl}from"node:fs/promises";import{basename as fl}from"node:path";import{setTimeout as Ni}from"node:timers/promises";var ki={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 Ai(t){let e=t.slice(t.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(ki,e)?ki[e]:null}var Or="/api/v1/oracle";var xi="workspace";function qe(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`${qe(t)}${Or}${e}`}function Yc(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(qe(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 Qc(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]=Yc(n[i],e,i)}return o}async function He(t,e=fetch,n,r){let o=qe(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 yt(i,ke(o,"/config")),a=N(ht(s,i.status)),c=`${o}${Or}`;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:Qc(a.oauth,o)};if(a.identity!==void 0){let l=N(a.identity);if(typeof l.uid!="string"||typeof l.companyId!="string")throw new f("Oracle server returned an invalid identity.",m.API_ERROR);let O=typeof l.companyName=="string"?u(l.companyName).trim().slice(0,120):"";p.identity={uid:u(l.uid),companyId:u(l.companyId),...O?{companyName:O}:{}}}if(a.capabilities!==void 0){let l=N(a.capabilities);if(![l.read,l.write,l.approve].every(O=>typeof O=="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=N(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=N(a.limits);if(Object.values(l).some(O=>!["string","number","boolean"].includes(typeof O)&&O!==null))throw new f("Oracle server returned invalid limits metadata.",m.API_ERROR);p.limits=Object.fromEntries(Object.entries(l).map(([O,k])=>[u(O),typeof k=="string"?u(k):k]))}return Array.isArray(a.commands)&&(p.commands=Xc(a.commands)),p}function Xc(t){let e=[];for(let n of t.slice(0,50)){let r=N(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=qe(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 He(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 He(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:N(e).sessions;if(!Array.isArray(n))throw new f("Oracle server returned an invalid sessions list.",m.API_ERROR);return n.map(Ii)}async createSession(e={},n){let r=await this.json("POST","/sessions",e,n),o=N(r);return!Array.isArray(o.messages)&&typeof o.sessionId=="string"?this.getSession(o.sessionId,n):Ri(r)}async getSession(e,n){return Ri(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 gt(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let d=await yt(i,ke(this.endpoint,`/sessions/${e}/turns`)),p=ht(d,i.status);throw new f(`Oracle server did not start a stream (${Zc(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 gt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Dt(a,o);return}let d=ht(await yt(a,ke(this.endpoint,s)),a.status),p=Array.isArray(d)?d:N(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 kr(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 gt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Dt(a,o);return}let d=ht(await yt(a,ke(this.endpoint,s)),a.status),p=Array.isArray(d)?d:N(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 kr(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 gt(o),tl(ht(await yt(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 gt(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 gt(i),ht(await yt(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),O=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&&O&&(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*Dt(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(`
|
|
361
|
-
`);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
|
|
405
|
+
`,"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 For where one crew stands, call `anyforge_get_crew` with `messages: 0`: the\n crew leads with `status`, the halt reason, its work item and pull request,\n and the default summary shape leaves out the large snapshots (named in\n `omittedFields`). Pass `shape: "full"` only for a left-out field.\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, including the lead and whether the program is long-lived;\n `anyforge_set_program_request_intake` switches its incoming requests, and\n `anyforge_set_program_status_line` writes its status line.\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. Labels are `tags`: set them on create, and change them with\n `action: \'update\'` and `addTags` / `removeTags` (no revision needed, at any\n status, finished items included). Replacing the whole list with `tags` needs\n `ifClassificationRevision`, the item\'s `classificationRevision`.\n `anyforge_get_work_item` returns `nextActions`, every status move the key can\n make now with the arguments to send; `status` with no action moves an item\n back to BACKLOG, REFINED or READY.\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 `anyforge_fix_crew_ci`, `anyforge_resume_crew_with_pr` and\n `anyforge_decide_crew_permission` are the Build view\'s recovery actions.\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'},ff=Object.entries(gn).map(([t,e])=>({uri:t==="SKILL.md"?li:`${li}/${t}`,name:t==="SKILL.md"?me:`${me}/${t.replace(/^references\//,"")}`,title:t==="SKILL.md"?"AnyForge platform guide":t,description:t==="SKILL.md"?Sc:`Reference for the ${me} skill: ${t}`,mimeType:Ec,size:new TextEncoder().encode(e).byteLength}));var Tc=gn["SKILL.md"],mf=Tc.replace(/^---[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/,"");var pi={agents:".agents/skills",claude:".claude/skills",codex:".codex/skills"};function Lc(t){let e=t.scope==="user"?t.homeDir??Cc():t.cwd??process.cwd();if(t.scope==="user"&&t.client==="codex"){let n=t.codexHome??process.env.CODEX_HOME??Ge(e,".codex");return Ge(n,"skills",me)}return Ge(e,pi[t.client],me)}function Nc(t){if(t.scope!=="project"&&t.scope!=="user")throw new w(`--scope must be project or user, got "${t.scope}".`);if(!Object.hasOwn(pi,t.client))throw new w(`--client must be agents, claude, or codex, got "${t.client}".`)}async function wr(t){try{return(await qt.lstat(t)).isSymbolicLink()}catch(e){if(e.code==="ENOENT")return!1;throw e}}async function qc(t){Nc(t);let e=Lc(t),n=Object.entries(gn),r=n.map(([s])=>Ge(e,s));if(await wr(e))throw new w(`Refusing to install through the symbolic link ${e}.`);let o=new Set(n.map(([s])=>di(s)).filter(s=>s!=="."));for(let s of o)if(await wr(Ge(e,s)))throw new w(`Refusing to install through the symbolic link ${Ge(e,s)}.`);if(!t.force){let s=[];for(let a of r)try{await qt.access(a),s.push(a)}catch(c){if(c.code!=="ENOENT")throw c}if(s.length>0)throw new w(`The ${me} skill already exists at ${e}.`,"Use --force only if you want to replace AnyForge-managed files in that directory.")}await qt.mkdir(e,{recursive:!0});let i=[];for(let[s,a]of n){let c=Ge(e,s);if(await qt.mkdir(di(c),{recursive:!0}),await wr(c))throw new w(`Refusing to replace the symbolic link ${c}.`);await qt.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 fi(t){if(t.subcommand!=="install")throw new w(`Unknown skill command${t.subcommand?`: ${t.subcommand}`:""}.`,"Use `anyforge skill install anyforge-platform`.");if(t.name!==me)throw new w(`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 w(`--scope must be project or user, got "${e}".`);if(!["agents","claude","codex"].includes(n))throw new w(`--client must be agents, claude, or codex, got "${n}".`);let r=await qc({scope:e,client:n,force:t.force});if(t.json){process.stdout.write(JSON.stringify(r)+`
|
|
406
|
+
`);return}console.log($c("Installed ")+`${r.name} ${ui(`(${r.scope}, ${r.client})`)}`),console.log(` ${r.directory}`),console.log(ui(` ${r.files.length} files installed. Re-run with --force to refresh them.`)),r.scope==="project"&&r.client==="agents"&&console.log(Pc(" This portable location is detected by AnyForge repository analysis."))}import{randomUUID as ad}from"node:crypto";import{open as cd,realpath as ld,stat as dd}from"node:fs/promises";import{basename as ud,join as pd,resolve as fd}from"node:path";import{bold as Gr,cyan as vs,dim as Se,yellow as Un}from"kleur/colors";import{ProcessTerminal as md,SelectList as gd,Text as hd,TuiAltScreen as wd}from"@earendil-works/pi-tui";var Fc=new Set(["find_callers","find_definition","find_imports","trace_path","get_neighbors","graph_stats"]);function Uc(t){return Fc.has(t)?"querying the code graph":t==="search_code_semantic"?"searching the code index":t==="search_codebase_docs"?"searching codebase docs":null}function lt(t){let e=Uc(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 Mc=new Set(["ask_user","present_catch_up_decision","present_memory_candidates","present_skill_proposal"]);function dt(t){return typeof t=="string"&&Mc.has(t)}import{truncateToWidth as Kt,wrapTextWithAnsi as Mi}from"@earendil-works/pi-tui";import{cyan as Cr,dim as Ve,green as ji,italic as yl,red as $r,yellow as Pr}from"kleur/colors";import{truncateToWidth as Ft,visibleWidth as yr,wrapTextWithAnsi as jc}from"@earendil-works/pi-tui";import{bold as Dc,dim as Bc}from"kleur/colors";function K(t){return t<1024?`${t} B`:t<1024*1024?`${Ut(t/1024)} KiB`:`${Ut(t/(1024*1024))} MiB`}function Mt(t){let e=Math.max(0,t);if(e<1e4)return`${Ut(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 vr(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?`${Ut(t/1e3,t<1e5?1:0)}k`:`${Ut(t/1e6)}M`}function Ut(t,e=1){return t.toFixed(e).replace(/\.0+$/,"")}function Wc(t){return u(t).replace(/(Ms|Bytes)$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase().trim()}function Gc(t,e){return typeof e=="boolean"?e?"on":"off":typeof e=="string"?u(e):/Ms$/.test(t)?Mt(e):/Bytes$/.test(t)?K(e):/tokens?/i.test(t)?de(e):String(e)}function mi(t){return t?Object.entries(t).filter(e=>e[1]!==null).map(([e,n])=>`${Wc(e)} ${Gc(e,n)}`):[]}function br(t,e,n=2){let r=Math.min(16,Math.max(...t.map(([s])=>yr(s)),0)+2),o=Math.max(10,e-n-r),i=[];for(let[s,a]of t)jc(a,o).forEach((d,p)=>{let l=p===0?Bc(Ft(s,r-1).padEnd(r)):" ".repeat(r);i.push(Ft(`${" ".repeat(n)}${l}${d}`,e))});return i}function gi(t,e,n){return[Ft(Dc(t),n),...br(e,n)]}function hi(t,e,n){let r=yr(n),o=e.filter(Boolean).join(" \xB7 ");if(r+4>t)return Ft(o||n,t);let i=t-r-2,s=Ft(o,i),a=Math.max(2,t-yr(s)-r);return`${s}${" ".repeat(a)}${n}`}import{bold as pt,cyan as ut,dim as Ne,green as Hc,italic as wi,strikethrough as Kc,underline as yi,yellow as zc}from"kleur/colors";import{Markdown as Vc}from"@earendil-works/pi-tui";var Jc={heading:t=>pt(ut(t)),link:t=>yi(ut(t)),linkUrl:t=>Ne(t),code:t=>zc(t),codeBlock:t=>t,codeBlockBorder:t=>Ne(t),quote:t=>wi(t),quoteBorder:t=>Ne(t),hr:t=>Ne(t),listBullet:t=>ut(t),bold:t=>pt(t),italic:t=>wi(t),strikethrough:t=>Kc(t),underline:t=>yi(t)},mt={selectedPrefix:t=>ut(t),selectedText:t=>pt(t),description:t=>Ne(t),scrollInfo:t=>Ne(t),noMatch:t=>Ne(t)},vi={borderColor:t=>Ne(t),selectList:mt},ft=class{constructor(e,n){this.linkOrigin=n;this.content=u(e),this.markdown=new Vc(nr(this.content,n),0,0,Jc)}linkOrigin;content;markdown;cache;getText(){return this.content}setText(e){let n=u(e);n!==this.content&&(this.content=n,this.markdown.setText(nr(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(Io):[];return this.cache={width:n,lines:r},r}};function jt(t,e){return t==="user"?`${pt(Hc(e?u(e):"You"))}`:`${pt(ut("Oracle"))}`}function bi(t,e,n){let r=ut("\u2502"),o=Math.max(1,e-4);return["",`${r} ${jt("user",n)}`,...t.render(o).map(i=>`${r} ${pt(i)}`),""]}function ke(t){return t.shared||t.visibility==="company"?`Shared with the company${t.entityKey?` \xB7 ${u(t.entityKey)}`:""}`:"Private to you"}function ki(t){return t.shared||t.visibility==="company"?"shared":"private"}import{createReadStream as ul}from"node:fs";import{stat as pl}from"node:fs/promises";import{basename as fl}from"node:path";import{setTimeout as Ni}from"node:timers/promises";var Oi={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 Ai(t){let e=t.slice(t.lastIndexOf(".")+1).toLowerCase();return Object.hasOwn(Oi,e)?Oi[e]:null}var kr="/api/v1/oracle";var xi="workspace";function qe(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 Oe(t,e){return`${qe(t)}${kr}${e}`}function Yc(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(qe(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 Qc(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]=Yc(n[i],e,i)}return o}async function He(t,e=fetch,n,r){let o=qe(t),i;try{i=await e(Oe(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 wt(i,Oe(o,"/config")),a=N(ht(s,i.status)),c=`${o}${kr}`;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:Qc(a.oauth,o)};if(a.identity!==void 0){let l=N(a.identity);if(typeof l.uid!="string"||typeof l.companyId!="string")throw new f("Oracle server returned an invalid identity.",m.API_ERROR);let k=typeof l.companyName=="string"?u(l.companyName).trim().slice(0,120):"";p.identity={uid:u(l.uid),companyId:u(l.companyId),...k?{companyName:k}:{}}}if(a.capabilities!==void 0){let l=N(a.capabilities);if(![l.read,l.write,l.approve].every(k=>typeof k=="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=N(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=N(a.limits);if(Object.values(l).some(k=>!["string","number","boolean"].includes(typeof k)&&k!==null))throw new f("Oracle server returned invalid limits metadata.",m.API_ERROR);p.limits=Object.fromEntries(Object.entries(l).map(([k,O])=>[u(k),typeof O=="string"?u(O):O]))}return Array.isArray(a.commands)&&(p.commands=Xc(a.commands)),p}function Xc(t){let e=[];for(let n of t.slice(0,50)){let r=N(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=qe(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 He(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 He(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:N(e).sessions;if(!Array.isArray(n))throw new f("Oracle server returned an invalid sessions list.",m.API_ERROR);return n.map(Ii)}async createSession(e={},n){let r=await this.json("POST","/sessions",e,n),o=N(r);return!Array.isArray(o.messages)&&typeof o.sessionId=="string"?this.getSession(o.sessionId,n):Ri(r)}async getSession(e,n){return Ri(await this.json("GET",`/sessions/${encodeURIComponent(e)}`,void 0,n))}async submitTurn(e,n,r,o){let i,s=Oe(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 gt(i),!(i.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){let d=await wt(i,Oe(this.endpoint,`/sessions/${e}/turns`)),p=ht(d,i.status);throw new f(`Oracle server did not start a stream (${Zc(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 gt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Dt(a,o);return}let d=ht(await wt(a,Oe(this.endpoint,s)),a.status),p=Array.isArray(d)?d:N(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 Or(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 gt(a),(a.headers.get("content-type")?.toLowerCase()??"").includes("text/event-stream")){yield*Dt(a,o);return}let d=ht(await wt(a,Oe(this.endpoint,s)),a.status),p=Array.isArray(d)?d:N(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 Or(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 gt(o),tl(ht(await wt(o,Oe(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 gt(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 gt(i),ht(await wt(i,Oe(this.endpoint,n)),i.status)}async authenticatedFetch(e,n,r={},o=2e4){try{let i=Oe(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=O=>this.#t(i,{...r,method:e,headers:{...s,Authorization:`Bearer ${O}`},redirect:"error",...c?{signal:c}:{}}),p=await this.#e(),l=await d(p),k=r.body===void 0||typeof r.body=="string"||r.body===null;if(l.status===401&&this.source==="oauth"){let O=await this.#e(p);O!==p&&k&&(l=await d(O))}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*Dt(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(`
|
|
407
|
+
`);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 k=Or(l);return k.type==="finalizing"?k:k.idx<=c?null:(c=k.idx,k)};try{for(;;){let{value:l,done:k}=await n.read();l?.byteLength&&e?.();let O=r.decode(l,{stream:!k});if(o+=O,i+=hn(O),i>2*1024*1024)throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR);let R=0,b=o.indexOf(`
|
|
362
408
|
`,R);for(;b>=0;){let g=o.slice(R,b).replace(/\r$/,"");if(i-=hn(o.slice(R,b+1)),R=b+1,g===""){let x=d();x&&(yield x)}else if(!g.startsWith(":")){let x=g.indexOf(":"),L=x<0?g:g.slice(0,x),j=x<0?"":g.slice(x+1).replace(/^ /,"");if(L==="data"&&(s.push(j),a+=hn(j)+1,a>2*1024*1024))throw new f("Oracle stream event exceeded the 2 MiB protocol limit.",m.API_ERROR)}b=o.indexOf(`
|
|
363
|
-
`,R)}if(o=o.slice(R),O)break}if(o.trim()){let l=o.replace(/\r$/,"");if(l.startsWith("data:")&&(s.push(l.slice(5).replace(/^ /,"")),a+=hn(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 hn(t){return Buffer.byteLength(t,"utf8")}function Ar(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 N(t){return!t||typeof t!="object"||Array.isArray(t)?{}:t}function Zc(t){if(typeof t=="string")return u(t);try{return u(JSON.stringify(t))}catch{return"unexpected response"}}function ht(t,e){let n=N(t);if(n.ok===!1){let r=_i(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 yt(t,e){try{return await t.json()}catch{throw new f(`Non-JSON response from ${e} (HTTP ${t.status}).`,m.FAILURE)}}async function gt(t){let e;try{e=await t.clone().json()}catch{e=null}let n=N(e),r=_i(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,el(t))}function el(t){try{return t.url?new URL(t.url).origin:void 0}catch{return}}function _i(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 Ii(t){let e=N(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 Ri(t){let e=N(t),n=Ii(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=N(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(il):void 0,c=Array.isArray(s.files)?s.files.map(sl):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:nl(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:rl(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof e.activeStreamId=="string"?{activeStreamId:e.activeStreamId}:{}}}function kr(t){let e=N(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 tl(t){let e=N(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 nl(t){let e=N(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=N(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=N(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 rl(t){let e=N(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(ol)}}function ol(t){let e=N(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 il(t){let e=N(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 sl(t){let e=N(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 al}from"node:crypto";import{promises as Ae}from"node:fs";import{homedir as cl}from"node:os";import{join as Rr}from"node:path";import*as xr from"proper-lockfile";function vn(){return Rr(cl(),".anyforge")}function wn(){return Rr(vn(),"oracle")}function Ei(){return Rr(vn(),"oracle.last-session")}function ll(){return{version:2,active:{},logins:[]}}function yn(t){return t===void 0||typeof t=="string"}function Si(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")||!yn(e.companyId)||!yn(e.companyName)||!yn(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 dl(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;if(e.version===1){let o=Si(e);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(e.version!==2||!Array.isArray(e.logins)||!yn(e.endpoint)||!e.active||typeof e.active!="object"||Array.isArray(e.active))return null;let n=e.logins.map(Si);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=wn(),e;try{e=await Ae.readFile(t,"utf8")}catch(o){if(o.code==="ENOENT")return ll();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=dl(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 Bt(t){if(!t.logins.length){await Ae.rm(wn(),{force:!0});return}await Li(wn(),t)}async function bn(t){return An(async()=>{let e=await ne(),n=await t(e);return await Bt(e),n})}function xe(t,e){return t.logins.filter(n=>n.endpoint===e)}function On(t,e){let n=xe(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 Ti(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 _r(t,e){t.logins=t.logins.filter(o=>!(o.endpoint===e.endpoint&&o.clientId===e.clientId));let n=xe(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 Ci(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 Ke(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 $i(){let t;try{t=await Ae.readFile(Ei(),"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 Pi(t,e,n=""){let r=(await $i()).filter(o=>!(o.endpoint===e&&o.companyId===n));await Li(Ei(),{version:2,sessions:[...r,{endpoint:e,companyId:n,sessionId:t}]})}async function kn(t,e=""){return(await $i()).find(n=>n.endpoint===t&&n.companyId===e)?.sessionId}async function Li(t,e){await Ae.mkdir(vn(),{recursive:!0,mode:448});let n=`${t}.${al()}.tmp`;try{await Ae.writeFile(n,`${JSON.stringify(e,null,2)}
|
|
364
|
-
`,{mode:384,flag:"wx"}),await Ae.rename(n,t),await Ae.chmod(t,384)}finally{await Ae.rm(n,{force:!0}).catch(()=>{})}}async function An(t){await Ae.mkdir(vn(),{recursive:!0,mode:448});let e=wn(),n=`${e}.refresh-admission`,r;try{r=await xr.lock(n,{realpath:!1,lockfilePath:n,stale:Number.MAX_SAFE_INTEGER,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}})}catch(o){throw o.code==="ELOCKED"?new Error(`Oracle credential access is still locked; another CLI command may be running. An abrupt exit can leave this guard behind. Stop all AnyForge CLI commands before removing the guard directory at ${n}.`,{cause:o}):o}try{let o=await xr.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 o()}}finally{await r()}}var Ir=8192,qi=10*1048576,ml=20*1048576,Fi=10,Ui=3;function Sr(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!dt(t.name)}var Re=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:Sr(e,this.toolIds)?this.answer="":e.type==="question"?this.question=Gt(e):e.type==="error"?this.error=Tr(e):e.type==="done"?this.done=e:e.type==="navigator_file_created"&&gl(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 vt(t,e,n,r,o,i=new Re){let s=async l=>{let O=i.streamId;i.accept(l)&&(i.streamId&&i.streamId!==O&&r.onStream?.(i.streamId),await r.onEvent(l))},a=()=>Er(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 O of Dt(l,r.onActivity))await s(O)}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 Dt(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 Er(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<Ui;){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 Ni(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof f&&c.exitCode===m.API_ERROR||(a+=1,a>=Ui))throw c;await Ni(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 Wt(t,e){let n=e.done,r=n?.servedBy,o=ze(n?.tokens),i=Y(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(Fe):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 C(t,e){return typeof t=="string"?u(t).slice(0,e):""}function Tr(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 Gt(t){let e=t.question;if(!e||typeof e!="object")throw new f("Oracle question event omitted its question card.",m.API_ERROR);return hl(e)}function Fe(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 gl(t,e){let n=Fe(e);t.some(r=>r.fileId===n.fileId)||t.push(n)}function hl(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 Y(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function ze(t){if(typeof t=="number"&&Number.isFinite(t))return{totalTokens:t};if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,n=Y(e.in)??Y(e.input)??Y(e.inputTokens),r=Y(e.cacheRead),o=Y(e.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=Y(e.out)??Y(e.output)??Y(e.outputTokens),a=Y(e.total)??Y(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 bt(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 Ot(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 Ht(t,e,n){await Pi(t,e,n).catch(()=>{})}async function xn(t,e,n,r){if(!n.length)return[];if(n.length>Fi)throw new y(`Attach no more than ${Fi} files per turn.`);let o=0,i=[];for(let a of n){let c=await pl(a);if(!c.isFile())throw new y(`Attachment is not a file: ${a}`);if(c.size>qi)throw new y(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let d=await yl(a,qi);if(o+=d.byteLength,o>ml)throw new y("Attachments exceed the 20 MiB total CLI limit.");let p=fl(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",l=Ai(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 yl(t,e){let n=[],r=0;for await(let o of ul(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 Je=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],vl=2e4,bl=6,Ol={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},kl={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=Y(e.round),this.maxRounds=Y(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: ${C(e.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${C(e.filename,200)}: ${C(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:`${C(r.name,160)}: ${C(r.message,500)}`})}break;case"provider_fallback":{let n=C(e.from,120),r=C(e.to,120),o=C(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=Fe(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:Tr(e)});break;case"done":this.done=e;break}}recordToolCall(e){let n=typeof e.id=="string"?e.id:void 0,r=Sr(e,this.toolIds),o=typeof e.name=="string"?e.name:"";if(dt(o))return;let i=C(typeof e.label=="string"?e.label:lt(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&&Ol[this.serverPhase]||"Thinking"}statusLine(e,n){let r=[this.label(),vr(e-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=e-this.lastSignalAt;return o>=vl&&this.phase!=="cancelling"&&r.push(`no update for ${vr(o)}`),n&&e-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let e=[],n=Mt((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=ji("\u2713"):this.end==="awaiting_input"?o=Cr("?")+" Waiting for your answer":this.end==="cancelled"?o=Pr("\u2717")+" Interrupted":this.end==="error"?o=$r("\u2717")+" Error":o=Pr("\u2717")+` Stopped: ${r&&kl[r]||"no final answer"}`,e.push(n),this.tools.length&&e.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=ze(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=zt(this.done?.servedBy);return s&&e.push(s),`${o} ${Ve(e.join(" \xB7 "))}`}};function zt(t){if(!t||typeof t!="object")return"";let e=t,n=C(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"?`: ${C(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${C(r.from,100)}${o})`}return n}var _n=class{constructor(e,n){this.activity=e;this.view=n;this.answer=new ft("",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=[jt("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=Cr(Je[this.view.frame()%Je.length]??"*");r.push(Kt(` ${o} ${Ve(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"?$r:o.tone==="warning"?Pr:Ve;r.push(...Mi(i(o.text),e))}return n.finished&&r.push(Kt(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[Kt(Ve(` \u23BF ${a}`),e)]}let r=this.activity.finished?n:n.slice(-bl),o=n.length-r.length,i=[];return o&&i.push(Kt(Ve(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),e)),r.forEach((s,a)=>{if(s.note)for(let l of Mi(s.note,Math.max(10,e-4)).slice(0,3))i.push(` ${Ve(wl(l))}`);let c=s.status==="running"?Cr(Je[this.view.frame()%Je.length]??"*"):s.status==="ok"?ji("\u2713"):$r("\u2717"),d=a===0&&!o?" \u23BF ":" ",p=s.spawnId?` ${Ve("agent \xB7")} `:"";i.push(Kt(`${d}${p}${c} ${s.label}`,e))}),i}};import{truncateToWidth as _l,visibleWidth as Il,wrapTextWithAnsi as Bi}from"@earendil-works/pi-tui";import{bold as Wi,cyan as Sl,dim as _e}from"kleur/colors";import{CombinedAutocompleteProvider as Al}from"@earendil-works/pi-tui";var Vt=[{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"]}],xl=[{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 Lr(t){let e=new Set(Vt.flatMap(n=>[n.name,...n.aliases??[]]));return(t??xl).filter(n=>!e.has(n.name))}function Di(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=Vt.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 Nr(t,e){let n=[...Vt.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=>Rl(i.completions??[],s)}:{}})),...t.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new Al(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 Rl(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 Jt(t,e){return t?e?"write + approve":"write":"read only"}function kt(t){return t?`${t.kind} ${u(t.id)}`:"no context"}function Ue(t){return t?u(t.companyName||t.companyId):""}function Gi(t,e){let n=[`${Wi(Sl("\u26A1\uFE0E AnyForge Oracle"))}${_e(` \xB7 v${u(t.version)}`)}`,`${t.workspace?`${Wi(t.workspace)} \xB7 `:""}${Oe(t.session)} \xB7 ${t.access} \xB7 ${kt(t.context)}`,...t.session.messages.length?[`Resumed: ${u(t.session.title)}`]:[],_e("Only your messages and the files you attach leave this terminal."),_e("/help commands \xB7 /status details")];if(e<24)return n.flatMap(s=>Bi(s,Math.max(1,e)));let r=Math.min(e,76),o=r-4,i=[_e(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>Bi(a,o))){let a=_l(s,o);i.push(`${_e("\u2502")} ${a}${" ".repeat(Math.max(0,o-Il(a)))} ${_e("\u2502")}`)}return i.push(_e(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function Hi(t){let e=(r,o)=>r?`${_e(r)} ${o}`:o,n=[{title:"Commands",rows:Vt.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(["",_e("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 qr(t){let n=[["Model",`workspace default ${t.model?`${u(t.model.provider)}/${u(t.model.id)}`:"chosen by the server"}`]],r=zt(t.lastServedBy);if(r){let o=t.lastServedBy,i=C(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function Fr(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 Ur(t){if(!t?.finished)return[];let e=[Mt((t.endedAt??t.startedAt)-t.startedAt)];e.push(`${t.toolCount} tool${t.toolCount===1?"":"s"}${t.failedCount?` (${t.failedCount} failed)`:""}`);let n=Y(t.done?.rounds);n!==void 0&&e.push(`${n} round${n===1?"":"s"}`);let r=ze(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=C(t.done?.exitReason,40);return e.push(o?o.replaceAll("_"," "):t.end??""),[["Last turn",e.filter(Boolean).join(" \xB7 ")]]}function Ki(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=mi(t.limits);return[["Session",`${t.session.sessionId} \xB7 ${u(t.session.title)||"Untitled"}`],["Audience",Oe(t.session)],["Access",`this session ${Jt(t.write,t.approve)} \xB7 grant ${t.grantScopes.join(", ")||"none"} \xB7 workspace role ${e}`],["Context",`${kt(t.context)}${n}`],...qr(t),...Ur(t.lastTurn),...Fr(t.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...t.identity?[["Workspace",El(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 El(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 zi(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 Pl}from"node:crypto";import{stat as Ll}from"node:fs/promises";import{basename as Vi}from"node:path";import{bold as Nl,cyan as Ji,dim as Yi,red as xt,yellow as ql}from"kleur/colors";import{Container as Fl,Editor as Ul,ProcessTerminal as Ml,ScrollView as jl,SelectList as Qi,TuiAltScreen as Dl,VStack as Bl,getNativeClipboard as Wl,matchesKey as En,truncateToWidth as Xi,wrapTextWithAnsi as Gl}from"@earendil-works/pi-tui";import{bold as Tl,cyan as Mr,dim as In,yellow as Cl}from"kleur/colors";import{Input as $l,Text as Ye,matchesKey as At}from"@earendil-works/pi-tui";var Sn=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 $l({prompt:`${Mr("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(`${Tl(Mr(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(Cl(this.errorMessage),0,0).render(r)),i.map(s=>`${Mr("\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(At(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(e);return}if(At(e,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(At(e,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(At(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(At(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(At(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":"",O=`${d} ${s+1}. ${c} ${u(i.label)}${p}${l}`;o.push(...new Ye(O,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 Zi=1500,Hl=100,Kl="\x1B]133;A\x07";function Rt(t){return{render:t,invalidate(){}}}function es(t,e,n,r){let o=new ft(e,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>t==="user"?bi(o,i,n).map((s,a)=>a===1?`${Kl}${s}`:s):[jt("assistant"),...o.render(i)]}}async function ts(t){let{client:e,config:n}=t,r=new Ml,o=new Dl(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new Fl,s=new jl(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new Ul(o,vi),c=Lr(n.commands),d=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,p=t.session,l=t.initialContext,O=t.contextText,k=t.canWrite,R=t.canApprove,b,g,x,L,j=[...t.initialFiles],G=!1,he=!1,et=!1,Bn=0,tt,Kr=0,Qt=!1,Me="",St,Xt,nt=[],rt={turns:0,inputTokens:0,outputTokens:0,tools:0},Zt=new Map,ye,Cs=Rt(h=>{if(b){let w=Ji(Je[Bn%Je.length]??"*");return[Xi(`${w} ${b.activity.statusLine(Date.now(),d())}`,h)]}return ye?[Xi(`${Ji("?")} Waiting for your answer to the question above`,h)]:[]}),$s=Rt(h=>{let w=[Ue(n.identity),Jt(k,R),kt(l),Oi(p),j.length?`${j.length} file${j.length===1?"":"s"} attached`:"",g!==void 0?"1 message queued":""];return[Yi(hi(h,w,b?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new Bl([{component:s,basis:0,grow:1,minSize:1},{component:Cs,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:$s,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=Yi)=>{let v=u(h);Ee(Rt(A=>Gl(w(v),Math.max(1,A))))},en=(h,w)=>{Ee(Rt(v=>["",...gi(h,w,v)]))},Ps=()=>{tt||(tt=setInterval(()=>{Bn+=1,oe()},Hl),tt.unref())},Wn=()=>{tt&&clearInterval(tt),tt=void 0},ot=()=>{G||o.hasOverlay()||o.setFocus(ye??a)},zr=h=>{x=h;let w=Zt.get(h.id);w&&i.removeChild(w);let v=new Sn(o,h,A=>{Zt.delete(h.id),ye===v&&(ye=void 0),i.removeChild(v),qs(A??{questionId:h.id,skipped:!0,answers:[]}),ot()});Zt.set(h.id,v),ye=v,Ee(v)},tn=h=>{i.clear(),Zt.clear(),ye=void 0,p=h,x=void 0,L=void 0,nt=[],Me="";let w={version:t.version,workspace:Ue(n.identity),session:h,access:Jt(k,R),context:l};Ee(Rt(A=>Gi(w,A)));let v;for(let A of h.messages){Ee(es(A.role,A.content,A.authorName,e.endpoint)),A.role==="assistant"&&(Me=A.content),A.question&&(v=A.question),A.questionResponse&&(V("A stored response was recorded for this question."),v?.id===A.questionResponse.questionId&&(v=void 0));for(let _ of A.files??[])nt.some($=>$.fileId===_.fileId)||nt.push(_),V(`File: ${_.filename} (${_.mediaType}, ${K(_.sizeBytes)}) \xB7 anyforge oracle download ${h.sessionId} ${_.fileId} --output .`)}v&&zr(v),ot(),Ht(h.sessionId,e.endpoint,n.identity?.companyId)},Vr,Ls=new Promise(h=>{Vr=h}),nn=()=>{G||(G=!0,Wn(),b?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),Vr())},Gn=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 A;if(w)try{A=bt(await e.cancelTurn(p.sessionId,w))}catch(_){A=`Could not confirm server cancellation: ${z(_)}`}else if(v)try{A=bt(await e.cancelRequest(p.sessionId,v))}catch(_){A=`Could not confirm server cancellation by request id: ${z(_)}`}else A="The turn had not reached the server; nothing was sent.";h.activity.note("info",A),oe()},it=async h=>{if(b||G)return;if(h.kind==="submit"&&h.response){let P=x;if(!P||P.id!==h.response.questionId){F("That question is no longer active. Resume the session to refresh it.");return}V(zl(P,h.response)),x=void 0}else h.kind==="submit"&&Ee(es("user",h.text));let w=p.sessionId,v=new Rn(Date.now(),w),A=new _n(v,{expanded:()=>et,frame:()=>Bn,linkOrigin:e.endpoint}),_={activity:v,view:A,controller:new AbortController,cancelling:!1};b=_,Ee(A),Ps();let $=new Re,Q=P=>{v.apply(P,Date.now()),P.type==="question"&&(zr(Gt(P)),ot()),oe()},je={onEvent:Q,onStream:P=>{_.streamId=P},onResponse:()=>{v.phase==="sending"&&v.setPhase("preparing",Date.now())},onActivity:()=>v.touch(Date.now())},ie="error";try{if(h.kind==="replay"){$.streamId=h.streamId,_.streamId=h.streamId,v.setPhase("thinking",Date.now());let P=await Er(e,w,$,async ue=>{$.accept(ue)&&Q(ue)},_.controller.signal,je.onActivity);ie=Wt(w,P).status}else{let P=h.response?[]:j.splice(0),ue=await xn(e,w,P,(Hn,Ks)=>{v.setUploading(Hn,Ks,Date.now()),oe()});_.controller.signal.throwIfAborted(),v.setPhase("sending",Date.now());let rn=Pl();_.requestId=rn;let Gs={requestId:rn,text:h.response?"":h.text,readOnly:!k,...R?{allowApprove:!0}:{},...l?{context:l}:{},...O?{contextText:O}:{},...h.response?{questionResponse:h.response}:{},...ue.length?{attachments:ue.map(Hn=>({fileId:Hn.fileId}))}:{}},Hs=await vt(e,w,Gs,je,_.controller.signal,$);ie=Wt(w,Hs).status}}catch(P){if(_.controller.signal.aborted)ie="cancelled";else{ie="error";let ue=P instanceof f&&P.hint?` ${u(P.hint)}`:"";v.note("error",`${z(P)}${ue}`)}}finally{v.finish(ie,Date.now()),b=void 0,Wn(),G||(he=ie==="cancelled"),St=v,v.answer.trim()&&(Me=v.answer),v.done?.servedBy&&(Xt=v.done.servedBy);let P=ze(v.done?.tokens);rt.turns+=1,rt.tools+=v.toolCount,rt.inputTokens+=P?.inputTokens??0,rt.outputTokens+=P?.outputTokens??0;for(let ue of v.files)nt.some(rn=>rn.fileId===ue.fileId)||nt.push(ue);oe()}if(!G){if(_.streamId&&ie!=="cancelled"){let P=await e.getSession(w).catch(()=>{});P&&P.sessionId===p.sessionId&&(p=P)}Ns(ie)}},Ns=h=>{if(!(G||b)){if(L){let w=L;L=void 0,it({kind:"submit",text:"",response:w});return}if(g!==void 0){let w=g;if(g=void 0,h==="complete"&&!ye){it({kind:"submit",text:w});return}a.getText()||a.setText(w),F("Your queued message is back in the input; press Enter to send it.")}ot(),oe()}},qs=h=>{if(b){L=h,F("Answer saved; Oracle continues once the current turn settles.");return}it({kind:"submit",text:"",response:h})},Jr=async h=>{try{let w=await e.getSession(h);tn(w),w.activeStreamId&&it({kind:"replay",streamId:w.activeStreamId}),F(`Resumed ${w.title||w.sessionId}.`)}catch(w){V(z(w),xt)}},Fs=async()=>{try{let h=await e.listSessions();if(!h.length){F("No saved conversations to resume.");return}let w=new Qi(h.map($=>({value:$.sessionId,label:$.title,description:Oe($)})),Math.min(12,h.length),mt),v=await kn(e.endpoint,n.identity?.companyId),A=v?h.findIndex($=>$.sessionId===v):-1;A>=0&&w.setSelectedIndex(A);let _=o.showOverlay(w,{width:"85%",maxHeight:"80%",anchor:"center"});w.onSelect=$=>{_.hide(),Jr($.value)},w.onCancel=()=>{_.hide(),ot()},_.focus()}catch(h){V(z(h),xt)}},Yr=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(Ot(l,h));tn(w),F(`Started a new conversation \xB7 ${Oe(w)}.`)}catch(w){V(z(w),xt)}},Qr=async(h,w)=>{let v=u(re(w));if(w.companyId&&w.companyId===n.identity?.companyId){F(`Already in ${v}.`);return}Qt=!0;try{F(`Connecting to ${v}...`);let A=await h.connect(w),_=await A.client.createSession(Ot(void 0,!1));e=A.client,n=A.config,c=Lr(n.commands),a.setAutocompleteProvider(Nr(c,process.cwd()));let $=e.scopes.has("write")&&n.capabilities?.write===!0,Q=e.scopes.has("approve")&&n.capabilities?.approve===!0,je=k&&!$||R&&!Q;k&&!$&&(k=!1),(!k||!Q)&&(R=!1);let ie=l!==void 0;l=void 0,Xt=void 0,St=void 0,tn(_),V(`Switched to ${Ue(n.identity)||v}. This is a new conversation in that workspace.${ie?" Context was cleared because its id belongs to the previous workspace.":""}`),je&&V(`Access here is ${Jt(k,R)}: this workspace's login or your role there does not allow more.`,ql)}catch(A){let _=A instanceof f&&A.hint?` ${u(A.hint)}`:"";V(`Could not switch to ${v}: ${z(A)}${_}`,xt)}finally{Qt=!1}},Us=async(h,w)=>{let v=n.identity?.companyId,A=new Qi(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),mt),_=w.findIndex(Q=>Q.companyId===v);_>=0&&A.setSelectedIndex(_);let $=o.showOverlay(A,{width:"85%",maxHeight:"80%",anchor:"center"});A.onSelect=Q=>{$.hide();let je=w.find(ie=>ie.clientId===Q.value);je&&Qr(h,je)},A.onCancel=()=>{$.hide(),ot()},$.focus()},Ms=async h=>{let w=t.workspaces;if(!w){V(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${Ue(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 A;try{A=Ke(v,h)}catch(_){let $=_ instanceof f&&_.hint?` ${u(_.hint)}`:"";V(`${z(_)}${$}`,xt);return}await Qr(w,A);return}if(v.length<=1){V(`${Ue(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 Us(w,v)},js=h=>({session:p,write:k,approve:R,grantScopes:[...e.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...l?{context:l}:{},...O?{contextText:O}:{},...n.model?{model:n.model}:{},lastServedBy:Xt,...St?{lastTurn:St}:{},totals:{...rt},...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=>Vi(w))}),Ds=async()=>{if(!Me.trim()){F("There is no answer to copy yet.");return}try{let h=Wl();h?.setText?await h.setText(Me):r.write(`\x1B]52;c;${Buffer.from(Me,"utf8").toString("base64")}\x07`),F(`Copied the last answer (${Me.length} characters).`)}catch(h){F(`Could not copy: ${z(h)}`)}},Bs=async(h,w,v)=>{if(h.idleOnly&&Qt){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(A=>["",...Hi({server:c,canApprove:R}).flatMap(_=>[Nl(_.title),...br(_.rows,A)])]));return;case"status":{let A=await t.workspaces?.list().then(_=>_.length).catch(()=>{});en("Status",Ki(js(A)));return}case"workspace":await Ms(v);return;case"model":en("Model",qr({...n.model?{model:n.model}:{},lastServedBy:Xt}));return;case"usage":en("Usage",[...Ur(St),...Fr(rt)]);return;case"files":en("Generated files",zi(p.sessionId,nt));return;case"copy":await Ds();return;case"exit":nn();return;case"new":if(v&&v!=="shared"){F("Usage: /new or /new shared");return}await Yr(v==="shared");return;case"clear":await Yr(!1);return;case"resume":v?await Jr(v):await Fs();return;case"context":{if(!v){V(`Context: ${kt(l)}${O?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(v==="clear")l=void 0;else{let[A,_,...$]=w;if(!["program","initiative","codebase"].includes(A??"")||!_||$.length){F("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}l={kind:A,id:_}}V(`Context for your next message: ${kt(l)}.`);return}case"attach":{if(!v){F("Usage: /attach <file path>");return}let A=await Ll(v).catch(()=>{});if(!A?.isFile()){F(`Not a readable file: ${v}`);return}j.push(v),V(`Attached ${Vi(v)} (${K(A.size)}). It uploads with your next message.`);return}}},Xr=h=>{let w=h.trim();if(!w)return;a.addToHistory(w);let v=Di(w,c);if(v?.kind==="local"){Bs(v.command,v.args,v.arg).catch(A=>V(z(A),xt));return}if(v?.kind==="unknown"){a.setText(w),F(`Unknown command /${v.name}. Type /help to see the commands.`);return}if(Qt){a.setText(w),F("Switching workspace; send your message once it has opened.");return}if(b){let A=g!==void 0;g=w,F(A?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),oe();return}it({kind:"submit",text:w})},Ws=o.addInputListener(h=>{if(En(h,"ctrl+c")){if(b)return Gn(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),oe(),{consume:!0};let w=Date.now();return w-Kr<=Zi?nn():(Kr=w,o.flash("Press Ctrl+C again to exit",Zi)),{consume:!0}}if(En(h,"escape")){let w=ye!==void 0&&o.getFocusedComponent()===ye;return b&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!w?(Gn(),{consume:!0}):void 0}if(En(h,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(nn(),{consume:!0}):void 0;if(En(h,"ctrl+o"))return et=!et,F(et?"Showing tool activity":"Hiding tool activity"),oe(),{consume:!0}}),Zr=()=>{let h=!!b;Gn().finally(()=>{h&&(he=!0),nn()})};process.on("SIGINT",Zr),a.onSubmit=Xr,a.onChange=()=>oe(),a.setAutocompleteProvider(Nr(c,process.cwd())),tn(t.session),o.start(),p.activeStreamId&&it({kind:"replay",streamId:p.activeStreamId}),t.initialPrompt&&Xr(t.initialPrompt),await Ls,Ws(),process.off("SIGINT",Zr),Wn(),b?.controller.abort(),await r.drainInput().catch(()=>{}),he&&(process.exitCode=130)}function zl(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 Vl,randomBytes as ns,timingSafeEqual as Jl}from"node:crypto";import{createServer as Yl}from"node:http";import Ql from"open";var Pn="https://crew.anyforge.ai",Xl=6e4,Zl=5*6e4,Tn=new Map;async function Qe(t){t.signal?.throwIfAborted();let e=qe(t.endpoint),n=await He(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=ns(32).toString("base64url"),i=await rd(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 od(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=ns(32).toString("base64url"),d=Vl("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 O=t.workspace?.trim();O&&l.searchParams.set(xi,O),t.onAuthorizationUrl?.(l.toString()),t.signal?.throwIfAborted();try{t.openBrowser?await t.openBrowser(l.toString()):await Ql(l.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
|
|
409
|
+
`,R)}if(o=o.slice(R),k)break}if(o.trim()){let l=o.replace(/\r$/,"");if(l.startsWith("data:")&&(s.push(l.slice(5).replace(/^ /,"")),a+=hn(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 hn(t){return Buffer.byteLength(t,"utf8")}function Ar(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 N(t){return!t||typeof t!="object"||Array.isArray(t)?{}:t}function Zc(t){if(typeof t=="string")return u(t);try{return u(JSON.stringify(t))}catch{return"unexpected response"}}function ht(t,e){let n=N(t);if(n.ok===!1){let r=_i(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 wt(t,e){try{return await t.json()}catch{throw new f(`Non-JSON response from ${e} (HTTP ${t.status}).`,m.FAILURE)}}async function gt(t){let e;try{e=await t.clone().json()}catch{e=null}let n=N(e),r=_i(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,el(t))}function el(t){try{return t.url?new URL(t.url).origin:void 0}catch{return}}function _i(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 Ii(t){let e=N(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 Ri(t){let e=N(t),n=Ii(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=N(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(il):void 0,c=Array.isArray(s.files)?s.files.map(sl):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:nl(s.question)}:{},...s.questionResponse&&typeof s.questionResponse=="object"?{questionResponse:rl(s.questionResponse)}:{},...a?{attachments:a}:{},...c?{files:c}:{}}});return{...n,messages:r,...typeof e.activeStreamId=="string"?{activeStreamId:e.activeStreamId}:{}}}function Or(t){let e=N(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 tl(t){let e=N(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 nl(t){let e=N(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=N(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=N(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 rl(t){let e=N(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(ol)}}function ol(t){let e=N(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 il(t){let e=N(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 sl(t){let e=N(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 al}from"node:crypto";import{promises as Ae}from"node:fs";import{homedir as cl}from"node:os";import{join as Rr}from"node:path";import*as xr from"proper-lockfile";function vn(){return Rr(cl(),".anyforge")}function yn(){return Rr(vn(),"oracle")}function Ei(){return Rr(vn(),"oracle.last-session")}function ll(){return{version:2,active:{},logins:[]}}function wn(t){return t===void 0||typeof t=="string"}function Si(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")||!wn(e.companyId)||!wn(e.companyName)||!wn(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 dl(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;if(e.version===1){let o=Si(e);return o?{version:2,endpoint:o.endpoint,active:{[o.endpoint]:o.clientId},logins:[o]}:null}if(e.version!==2||!Array.isArray(e.logins)||!wn(e.endpoint)||!e.active||typeof e.active!="object"||Array.isArray(e.active))return null;let n=e.logins.map(Si);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 ll();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=dl(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 Bt(t){if(!t.logins.length){await Ae.rm(yn(),{force:!0});return}await Li(yn(),t)}async function bn(t){return An(async()=>{let e=await ne(),n=await t(e);return await Bt(e),n})}function xe(t,e){return t.logins.filter(n=>n.endpoint===e)}function kn(t,e){let n=xe(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 Ti(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 _r(t,e){t.logins=t.logins.filter(o=>!(o.endpoint===e.endpoint&&o.clientId===e.clientId));let n=xe(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 Ci(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 Ke(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 w(`More than one saved workspace is named "${n}". Use its id instead.`,s):new w(`No saved Oracle login for workspace "${n}".`,`${s} Add one with \`anyforge oracle login --workspace "${n}"\`.`)}async function $i(){let t;try{t=await Ae.readFile(Ei(),"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 Pi(t,e,n=""){let r=(await $i()).filter(o=>!(o.endpoint===e&&o.companyId===n));await Li(Ei(),{version:2,sessions:[...r,{endpoint:e,companyId:n,sessionId:t}]})}async function On(t,e=""){return(await $i()).find(n=>n.endpoint===t&&n.companyId===e)?.sessionId}async function Li(t,e){await Ae.mkdir(vn(),{recursive:!0,mode:448});let n=`${t}.${al()}.tmp`;try{await Ae.writeFile(n,`${JSON.stringify(e,null,2)}
|
|
410
|
+
`,{mode:384,flag:"wx"}),await Ae.rename(n,t),await Ae.chmod(t,384)}finally{await Ae.rm(n,{force:!0}).catch(()=>{})}}async function An(t){await Ae.mkdir(vn(),{recursive:!0,mode:448});let e=yn(),n=`${e}.refresh-admission`,r;try{r=await xr.lock(n,{realpath:!1,lockfilePath:n,stale:Number.MAX_SAFE_INTEGER,update:1e4,retries:{retries:300,minTimeout:100,maxTimeout:100,randomize:!1}})}catch(o){throw o.code==="ELOCKED"?new Error(`Oracle credential access is still locked; another CLI command may be running. An abrupt exit can leave this guard behind. Stop all AnyForge CLI commands before removing the guard directory at ${n}.`,{cause:o}):o}try{let o=await xr.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 o()}}finally{await r()}}var Ir=8192,qi=10*1048576,ml=20*1048576,Fi=10,Ui=3;function Sr(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!dt(t.name)}var Re=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:Sr(e,this.toolIds)?this.answer="":e.type==="question"?this.question=Gt(e):e.type==="error"?this.error=Tr(e):e.type==="done"?this.done=e:e.type==="navigator_file_created"&&gl(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 vt(t,e,n,r,o,i=new Re){let s=async l=>{let k=i.streamId;i.accept(l)&&(i.streamId&&i.streamId!==k&&r.onStream?.(i.streamId),await r.onEvent(l))},a=()=>Er(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 k of Dt(l,r.onActivity))await s(k)}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 Dt(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 Er(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<Ui;){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 Ni(300,void 0,o?{signal:o}:void 0)}}catch(c){if(o?.aborted||c instanceof f&&c.exitCode===m.API_ERROR||(a+=1,a>=Ui))throw c;await Ni(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 Wt(t,e){let n=e.done,r=n?.servedBy,o=ze(n?.tokens),i=Y(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(Fe):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 C(t,e){return typeof t=="string"?u(t).slice(0,e):""}function Tr(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 Gt(t){let e=t.question;if(!e||typeof e!="object")throw new f("Oracle question event omitted its question card.",m.API_ERROR);return hl(e)}function Fe(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 gl(t,e){let n=Fe(e);t.some(r=>r.fileId===n.fileId)||t.push(n)}function hl(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 Y(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function ze(t){if(typeof t=="number"&&Number.isFinite(t))return{totalTokens:t};if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,n=Y(e.in)??Y(e.input)??Y(e.inputTokens),r=Y(e.cacheRead),o=Y(e.cacheWrite),i=n===void 0?void 0:n+(r??0)+(o??0),s=Y(e.out)??Y(e.output)??Y(e.outputTokens),a=Y(e.total)??Y(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 bt(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 kt(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 Ht(t,e,n){await Pi(t,e,n).catch(()=>{})}async function xn(t,e,n,r){if(!n.length)return[];if(n.length>Fi)throw new w(`Attach no more than ${Fi} files per turn.`);let o=0,i=[];for(let a of n){let c=await pl(a);if(!c.isFile())throw new w(`Attachment is not a file: ${a}`);if(c.size>qi)throw new w(`Attachment exceeds the 10 MiB per-file CLI limit: ${a}`);let d=await wl(a,qi);if(o+=d.byteLength,o>ml)throw new w("Attachments exceed the 20 MiB total CLI limit.");let p=fl(a).replace(/[\u0000-\u001f\u007f/\\]/g,"_").slice(0,200)||"attachment",l=Ai(p)??"";if(!l)throw new w(`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 wl(t,e){let n=[],r=0;for await(let o of ul(t)){let i=Buffer.isBuffer(o)?o:Buffer.from(o);if(r+=i.byteLength,r>e)throw new w(`Attachment exceeds the ${K(e)} limit: ${t}`);n.push(i)}return Buffer.concat(n)}var Je=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],vl=2e4,bl=6,kl={lookup:"Looking things up",quick_edit:"Making a quick edit",final_answer:"Composing the answer",follow_up:"Suggesting follow-ups"},Ol={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=Y(e.round),this.maxRounds=Y(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: ${C(e.message,500)}`});break;case"navigator_file_error":this.notices.push({tone:"warning",text:`Could not generate ${C(e.filename,200)}: ${C(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:`${C(r.name,160)}: ${C(r.message,500)}`})}break;case"provider_fallback":{let n=C(e.from,120),r=C(e.to,120),o=C(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=Fe(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:Tr(e)});break;case"done":this.done=e;break}}recordToolCall(e){let n=typeof e.id=="string"?e.id:void 0,r=Sr(e,this.toolIds),o=typeof e.name=="string"?e.name:"";if(dt(o))return;let i=C(typeof e.label=="string"?e.label:lt(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&&kl[this.serverPhase]||"Thinking"}statusLine(e,n){let r=[this.label(),vr(e-this.startedAt)];this.round&&r.push(this.maxRounds?`step ${this.round}/${this.maxRounds}`:`step ${this.round}`);let o=e-this.lastSignalAt;return o>=vl&&this.phase!=="cancelling"&&r.push(`no update for ${vr(o)}`),n&&e-this.startedAt>=n*.9&&this.phase!=="finishing"&&r.push("wrapping up"),r.join(" \xB7 ")}summaryLine(){let e=[],n=Mt((this.endedAt??this.startedAt)-this.startedAt),r=typeof this.done?.exitReason=="string"?this.done.exitReason:void 0,o;this.end==="complete"?o=ji("\u2713"):this.end==="awaiting_input"?o=Cr("?")+" Waiting for your answer":this.end==="cancelled"?o=Pr("\u2717")+" Interrupted":this.end==="error"?o=$r("\u2717")+" Error":o=Pr("\u2717")+` Stopped: ${r&&Ol[r]||"no final answer"}`,e.push(n),this.tools.length&&e.push(`${this.tools.length} tool${this.tools.length===1?"":"s"}`);let i=ze(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=zt(this.done?.servedBy);return s&&e.push(s),`${o} ${Ve(e.join(" \xB7 "))}`}};function zt(t){if(!t||typeof t!="object")return"";let e=t,n=C(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"?`: ${C(r.reason,40).replaceAll("_"," ")}`:"";return`${n} (fallback from ${C(r.from,100)}${o})`}return n}var _n=class{constructor(e,n){this.activity=e;this.view=n;this.answer=new ft("",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=[jt("assistant")];if(!n.finished&&!n.tools.length&&!n.answer.trim()){let o=Cr(Je[this.view.frame()%Je.length]??"*");r.push(Kt(` ${o} ${Ve(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"?$r:o.tone==="warning"?Pr:Ve;r.push(...Mi(i(o.text),e))}return n.finished&&r.push(Kt(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[Kt(Ve(` \u23BF ${a}`),e)]}let r=this.activity.finished?n:n.slice(-bl),o=n.length-r.length,i=[];return o&&i.push(Kt(Ve(` \u23BF \u2026 ${o} earlier tool${o===1?"":"s"}`),e)),r.forEach((s,a)=>{if(s.note)for(let l of Mi(s.note,Math.max(10,e-4)).slice(0,3))i.push(` ${Ve(yl(l))}`);let c=s.status==="running"?Cr(Je[this.view.frame()%Je.length]??"*"):s.status==="ok"?ji("\u2713"):$r("\u2717"),d=a===0&&!o?" \u23BF ":" ",p=s.spawnId?` ${Ve("agent \xB7")} `:"";i.push(Kt(`${d}${p}${c} ${s.label}`,e))}),i}};import{truncateToWidth as _l,visibleWidth as Il,wrapTextWithAnsi as Bi}from"@earendil-works/pi-tui";import{bold as Wi,cyan as Sl,dim as _e}from"kleur/colors";import{CombinedAutocompleteProvider as Al}from"@earendil-works/pi-tui";var Vt=[{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"]}],xl=[{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 Lr(t){let e=new Set(Vt.flatMap(n=>[n.name,...n.aliases??[]]));return(t??xl).filter(n=>!e.has(n.name))}function Di(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=Vt.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 Nr(t,e){let n=[...Vt.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=>Rl(i.completions??[],s)}:{}})),...t.map(i=>({name:i.name,description:i.description,...i.args?{argumentHint:i.args}:{}}))],r=new Al(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 Rl(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 Jt(t,e){return t?e?"write + approve":"write":"read only"}function Ot(t){return t?`${t.kind} ${u(t.id)}`:"no context"}function Ue(t){return t?u(t.companyName||t.companyId):""}function Gi(t,e){let n=[`${Wi(Sl("\u26A1\uFE0E AnyForge Oracle"))}${_e(` \xB7 v${u(t.version)}`)}`,`${t.workspace?`${Wi(t.workspace)} \xB7 `:""}${ke(t.session)} \xB7 ${t.access} \xB7 ${Ot(t.context)}`,...t.session.messages.length?[`Resumed: ${u(t.session.title)}`]:[],_e("Only your messages and the files you attach leave this terminal."),_e("/help commands \xB7 /status details")];if(e<24)return n.flatMap(s=>Bi(s,Math.max(1,e)));let r=Math.min(e,76),o=r-4,i=[_e(`\u256D${"\u2500".repeat(r-2)}\u256E`)];for(let s of n.flatMap(a=>Bi(a,o))){let a=_l(s,o);i.push(`${_e("\u2502")} ${a}${" ".repeat(Math.max(0,o-Il(a)))} ${_e("\u2502")}`)}return i.push(_e(`\u2570${"\u2500".repeat(r-2)}\u256F`)),i}function Hi(t){let e=(r,o)=>r?`${_e(r)} ${o}`:o,n=[{title:"Commands",rows:Vt.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(["",_e("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 qr(t){let n=[["Model",`workspace default ${t.model?`${u(t.model.provider)}/${u(t.model.id)}`:"chosen by the server"}`]],r=zt(t.lastServedBy);if(r){let o=t.lastServedBy,i=C(o.routing,40).replaceAll("_"," ");n.push(["Last answer",`${r}${i?` \xB7 ${i}`:""}`])}return n}function Fr(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 Ur(t){if(!t?.finished)return[];let e=[Mt((t.endedAt??t.startedAt)-t.startedAt)];e.push(`${t.toolCount} tool${t.toolCount===1?"":"s"}${t.failedCount?` (${t.failedCount} failed)`:""}`);let n=Y(t.done?.rounds);n!==void 0&&e.push(`${n} round${n===1?"":"s"}`);let r=ze(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=C(t.done?.exitReason,40);return e.push(o?o.replaceAll("_"," "):t.end??""),[["Last turn",e.filter(Boolean).join(" \xB7 ")]]}function Ki(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=mi(t.limits);return[["Session",`${t.session.sessionId} \xB7 ${u(t.session.title)||"Untitled"}`],["Audience",ke(t.session)],["Access",`this session ${Jt(t.write,t.approve)} \xB7 grant ${t.grantScopes.join(", ")||"none"} \xB7 workspace role ${e}`],["Context",`${Ot(t.context)}${n}`],...qr(t),...Ur(t.lastTurn),...Fr(t.totals),["Limits",r.length?r.join(" \xB7 "):"not reported"],...t.identity?[["Workspace",El(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 El(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 zi(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 Pl}from"node:crypto";import{stat as Ll}from"node:fs/promises";import{basename as Vi}from"node:path";import{bold as Nl,cyan as Ji,dim as Yi,red as xt,yellow as ql}from"kleur/colors";import{Container as Fl,Editor as Ul,ProcessTerminal as Ml,ScrollView as jl,SelectList as Qi,TuiAltScreen as Dl,VStack as Bl,getNativeClipboard as Wl,matchesKey as En,truncateToWidth as Xi,wrapTextWithAnsi as Gl}from"@earendil-works/pi-tui";import{bold as Tl,cyan as Mr,dim as In,yellow as Cl}from"kleur/colors";import{Input as $l,Text as Ye,matchesKey as At}from"@earendil-works/pi-tui";var Sn=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 $l({prompt:`${Mr("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(`${Tl(Mr(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(Cl(this.errorMessage),0,0).render(r)),i.map(s=>`${Mr("\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(At(e,"escape")){this.question.optional?this.finish(null):this.requireAnswer();return}this.customInput.handleInput(e);return}if(At(e,"up")){this.optionIndex=Math.max(0,this.optionIndex-1),this.errorMessage="",this.tui.requestRender();return}if(At(e,"down")){this.optionIndex=Math.min(n.options.length-1,this.optionIndex+1),this.errorMessage="",this.tui.requestRender();return}if(At(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(At(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(At(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":"",k=`${d} ${s+1}. ${c} ${u(i.label)}${p}${l}`;o.push(...new Ye(k,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 Zi=1500,Hl=100,Kl="\x1B]133;A\x07";function Rt(t){return{render:t,invalidate(){}}}function es(t,e,n,r){let o=new ft(e,r);return{getText:()=>o.getText(),invalidate:()=>o.invalidate(),render:i=>t==="user"?bi(o,i,n).map((s,a)=>a===1?`${Kl}${s}`:s):[jt("assistant"),...o.render(i)]}}async function ts(t){let{client:e,config:n}=t,r=new Ml,o=new Dl(r,void 0,void 0,{scrollToEndIndicator:()=>" more below \xB7 End to jump "}),i=new Fl,s=new jl(i,{follow:"end",primary:!0,overscroll:"chain"}),a=new Ul(o,vi),c=Lr(n.commands),d=()=>typeof n.limits?.turnSoftMs=="number"?n.limits.turnSoftMs:void 0,p=t.session,l=t.initialContext,k=t.contextText,O=t.canWrite,R=t.canApprove,b,g,x,L,j=[...t.initialFiles],G=!1,he=!1,et=!1,Bn=0,tt,Kr=0,Qt=!1,Me="",St,Xt,nt=[],rt={turns:0,inputTokens:0,outputTokens:0,tools:0},Zt=new Map,we,Cs=Rt(h=>{if(b){let y=Ji(Je[Bn%Je.length]??"*");return[Xi(`${y} ${b.activity.statusLine(Date.now(),d())}`,h)]}return we?[Xi(`${Ji("?")} Waiting for your answer to the question above`,h)]:[]}),$s=Rt(h=>{let y=[Ue(n.identity),Jt(O,R),Ot(l),ki(p),j.length?`${j.length} file${j.length===1?"":"s"} attached`:"",g!==void 0?"1 message queued":""];return[Yi(hi(h,y,b?"esc to interrupt":"/help \xB7 /status"))]});o.setLayoutRoot(new Bl([{component:s,basis:0,grow:1,minSize:1},{component:Cs,basis:"auto",shrink:0},{component:a,basis:"auto",shrink:0,minSize:2},{component:$s,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,y=Yi)=>{let v=u(h);Ee(Rt(A=>Gl(y(v),Math.max(1,A))))},en=(h,y)=>{Ee(Rt(v=>["",...gi(h,y,v)]))},Ps=()=>{tt||(tt=setInterval(()=>{Bn+=1,oe()},Hl),tt.unref())},Wn=()=>{tt&&clearInterval(tt),tt=void 0},ot=()=>{G||o.hasOverlay()||o.setFocus(we??a)},zr=h=>{x=h;let y=Zt.get(h.id);y&&i.removeChild(y);let v=new Sn(o,h,A=>{Zt.delete(h.id),we===v&&(we=void 0),i.removeChild(v),qs(A??{questionId:h.id,skipped:!0,answers:[]}),ot()});Zt.set(h.id,v),we=v,Ee(v)},tn=h=>{i.clear(),Zt.clear(),we=void 0,p=h,x=void 0,L=void 0,nt=[],Me="";let y={version:t.version,workspace:Ue(n.identity),session:h,access:Jt(O,R),context:l};Ee(Rt(A=>Gi(y,A)));let v;for(let A of h.messages){Ee(es(A.role,A.content,A.authorName,e.endpoint)),A.role==="assistant"&&(Me=A.content),A.question&&(v=A.question),A.questionResponse&&(V("A stored response was recorded for this question."),v?.id===A.questionResponse.questionId&&(v=void 0));for(let _ of A.files??[])nt.some($=>$.fileId===_.fileId)||nt.push(_),V(`File: ${_.filename} (${_.mediaType}, ${K(_.sizeBytes)}) \xB7 anyforge oracle download ${h.sessionId} ${_.fileId} --output .`)}v&&zr(v),ot(),Ht(h.sessionId,e.endpoint,n.identity?.companyId)},Vr,Ls=new Promise(h=>{Vr=h}),nn=()=>{G||(G=!0,Wn(),b?.controller.abort(new Error("Detached from the active Oracle stream.")),o.stop(),Vr())},Gn=async()=>{let h=b;if(!h||h.cancelling)return;h.cancelling=!0,h.activity.setPhase("cancelling",Date.now()),oe();let{streamId:y,requestId:v}=h;h.controller.abort(new Error("Turn cancelled by user."));let A;if(y)try{A=bt(await e.cancelTurn(p.sessionId,y))}catch(_){A=`Could not confirm server cancellation: ${z(_)}`}else if(v)try{A=bt(await e.cancelRequest(p.sessionId,v))}catch(_){A=`Could not confirm server cancellation by request id: ${z(_)}`}else A="The turn had not reached the server; nothing was sent.";h.activity.note("info",A),oe()},it=async h=>{if(b||G)return;if(h.kind==="submit"&&h.response){let P=x;if(!P||P.id!==h.response.questionId){F("That question is no longer active. Resume the session to refresh it.");return}V(zl(P,h.response)),x=void 0}else h.kind==="submit"&&Ee(es("user",h.text));let y=p.sessionId,v=new Rn(Date.now(),y),A=new _n(v,{expanded:()=>et,frame:()=>Bn,linkOrigin:e.endpoint}),_={activity:v,view:A,controller:new AbortController,cancelling:!1};b=_,Ee(A),Ps();let $=new Re,Q=P=>{v.apply(P,Date.now()),P.type==="question"&&(zr(Gt(P)),ot()),oe()},je={onEvent:Q,onStream:P=>{_.streamId=P},onResponse:()=>{v.phase==="sending"&&v.setPhase("preparing",Date.now())},onActivity:()=>v.touch(Date.now())},ie="error";try{if(h.kind==="replay"){$.streamId=h.streamId,_.streamId=h.streamId,v.setPhase("thinking",Date.now());let P=await Er(e,y,$,async ue=>{$.accept(ue)&&Q(ue)},_.controller.signal,je.onActivity);ie=Wt(y,P).status}else{let P=h.response?[]:j.splice(0),ue=await xn(e,y,P,(Hn,Ks)=>{v.setUploading(Hn,Ks,Date.now()),oe()});_.controller.signal.throwIfAborted(),v.setPhase("sending",Date.now());let rn=Pl();_.requestId=rn;let Gs={requestId:rn,text:h.response?"":h.text,readOnly:!O,...R?{allowApprove:!0}:{},...l?{context:l}:{},...k?{contextText:k}:{},...h.response?{questionResponse:h.response}:{},...ue.length?{attachments:ue.map(Hn=>({fileId:Hn.fileId}))}:{}},Hs=await vt(e,y,Gs,je,_.controller.signal,$);ie=Wt(y,Hs).status}}catch(P){if(_.controller.signal.aborted)ie="cancelled";else{ie="error";let ue=P instanceof f&&P.hint?` ${u(P.hint)}`:"";v.note("error",`${z(P)}${ue}`)}}finally{v.finish(ie,Date.now()),b=void 0,Wn(),G||(he=ie==="cancelled"),St=v,v.answer.trim()&&(Me=v.answer),v.done?.servedBy&&(Xt=v.done.servedBy);let P=ze(v.done?.tokens);rt.turns+=1,rt.tools+=v.toolCount,rt.inputTokens+=P?.inputTokens??0,rt.outputTokens+=P?.outputTokens??0;for(let ue of v.files)nt.some(rn=>rn.fileId===ue.fileId)||nt.push(ue);oe()}if(!G){if(_.streamId&&ie!=="cancelled"){let P=await e.getSession(y).catch(()=>{});P&&P.sessionId===p.sessionId&&(p=P)}Ns(ie)}},Ns=h=>{if(!(G||b)){if(L){let y=L;L=void 0,it({kind:"submit",text:"",response:y});return}if(g!==void 0){let y=g;if(g=void 0,h==="complete"&&!we){it({kind:"submit",text:y});return}a.getText()||a.setText(y),F("Your queued message is back in the input; press Enter to send it.")}ot(),oe()}},qs=h=>{if(b){L=h,F("Answer saved; Oracle continues once the current turn settles.");return}it({kind:"submit",text:"",response:h})},Jr=async h=>{try{let y=await e.getSession(h);tn(y),y.activeStreamId&&it({kind:"replay",streamId:y.activeStreamId}),F(`Resumed ${y.title||y.sessionId}.`)}catch(y){V(z(y),xt)}},Fs=async()=>{try{let h=await e.listSessions();if(!h.length){F("No saved conversations to resume.");return}let y=new Qi(h.map($=>({value:$.sessionId,label:$.title,description:ke($)})),Math.min(12,h.length),mt),v=await On(e.endpoint,n.identity?.companyId),A=v?h.findIndex($=>$.sessionId===v):-1;A>=0&&y.setSelectedIndex(A);let _=o.showOverlay(y,{width:"85%",maxHeight:"80%",anchor:"center"});y.onSelect=$=>{_.hide(),Jr($.value)},y.onCancel=()=>{_.hide(),ot()},_.focus()}catch(h){V(z(h),xt)}},Yr=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 y=await e.createSession(kt(l,h));tn(y),F(`Started a new conversation \xB7 ${ke(y)}.`)}catch(y){V(z(y),xt)}},Qr=async(h,y)=>{let v=u(re(y));if(y.companyId&&y.companyId===n.identity?.companyId){F(`Already in ${v}.`);return}Qt=!0;try{F(`Connecting to ${v}...`);let A=await h.connect(y),_=await A.client.createSession(kt(void 0,!1));e=A.client,n=A.config,c=Lr(n.commands),a.setAutocompleteProvider(Nr(c,process.cwd()));let $=e.scopes.has("write")&&n.capabilities?.write===!0,Q=e.scopes.has("approve")&&n.capabilities?.approve===!0,je=O&&!$||R&&!Q;O&&!$&&(O=!1),(!O||!Q)&&(R=!1);let ie=l!==void 0;l=void 0,Xt=void 0,St=void 0,tn(_),V(`Switched to ${Ue(n.identity)||v}. This is a new conversation in that workspace.${ie?" Context was cleared because its id belongs to the previous workspace.":""}`),je&&V(`Access here is ${Jt(O,R)}: this workspace's login or your role there does not allow more.`,ql)}catch(A){let _=A instanceof f&&A.hint?` ${u(A.hint)}`:"";V(`Could not switch to ${v}: ${z(A)}${_}`,xt)}finally{Qt=!1}},Us=async(h,y)=>{let v=n.identity?.companyId,A=new Qi(y.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,y.length),mt),_=y.findIndex(Q=>Q.companyId===v);_>=0&&A.setSelectedIndex(_);let $=o.showOverlay(A,{width:"85%",maxHeight:"80%",anchor:"center"});A.onSelect=Q=>{$.hide();let je=y.find(ie=>ie.clientId===Q.value);je&&Qr(h,je)},A.onCancel=()=>{$.hide(),ot()},$.focus()},Ms=async h=>{let y=t.workspaces;if(!y){V(`This terminal signs in with ANYFORGE_API_TOKEN, which is bound to ${Ue(n.identity)||"one workspace"}. To switch workspaces, unset it and sign in with \`anyforge oracle login --workspace <name>\`.`);return}let v=await y.list();if(h){let A;try{A=Ke(v,h)}catch(_){let $=_ instanceof f&&_.hint?` ${u(_.hint)}`:"";V(`${z(_)}${$}`,xt);return}await Qr(y,A);return}if(v.length<=1){V(`${Ue(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 Us(y,v)},js=h=>({session:p,write:O,approve:R,grantScopes:[...e.scopes],...n.capabilities?{capabilities:n.capabilities}:{},...l?{context:l}:{},...k?{contextText:k}:{},...n.model?{model:n.model}:{},lastServedBy:Xt,...St?{lastTurn:St}:{},totals:{...rt},...n.limits?{limits:n.limits}:{},...n.identity?{identity:n.identity}:{},...h!==void 0?{savedWorkspaces:h}:{},endpoint:e.endpoint,version:t.version,pendingFiles:j.map(y=>Vi(y))}),Ds=async()=>{if(!Me.trim()){F("There is no answer to copy yet.");return}try{let h=Wl();h?.setText?await h.setText(Me):r.write(`\x1B]52;c;${Buffer.from(Me,"utf8").toString("base64")}\x07`),F(`Copied the last answer (${Me.length} characters).`)}catch(h){F(`Could not copy: ${z(h)}`)}},Bs=async(h,y,v)=>{if(h.idleOnly&&Qt){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(A=>["",...Hi({server:c,canApprove:R}).flatMap(_=>[Nl(_.title),...br(_.rows,A)])]));return;case"status":{let A=await t.workspaces?.list().then(_=>_.length).catch(()=>{});en("Status",Ki(js(A)));return}case"workspace":await Ms(v);return;case"model":en("Model",qr({...n.model?{model:n.model}:{},lastServedBy:Xt}));return;case"usage":en("Usage",[...Ur(St),...Fr(rt)]);return;case"files":en("Generated files",zi(p.sessionId,nt));return;case"copy":await Ds();return;case"exit":nn();return;case"new":if(v&&v!=="shared"){F("Usage: /new or /new shared");return}await Yr(v==="shared");return;case"clear":await Yr(!1);return;case"resume":v?await Jr(v):await Fs();return;case"context":{if(!v){V(`Context: ${Ot(l)}${k?" + free-text note":""}. Use /context <program|initiative|codebase> <id> or /context clear.`);return}if(v==="clear")l=void 0;else{let[A,_,...$]=y;if(!["program","initiative","codebase"].includes(A??"")||!_||$.length){F("Usage: /context program <id>, /context initiative <id>, /context codebase <id>, or /context clear");return}l={kind:A,id:_}}V(`Context for your next message: ${Ot(l)}.`);return}case"attach":{if(!v){F("Usage: /attach <file path>");return}let A=await Ll(v).catch(()=>{});if(!A?.isFile()){F(`Not a readable file: ${v}`);return}j.push(v),V(`Attached ${Vi(v)} (${K(A.size)}). It uploads with your next message.`);return}}},Xr=h=>{let y=h.trim();if(!y)return;a.addToHistory(y);let v=Di(y,c);if(v?.kind==="local"){Bs(v.command,v.args,v.arg).catch(A=>V(z(A),xt));return}if(v?.kind==="unknown"){a.setText(y),F(`Unknown command /${v.name}. Type /help to see the commands.`);return}if(Qt){a.setText(y),F("Switching workspace; send your message once it has opened.");return}if(b){let A=g!==void 0;g=y,F(A?"Replaced your queued message. It sends when Oracle finishes.":"Queued. It sends when Oracle finishes; Esc interrupts instead."),oe();return}it({kind:"submit",text:y})},Ws=o.addInputListener(h=>{if(En(h,"ctrl+c")){if(b)return Gn(),{consume:!0};if(o.hasOverlay())return;if(o.getFocusedComponent()===a&&a.getText())return a.setText(""),oe(),{consume:!0};let y=Date.now();return y-Kr<=Zi?nn():(Kr=y,o.flash("Press Ctrl+C again to exit",Zi)),{consume:!0}}if(En(h,"escape")){let y=we!==void 0&&o.getFocusedComponent()===we;return b&&!a.isShowingAutocomplete()&&!o.hasOverlay()&&!y?(Gn(),{consume:!0}):void 0}if(En(h,"ctrl+d"))return o.getFocusedComponent()===a&&!a.getText()?(nn(),{consume:!0}):void 0;if(En(h,"ctrl+o"))return et=!et,F(et?"Showing tool activity":"Hiding tool activity"),oe(),{consume:!0}}),Zr=()=>{let h=!!b;Gn().finally(()=>{h&&(he=!0),nn()})};process.on("SIGINT",Zr),a.onSubmit=Xr,a.onChange=()=>oe(),a.setAutocompleteProvider(Nr(c,process.cwd())),tn(t.session),o.start(),p.activeStreamId&&it({kind:"replay",streamId:p.activeStreamId}),t.initialPrompt&&Xr(t.initialPrompt),await Ls,Ws(),process.off("SIGINT",Zr),Wn(),b?.controller.abort(),await r.drainInput().catch(()=>{}),he&&(process.exitCode=130)}function zl(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 Vl,randomBytes as ns,timingSafeEqual as Jl}from"node:crypto";import{createServer as Yl}from"node:http";import Ql from"open";var Pn="https://crew.anyforge.ai",Xl=6e4,Zl=5*6e4,Tn=new Map;async function Qe(t){t.signal?.throwIfAborted();let e=qe(t.endpoint),n=await He(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=ns(32).toString("base64url"),i=await rd(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 od(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=ns(32).toString("base64url"),d=Vl("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 k=t.workspace?.trim();k&&l.searchParams.set(xi,k),t.onAuthorizationUrl?.(l.toString()),t.signal?.throwIfAborted();try{t.openBrowser?await t.openBrowser(l.toString()):await Ql(l.toString())}catch{process.stderr.write(`Open this URL to authorize AnyForge Oracle:
|
|
365
411
|
${l.toString()}
|
|
366
|
-
`)}let
|
|
367
|
-
${e}`,i=Tn.get(o);if(i)return i;let s=An(()=>nd(r,e,n));Tn.set(o,s);try{return await s}finally{Tn.get(o)===s&&Tn.delete(o)}}async function nd(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()+Xl&&(!n||o.accessToken!==n))return o.accessToken;let i=async()=>{_r(r,o),await Bt(r)},s=await He(o.endpoint),a;try{let d={client_id:o.clientId,grant_type:"refresh_token",refresh_token:o.refreshToken,resource:s.resource};a=await ps(s.oauth.token,d)}catch(d){throw d instanceof Cn&&["invalid_grant","invalid_client","invalid_target"].includes(d.code)?(await i(),new fe):d}us(a);let c;try{c=ds(a.scope)}catch(d){throw await i(),d}if(c.some(d=>!o.scopes.includes(d)))throw await $n(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 Bt(r),o.accessToken}var Cn=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 rd(t,e){e?.throwIfAborted();let n,r,o=!1,i=new Promise((p,l)=>{n=p,r=l});i.catch(()=>{});let s=Yl((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
|
|
412
|
+
`)}let O=await i.wait();if(!ls(O.state,o))throw new f("Oracle sign-in callback state did not match; authorization was rejected.",m.API_ERROR);if(O.error)throw new f(`Oracle authorization failed: ${u(O.error)}${O.errorDescription?` (${u(O.errorDescription)})`:""}`,m.API_ERROR);if(!O.code)throw new f("Oracle authorization callback did not include a code.",m.API_ERROR);let R={client_id:a.client_id,grant_type:"authorization_code",code:O.code,redirect_uri:i.redirectUri,code_verifier:c,resource:n.resource},b=await ps(n.oauth.token,R,t.signal);us(b);let g=ds(b.scope);if(!g.includes("read")||g.some(G=>!r.includes(G)))throw await $n(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 x=await He(e,fetch,b.access_token,t.signal).then(G=>G.identity).catch(()=>{});t.signal?.throwIfAborted();let L={endpoint:e,...os(x),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 bn(G=>Ti(G,L));return j&&await $n(n.oauth.revocation,j.clientId,j.accessToken,j.refreshToken).catch(()=>{}),L}finally{await i.close()}}function os(t){return t?{companyId:t.companyId,...t.companyName?{companyName:t.companyName}:{},uid:t.uid}:{}}async function is(t={}){if(t.workspace&&!t.all){let n=Yt(await ne(),t.endpointOverride);n&&await jr(n)}let e=await An(async()=>{let n=await ne(),r=[];if(t.all)r=[...n.logins];else{let s=Yt(n,t.endpointOverride);t.workspace?r=[Ke(s?xe(n,s):[],t.workspace)]:s&&(r=[kn(n,s)].filter(a=>!!a))}let o=[],i;for(let s of r){try{await ss(s)}catch(a){o.push({login:s,error:a})}i=_r(n,s)??i}return await Bt(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 ss(t){let e=await He(t.endpoint);await $n(e.oauth.revocation,t.clientId,t.accessToken,t.refreshToken)}function Yt(t,e){let n=e||process.env.ANYFORGE_ENDPOINT;return n?qe(n):t.endpoint}function as(){return new w("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 Ln(t,e){let n=t||process.env.ANYFORGE_ENDPOINT;if(process.env.ANYFORGE_API_TOKEN){if(e)throw as();return rs(n)}let r=await ne(),o=Yt(r,n);if(e)return Ie(await ed(o,e));let i=o?kn(r,o):void 0;if(i)return Ie(i);try{return await rs(n)}catch(s){throw r.logins.length&&n?new fe:s instanceof fe?new fe:s}}async function ed(t,e){if(!t)return Ke([],e);let n=await Nn(t);return Ke(n,e)}function Ie(t){return{endpoint:t.endpoint,accessToken:t.accessToken,scopes:t.scopes.filter(Dr),source:"oauth",getAccessToken:async e=>td(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 bn(i=>Ci(i,t.endpoint,t.clientId,e));Object.assign(t,os(e));let o=r.duplicate;o&&await ss(o).catch(()=>{})}}}async function Nn(t){return await jr(t),xe(await ne(),t)}async function jr(t){let e=xe(await ne(),t).filter(n=>!n.companyId);for(let n of e)await new ae(Ie(n)).config().catch(()=>{})}async function _t(t){let e=Yt(await ne(),t);if(!e)return{logins:[]};let n=await Nn(e),r=kn(await ne(),e);return{endpoint:e,logins:n,...r?{active:r}:{}}}async function cs(t,e){if(process.env.ANYFORGE_API_TOKEN)throw as();let n=Yt(await ne(),t);return n&&await jr(n),bn(r=>{let o=Ke(n?xe(r,n):[],e);return r.active[o.endpoint]=o.clientId,r.endpoint=o.endpoint,o})}async function rs(t){let e=await $e(t),r=(await Ct({apiKey:e.apiKey,endpoint:e.endpoint})).scopes.filter(Dr);return{endpoint:e.endpoint,accessToken:e.apiKey,scopes:r,source:"control"}}async function td(t,e,n){let r=qe(t),o=`${r}
|
|
413
|
+
${e}`,i=Tn.get(o);if(i)return i;let s=An(()=>nd(r,e,n));Tn.set(o,s);try{return await s}finally{Tn.get(o)===s&&Tn.delete(o)}}async function nd(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()+Xl&&(!n||o.accessToken!==n))return o.accessToken;let i=async()=>{_r(r,o),await Bt(r)},s=await He(o.endpoint),a;try{let d={client_id:o.clientId,grant_type:"refresh_token",refresh_token:o.refreshToken,resource:s.resource};a=await ps(s.oauth.token,d)}catch(d){throw d instanceof Cn&&["invalid_grant","invalid_client","invalid_target"].includes(d.code)?(await i(),new fe):d}us(a);let c;try{c=ds(a.scope)}catch(d){throw await i(),d}if(c.some(d=>!o.scopes.includes(d)))throw await $n(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 Bt(r),o.accessToken}var Cn=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 rd(t,e){e?.throwIfAborted();let n,r,o=!1,i=new Promise((p,l)=>{n=p,r=l});i.catch(()=>{});let s=Yl((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 k=p.url??"/";if(!k.startsWith("/callback")||k.startsWith("//")){l.writeHead(400,{"Content-Type":"text/plain; charset=utf-8"}).end("Authorization response path is invalid.");return}let O=new URL(k,"http://127.0.0.1");if(p.method!=="GET"||O.pathname!=="/callback"){l.writeHead(404,{"Content-Type":"text/plain; charset=utf-8"}).end("Not found");return}let R=O.searchParams.get("code")?.slice(0,2048)??"",b=O.searchParams.get("state")?.slice(0,256)??"",g=O.searchParams.get("error")?.slice(0,256)??void 0,x=O.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(!ls(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:R,state:b,...g?{error:g}:{},...x?{errorDescription:x}:{}})});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)))},Zl);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 ls(t,e){let n=Buffer.from(t),r=Buffer.from(e);return n.length===r.length&&Jl(n,r)}function ds(t){let e=t.split(/[\s+]+/).filter(Boolean);if(!e.every(Dr))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 Dr(t){return t==="read"||t==="write"||t==="approve"}function us(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 od(t,e,n){return fs(t,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e),signal:n})}async function ps(t,e,n){return fs(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 fs(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 Cn(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 $n(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 yd=1048576,vd=25*1048576;async function bs(t){let[e,...n]=t.positional,r=["exec","sessions","resume","login","logout","download","workspaces","use"].includes(e??""),o=Xe(t.flags,"print");if(o!==void 0&&t.positional.length)throw new w("-p/--print takes the prompt as its value and cannot be combined with a subcommand or positional prompt.");let i=Xe(t.flags,"workspace")?.trim()||void 0,s=r?void 0:t.positional.join(" ");if(W(t.flags,"read-only")&&(W(t.flags,"write")||W(t.flags,"approve")))throw new w("--read-only cannot be combined with --write or --approve.");if(!r&&o===void 0&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new w("Interactive Oracle needs a terminal.",'Use `anyforge oracle exec "your question"` for scripts and pipes.');if(e==="login"){if(n.length)throw new w("oracle login does not accept positional arguments.");let b=await Qe({endpoint:t.endpointOverride??t.endpoint,write:W(t.flags,"write"),approve:W(t.flags,"approve"),...i?{workspace:i}:{}}),x=[W(t.flags,"write")?"write":void 0,W(t.flags,"approve")?"approve":void 0].filter(L=>!!L).filter(L=>!b.scopes.includes(L));process.stdout.write(`${ms(b)}
|
|
368
414
|
`),x.length&&process.stderr.write(`This account was granted read only; the server withheld: ${x.join(", ")}.
|
|
369
|
-
`),await
|
|
415
|
+
`),await kd(b,i);return}if(e==="logout"){if(n.length)throw new w("oracle logout does not accept positional arguments.");let b=W(t.flags,"all");if(b&&i)throw new w("Choose either --workspace or --all.");let g=await is({...t.endpointOverride?{endpointOverride:t.endpointOverride}:{},...i?{workspace:i}:{},all:b});if(!g.removed.length){process.stdout.write(`No saved Oracle login.
|
|
370
416
|
`);return}let x=g.removed.map(L=>u(re(L))).join(", ");process.stdout.write(`Logged out of ${x}; ${g.removed.length===1?"its access was":"their access was"} revoked.
|
|
371
417
|
`),g.nowActive&&process.stdout.write(`Active workspace is now ${u(re(g.nowActive))}.
|
|
372
|
-
`);return}if(e==="workspaces"){if(n.length)throw new
|
|
373
|
-
`);return}let a;try{a=await
|
|
374
|
-
`),a=await
|
|
375
|
-
`)),G={requestId:ad(),text:b,readOnly:!0,...x?{context:x}:{},...Xe(t.flags,"context")?{contextText:Xe(t.flags,"context")}:{},...j.length?{attachments:j.map(he=>({fileId:he.fileId}))}:{}};await Id(a,L.sessionId,G,{json:t.flags.json===!0,streamJson:W(t.flags,"stream-json"),config:g});return}if(e==="resume"&&n.length>1)throw new
|
|
418
|
+
`);return}if(e==="workspaces"){if(n.length)throw new w("oracle workspaces does not accept positional arguments.");await Ad(t.endpointOverride,t.flags.json===!0);return}if(e==="use"){let b=n.join(" ").trim();if(!b)throw new w("oracle use needs a workspace name or id.","Run `anyforge oracle workspaces` to list your saved workspaces.");let g=await cs(t.endpointOverride,b);process.stdout.write(`Now using ${u(re(g))} (${g.scopes.join(", ")}) at ${g.endpoint}.
|
|
419
|
+
`);return}let a;try{a=await ws(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 L=await Qe({endpoint:t.endpointOverride??t.endpoint,...i?{workspace:i}:{}});process.stderr.write(`${ms(L)}
|
|
420
|
+
`),a=await ws(t.endpointOverride)}else throw b}if(e==="sessions"){if(n.length)throw new w("oracle sessions does not accept positional arguments.");let b=t.flags.json===!0;b||await gs(await a.config().catch(()=>{}));let g=await a.listSessions();Rd(g,b);return}if(e==="download"){await Ed(a,n,t.flags);return}if(e==="exec"||o!==void 0){if(W(t.flags,"write")||W(t.flags,"approve"))throw new w("oracle exec is read-only. Use interactive `anyforge oracle --write` for authorized actions.");if(t.flags.json===!0&&W(t.flags,"stream-json"))throw new w("Choose either --json or --stream-json.");let b=await xd(o??n.join(" ")),g=await a.config();Br(a,g),t.flags.json!==!0&&!W(t.flags,"stream-json")&&await gs(g),ys(Xe(t.flags,"context"));let x=Ar(t.flags),L=await a.createSession(kt(x,!1));await Ht(L.sessionId,a.endpoint,g.identity?.companyId);let j=await xn(a,L.sessionId,hs(t.flags),(he,et)=>ee(process.stderr,`Uploading explicit attachment ${he} (${K(et)}).
|
|
421
|
+
`)),G={requestId:ad(),text:b,readOnly:!0,...x?{context:x}:{},...Xe(t.flags,"context")?{contextText:Xe(t.flags,"context")}:{},...j.length?{attachments:j.map(he=>({fileId:he.fileId}))}:{}};await Id(a,L.sessionId,G,{json:t.flags.json===!0,streamJson:W(t.flags,"stream-json"),config:g});return}if(e==="resume"&&n.length>1)throw new w("oracle resume accepts at most one session id.");if(e==="resume"&&(!process.stdin.isTTY||!process.stdout.isTTY))throw new w("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();Br(a,c);let d=Ar(t.flags),p=Xe(t.flags,"context");ys(p);let l=W(t.flags,"shared"),k=W(t.flags,"write")||W(t.flags,"approve");if(k&&(!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(W(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 w("--shared requires --program or --initiative.");let O;if(e==="resume"){let g=(n[0]||void 0)??await _d(await a.listSessions(),await On(a.endpoint,c.identity?.companyId));O=await a.getSession(g)}else O=await a.createSession(kt(d,l));await Ht(O.sessionId,a.endpoint,c.identity?.companyId);let R=await Promise.resolve().then(()=>(Fn(),qn));await ts({version:R.default.version,client:a,config:c,session:O,initialPrompt:s,initialContext:d,contextText:p,shared:l,canWrite:k,canApprove:W(t.flags,"approve"),initialFiles:hs(t.flags),...a.source==="oauth"?{workspaces:bd(a.endpoint)}:{}})}function bd(t){return{list:()=>Nn(t),connect:async e=>{let n=new ae(Ie(e)),r=await n.config();return Br(n,r),{client:n,config:r}}}}function ms(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 kd(t,e){let n=u(re(t));e&&t.companyId&&!Od(t,e)&&process.stderr.write(`You asked for "${u(e)}", but ${n} was chosen on the consent page, so this login is for ${n}.
|
|
376
422
|
`);let r=xe(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>\`.
|
|
377
|
-
`)}function
|
|
423
|
+
`)}function Od(t,e){return t.companyId===e||t.companyName?.toLowerCase()===e.toLowerCase()}async function gs(t){let e=Ue(t?.identity);e&&await ee(process.stderr,`${Se(`Workspace: ${e}`)}
|
|
378
424
|
`)}async function Ad(t,e){let n=await _t(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)}
|
|
379
425
|
`);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"}:`)}
|
|
380
426
|
`);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?Gr(`* ${s}`):` ${s}`,a?Se(a):"",Se(o.scopes.join(", ")),i?vs("active"):""];process.stdout.write(`${c.filter(Boolean).join(" ")}
|
|
381
427
|
`)}}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.
|
|
382
|
-
`)}function W(t,e){return t[e]===!0}function Xe(t,e){let n=t[e];return typeof n=="string"?n:void 0}function hs(t){let e=t.file;return typeof e=="string"?[e]:Array.isArray(e)?e:[]}async function
|
|
428
|
+
`)}function W(t,e){return t[e]===!0}function Xe(t,e){let n=t[e];return typeof n=="string"?n:void 0}function hs(t){let e=t.file;return typeof e=="string"?[e]:Array.isArray(e)?e:[]}async function ws(t,e){try{return new ae(await Ln(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 Br(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 ys(t){if(t&&Buffer.byteLength(t,"utf8")>Ir)throw new w(`--context must be ${Ir/1024} KiB or less.`)}async function xd(t){let e=t.trim();if(process.stdin.isTTY){if(e)return e;throw new w("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>yd)throw new w("Piped Oracle input exceeds the 1 MiB limit.");n.push(s)}let o=Buffer.concat(n).toString("utf8").trim();if(!e&&!o)throw new w("oracle exec needs a prompt in argv or piped stdin.");return[e,o].filter(Boolean).join(`
|
|
383
429
|
|
|
384
430
|
`)}function Rd(t,e){if(e){process.stdout.write(`${JSON.stringify(t,null,2)}
|
|
385
431
|
`);return}if(!t.length){process.stdout.write(`No Oracle sessions are available.
|
|
386
|
-
`);return}for(let n of t){let r=
|
|
387
|
-
`)}}async function _d(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 md,i=new
|
|
432
|
+
`);return}for(let n of t){let r=ke(n);process.stdout.write(`${Gr(n.sessionId)} ${u(n.title)} ${Se(r)}
|
|
433
|
+
`)}}async function _d(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 md,i=new wd(o),s=new hd(Gr("Resume an Oracle session")),a=new gd(t.map(l=>({value:l.sessionId,label:l.title,description:ke(l)})),12,mt),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 Id(t,e,n,r){let o=new Re,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 x=await t.cancelTurn(e,o.streamId);await ee(process.stderr,`${bt(x)}
|
|
388
434
|
`)}catch(x){await ee(process.stderr,`Could not confirm server cancellation: ${z(x)}
|
|
389
435
|
`)}else try{let x=await t.cancelRequest(e,n.requestId);await ee(process.stderr,`${bt(x)}
|
|
390
436
|
`)}catch(x){await ee(process.stderr,`Could not confirm server cancellation by request id: ${z(x)}
|
|
391
437
|
`)}},p=()=>{c().catch(()=>{})};process.on("SIGINT",p);let l=async x=>ee(process.stdout,`${JSON.stringify(be(x))}
|
|
392
|
-
`),
|
|
438
|
+
`),k=new Wr(e),O=-1,R=async x=>{if(r.streamJson){if(x.idx<=O)return;x.type!=="finalizing"&&(O=x.idx),await l({...x,sessionId:e})}else await k.emit(x)},b;try{b=await vt(t,e,n,{onEvent:R},a.signal,o)}catch(x){if(!i)throw process.off("SIGINT",p),x;b=o.outcome()}finally{process.off("SIGINT",p),s&&await s}let g=Wt(e,b);return r.streamJson?await l({protocolVersion:1,type:"result",result:g}):r.json?await ee(process.stdout,`${JSON.stringify(g,null,2)}
|
|
393
439
|
`):(g.answer&&await ee(process.stdout,`${Eo(g.answer,t.endpoint)}
|
|
394
440
|
`),g.status==="awaiting_input"&&await ee(process.stderr,`Oracle needs a response; resume this session interactively to answer the question.
|
|
395
441
|
`),g.status==="interrupted"&&await ee(process.stderr,`Oracle turn ended without a complete final answer.
|
|
@@ -397,10 +443,10 @@ ${e}`,i=Tn.get(o);if(i)return i;let s=An(()=>nd(r,e,n));Tn.set(o,s);try{return a
|
|
|
397
443
|
`),(g.servedBy||g.usage||g.rounds!==void 0)&&await ee(process.stderr,Sd(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 Wr=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}
|
|
398
444
|
`)}describe(e){switch(e.type){case"tool_call":{if(dt(e.name))return"";let n=C(typeof e.label=="string"?e.label:lt(C(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")} ${vs(n)}`}case"tool_result":{if(e.ok!==!1)return"";let n=typeof e.toolUseId=="string"?this.labels.get(e.toolUseId):void 0;return Un(`Oracle \xB7 ${n??"a tool"} failed`)}case"warning":return Un(`Warning: ${C(e.message,500)}`);case"navigator_file_error":return Un(`Could not generate ${C(e.filename,200)}: ${C(e.error,500)}`);case"navigator_file_created":{let n=Fe(e.file);return Se(`Generated ${n.filename} (${K(n.sizeBytes)}). Download: anyforge oracle download ${this.sessionId} ${n.fileId} --output .`)}case"provider_fallback":{let n=C(e.from,120),r=C(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`${Un("Attachment")} ${C(r.name,160)}: ${C(r.message,500)}`}).join(`
|
|
399
445
|
`):"";default:return""}}};function Sd(t,e,n){let r=[];if(t&&typeof t=="object"){let o=t,i=zt(o);i&&r.push(`model ${i}`),typeof o.routing=="string"&&r.push(C(o.routing.replaceAll("_"," "),40)),typeof o.configuredModel=="string"&&o.configuredModel!==o.model&&r.push(`configured ${C(o.configuredModel,100)}`),typeof o.provider=="string"&&r.push(C(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 "))}
|
|
400
|
-
`:""}async function Ed(t,e,n){if(e.length!==2)throw new
|
|
401
|
-
`)}function Td(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 Cd(t){let e=ud(t.replace(/\\/g,"/")).replace(/[\u0000-\u001f\u007f/:]/g,"_").replace(/^\.+/,"").trim().slice(0,180);return!e||e==="."||e===".."?"oracle-file.bin":e}async function $d(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 Dd,Writable as Bd}from"node:stream";import{ndJsonStream as Wd}from"@agentclientprotocol/sdk";import{randomUUID as Pd}from"node:crypto";import{isAbsolute as Ld}from"node:path";import{setTimeout as Nd}from"node:timers/promises";import{agent as qd,RequestError as q}from"@agentclientprotocol/sdk";import{RequestError as Mn}from"@agentclientprotocol/sdk";var
|
|
446
|
+
`:""}async function Ed(t,e,n){if(e.length!==2)throw new w("oracle download needs exactly a session id and file id.");let[r,o]=e;if(!r||!o)throw new w("oracle download needs a session id and file id.");let i=Xe(n,"output");if(!i)throw new w("oracle download requires --output <existing-directory>.");let s=fd(i),a=await ld(s);if(!(await dd(a)).isDirectory())throw new w("--output must point to an existing directory.");let d=await t.download(r,o),p=Cd(Td(d.headers.get("content-disposition"))),l=pd(a,p),k=await $d(d,vd),O;try{O=await cd(l,"wx",384),await O.writeFile(k)}catch(R){throw R.code==="EEXIST"?new f(`Refusing to overwrite ${l}.`,m.FAILURE):R}finally{await O?.close()}process.stdout.write(`Downloaded ${u(p)} to ${u(l)} (${K(k.byteLength)}).
|
|
447
|
+
`)}function Td(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 Cd(t){let e=ud(t.replace(/\\/g,"/")).replace(/[\u0000-\u001f\u007f/:]/g,"_").replace(/^\.+/,"").trim().slice(0,180);return!e||e==="."||e===".."?"oracle-file.bin":e}async function $d(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 Dd,Writable as Bd}from"node:stream";import{ndJsonStream as Wd}from"@agentclientprotocol/sdk";import{randomUUID as Pd}from"node:crypto";import{isAbsolute as Ld}from"node:path";import{setTimeout as Nd}from"node:timers/promises";import{agent as qd,RequestError as q}from"@agentclientprotocol/sdk";import{RequestError as Mn}from"@agentclientprotocol/sdk";var ks=65536;function Os(t){if(t.length>100)throw Mn.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]
|
|
402
448
|
${JSON.stringify({name:r.name,uri:r.uri,description:r.description})}`;throw Mn.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(`
|
|
403
|
-
`);if(!n.trim()||Buffer.byteLength(n,"utf8")>
|
|
449
|
+
`);if(!n.trim()||Buffer.byteLength(n,"utf8")>ks)throw Mn.invalidParams(void 0,`Prompt must contain text and fit within ${ks} UTF-8 bytes.`);return n}function te(){throw Mn.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 Hr(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 As(t,e){if(e===`/skip ${t.id}`)return Hr(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 Hr(t,n)}function It(t){let e=t.questions.map(r=>`${r.header}: ${r.question}
|
|
404
450
|
${r.options.map(o=>` ${JSON.stringify(o.id)}: ${o.label} \u2014 ${o.description}${o.confirms?" (confirms an action)":""}`).join(`
|
|
405
451
|
`)}`).join(`
|
|
406
452
|
|
|
@@ -412,14 +458,15 @@ Or skip: /skip ${t.id}`:""}`}function xs(t){return{type:"object",properties:Obje
|
|
|
412
458
|
${e.options.map(r=>`${r.id}: ${r.label} \u2014 ${r.description}${r.confirms?" (confirms an action)":""}`).join(`
|
|
413
459
|
`)}`,enum:e.options.map(r=>r.id),enumNames:e.options.map(r=>r.label)}])),required:t.questions.map((e,n)=>`answer_${n}`)}}function Rs(t,e){if(!e||typeof e!="object"||Array.isArray(e))return te();let n=e;return Object.keys(n).length!==t.questions.length?te():Hr(t,{questionId:t.id,answers:t.questions.map((r,o)=>{let i=n[`answer_${o}`];return typeof i=="string"&&(r.multiSelect||!r.options.some(s=>s.id===i))?{questionId:r.id,optionIds:[],customText:i}:{questionId:r.id,optionIds:r.multiSelect?i:[i]}})})}function Fd(t){return{client:async()=>new ae(await Ln(t.endpointOverride,t.workspace)),login:async e=>{let n=await _t(t.endpointOverride),r=await Qe({endpoint:n.endpoint??Pn,workspace:t.workspace,write:t.write,approve:t.approve,signal:e,onAuthorizationUrl:o=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
|
|
414
460
|
${o}
|
|
415
|
-
`)});return new ae(Ie(r))}}}var jn=class{constructor(e,n=Fd(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 Md(r)}};return qd({name:"anyforge"}).onRequest("initialize",({params:n})=>this.initialize(n)).onRequest("authenticate",({params:n})=>e(async()=>{if(this.ready(),n.methodId!=="oracle-browser")throw q.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 q.invalidRequest(void 0,"This connection is already initialized.");if(!Number.isSafeInteger(e.protocolVersion)||e.protocolVersion<1)throw q.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 q.invalidRequest(void 0,"Initialize the ACP connection first.");if(this.stopping)throw q.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 q.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(),!Ld(e.cwd))throw q.invalidParams(void 0,"ACP cwd must be an absolute path. Hosted mode does not read this directory.");if(this.loginPromise)throw q.invalidRequest(void 0,"Wait for authentication to finish.");if(this.sessions.size+this.pendingSetups>=128&&(!("sessionId"in e)||!this.sessions.has(e.sessionId)))throw q.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 q.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 q.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 q.invalidRequest(void 0,"The hosted session is still running. Wait for it to settle before loading it.");if(this.stopping)throw q.requestCancelled();this.sessions.set(e.sessionId,{client:r,question:Ud(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 q.invalidParams(void 0,"Load or create this session before prompting it.");if(r.active||this.loading.has(e.sessionId))throw q.invalidRequest(void 0,"A prompt or load is already running in this session. Wait for it to settle.");let o=
|
|
461
|
+
`)});return new ae(Ie(r))}}}var jn=class{constructor(e,n=Fd(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 Md(r)}};return qd({name:"anyforge"}).onRequest("initialize",({params:n})=>this.initialize(n)).onRequest("authenticate",({params:n})=>e(async()=>{if(this.ready(),n.methodId!=="oracle-browser")throw q.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 q.invalidRequest(void 0,"This connection is already initialized.");if(!Number.isSafeInteger(e.protocolVersion)||e.protocolVersion<1)throw q.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 q.invalidRequest(void 0,"Initialize the ACP connection first.");if(this.stopping)throw q.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 q.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(),!Ld(e.cwd))throw q.invalidParams(void 0,"ACP cwd must be an absolute path. Hosted mode does not read this directory.");if(this.loginPromise)throw q.invalidRequest(void 0,"Wait for authentication to finish.");if(this.sessions.size+this.pendingSetups>=128&&(!("sessionId"in e)||!this.sessions.has(e.sessionId)))throw q.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 q.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 q.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 q.invalidRequest(void 0,"The hosted session is still running. Wait for it to settle before loading it.");if(this.stopping)throw q.requestCancelled();this.sessions.set(e.sessionId,{client:r,question:Ud(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 q.invalidParams(void 0,"Load or create this session before prompting it.");if(r.active||this.loading.has(e.sessionId))throw q.invalidRequest(void 0,"A prompt or load is already running in this session. Wait for it to settle.");let o=Os(e.prompt),i=r.question?As(r.question,o):void 0;if(!r.question&&(o.startsWith("/answer ")||o.startsWith("/skip ")))throw q.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=Pd();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}:{}},k=new Re,O=new Set,R=await vt(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"&&!O.has(g.id))O.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):lt(typeof g.name=="string"?g.name:""),status:"in_progress"});else if(g.type==="tool_result"&&typeof g.toolUseId=="string"&&O.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=Gt(g);else if(g.type==="navigator_file_created"){let x=Fe(g.file);await this.text(s,r,`Generated file: ${x.filename} (${x.mediaType}, ${x.sizeBytes} bytes). Session id: ${r}; file id: ${x.fileId}. Download using anyforge oracle download with an explicit output directory.`)}}},n.abort.signal,k);if(n.requestId=void 0,R.error)throw q.internalError(void 0,R.error);if(i&&(e.question=R.question),n.cancelled||R.done?.exitReason==="cancelled")return{stopReason:"cancelled"};if(!R.question){let g=R.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 q.internalError(void 0,`Oracle stopped before completion (${typeof g=="string"?u(g):"unknown reason"}). Resume the hosted session to inspect it.`)}if(e.question=R.question,!this.formSupported)return await this.text(s,r,It(R.question)),{stopReason:"end_turn"};let b;try{b=await jd(s.request("elicitation/create",{mode:"form",sessionId:r,message:It(R.question),requestedSchema:xs(R.question)},{cancellationSignal:n.interaction.signal}),n.interaction.signal)}catch(g){if(n.cancelled)return{stopReason:"cancelled"};if(!(g instanceof q)||g.code!==-32601)throw g;return await this.text(s,r,It(R.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.
|
|
416
462
|
`+It(R.question)),{stopReason:"end_turn"};i=Rs(R.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)}
|
|
417
463
|
`),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 Nd(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 Ud(t){let e=t.messages.at(-1);return e?.role==="assistant"?e.question:void 0}function Md(t){return t instanceof q?t:t instanceof f&&t.exitCode===m.NO_CREDENTIALS||t instanceof le&&t.status===401?q.authRequired(void 0,"Sign in with authenticate or run anyforge oracle login, then retry."):q.internalError(void 0,z(t))}function jd(t,e){return new Promise((n,r)=>{let o=()=>r(q.requestCancelled());e.addEventListener("abort",o,{once:!0}),t.then(n,r).finally(()=>e.removeEventListener("abort",o)).catch(()=>{}),e.aborted&&o()})}var Gd=new Set(["endpoint","workspace","write","approve","read-only","program","initiative","codebase","context","login","help"]);function _s(){return`AnyForge ACP \u2014 hosted Oracle over ACP v1 JSON-RPC stdio.
|
|
418
464
|
|
|
419
465
|
Usage: anyforge acp [options]
|
|
420
466
|
|
|
421
|
-
|
|
422
|
-
|
|
467
|
+
Editors that install from the ACP Registry run npx -y @anyforge/control acp.
|
|
468
|
+
In T3 Code, add a Local ACP command with executable anyforge and argument acp.
|
|
469
|
+
No local coding agent is started.
|
|
423
470
|
|
|
424
471
|
Options:
|
|
425
472
|
--workspace <name|id> Select one saved Oracle workspace
|
|
@@ -450,8 +497,10 @@ with the displayed /answer JSON command. Ordinary prose never confirms actions.
|
|
|
450
497
|
Sessions support new, load, text/tool progress and cancellation. Local modes,
|
|
451
498
|
model selection, session list/fork/delete and local tool execution are not offered.
|
|
452
499
|
Protocol traffic uses stdout; diagnostics use stderr.
|
|
453
|
-
`}function Hd(t,e){if(t.positional.length)throw new
|
|
454
|
-
|
|
500
|
+
`}function Hd(t,e){if(t.positional.length)throw new w("acp does not accept positional arguments. Prompts arrive over ACP stdio.");let n=Object.keys(t.flags).find(d=>!Gd.has(d));if(n)throw new w(`--${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 w("--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 w("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 w("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 w("--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 w("--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 Is(t,e){let n=Hd(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 _t(n.endpointOverride),p=await Qe({endpoint:d.endpoint??Pn,workspace:n.workspace,write:n.write,approve:n.approve,signal:a.signal,onAuthorizationUrl:l=>process.stderr.write(`Authorize AnyForge Oracle in your browser:
|
|
501
|
+
${l}
|
|
502
|
+
`)});await new ae(Ie(p)).config(),process.stderr.write(`AnyForge Oracle login saved. Restart or retry your ACP session.
|
|
503
|
+
`)}finally{process.removeListener("SIGTERM",c),process.removeListener("SIGINT",c)}return}let r=new jn(n),o=Wd(Bd.toWeb(process.stdout),Dd.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 Ts="https://crew.anyforge.ai",zd=new Set(["endpoint","limit","status","type","priority","initiative","program","search","assignee","notes","input","context","messages","scope","client","days","profile","interval","codebase","file","output","workspace","repo","branch","print","event","actor","from","to","cursor","role","team"]),Vd=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 Jd(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(zd.has(i)){let a=s??t[++n];if(a===void 0)throw new w(`--${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(Vd.has(i)){e.flags[i]=s===void 0?!0:s!=="false";continue}throw new w(`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 w(`--${e} must be a positive number, got "${t}".`);return n}function T(t){return typeof t=="string"?t:void 0}function Dn(t){return t===!0?!0:void 0}function Yd(){console.log(`
|
|
455
504
|
${D("AnyForge Control")} \u2014 governed LLM proxy, and a scriptable AnyForge client.
|
|
456
505
|
|
|
457
506
|
${D("USAGE")}
|
|
@@ -636,6 +685,7 @@ ${D("IN THE CONVERSATION")}
|
|
|
636
685
|
/catchup What needs you, what landed, what is running
|
|
637
686
|
(acting on its decisions needs --write --approve)
|
|
638
687
|
/remember [what] Save what this conversation established
|
|
688
|
+
/skillify [what] Propose what this conversation did as a team skill
|
|
639
689
|
|
|
640
690
|
Enter sends; while Oracle works it queues one message. Shift+Enter adds a
|
|
641
691
|
line. Esc or Ctrl+C interrupts an answer and keeps the session open; press
|
|
@@ -649,8 +699,8 @@ Examples:
|
|
|
649
699
|
anyforge oracle login --workspace "Acme Engineering"
|
|
650
700
|
anyforge oracle exec "What is blocked?" --workspace "Beta Labs"
|
|
651
701
|
cat incident.log | anyforge oracle exec "Explain this log" --stream-json
|
|
652
|
-
`)}async function Xd(t,e,n,r){let o=e.flags.json===!0,[i]=e.positional;if(t==="oracle")return bs({positional:e.positional,flags:e.flags,endpointOverride:r,endpoint:n});if(t==="acp"){let a=await Promise.resolve().then(()=>(Fn(),qn));return Is({positional:e.positional,flags:e.flags,endpointOverride:r},a.default.version)}switch(t){case"init":return go({endpoint:n,force:e.flags.force===!0});case"logout":return Ro();case"status":return Co({endpoint:r,json:o});case"whoami":return Ao({endpoint:r,json:o});case"apply-patch":return Po({patchFile:i,threeWay:e.flags["3way"]===!0,dryRun:e.flags["dry-run"]===!0});case"proxy":console.log(Kd("Local proxy mode is coming in a future release.")),console.log(Ze(" Until then, your agents talk directly to the AnyForge proxy."));return;case"skill":return fi({subcommand:i,name:e.positional[1],scope:T(e.flags.scope),client:T(e.flags.client),force:e.flags.force===!0,json:o})}let s=await $e(r);switch(t){case"approvals":return Lo({creds:s,json:o,limit:ge(e.flags.limit,"limit")});case"approve":return sr({creds:s,json:o,approvalId:i,decision:"APPROVED",notes:T(e.flags.notes)});case"reject":return sr({creds:s,json:o,approvalId:i,decision:"REJECTED",notes:T(e.flags.notes)});case"crews":return No({creds:s,json:o,status:T(e.flags.status),activeOnly:Dn(e.flags.active),limit:ge(e.flags.limit,"limit")});case"crew":{let[a,c]=e.positional;return a==="start"?jo({creds:s,json:o,workItemId:c,profileId:T(e.flags.profile),follow:e.flags.follow===!0,intervalSeconds:ge(e.flags.interval,"interval")}):a==="follow"?lr({creds:s,json:o,crewId:c,intervalSeconds:ge(e.flags.interval,"interval")}):qo({creds:s,json:o,crewId:i,messages:ge(e.flags.messages,"messages")})}case"halt":return cr({creds:s,json:o,crewId:i,action:"halt"});case"resume":return cr({creds:s,json:o,crewId:i,action:"resume"});case"studio":return Do({creds:s,json:o,command:i,id:e.positional[1],input:T(e.flags.input),context:T(e.flags.context),file:T(e.flags.file),repo:T(e.flags.repo),branch:T(e.flags.branch),initiative:T(e.flags.initiative),cursor:T(e.flags.cursor)});case"work":return Go({creds:s,json:o,status:T(e.flags.status),type:T(e.flags.type),priority:T(e.flags.priority),initiative:T(e.flags.initiative),program:T(e.flags.program),search:T(e.flags.search),assignee:T(e.flags.assignee),includeArchived:Dn(e.flags.archived),limit:ge(e.flags.limit,"limit"),all:Dn(e.flags.all)});case"workflows":return Ho({creds:s,json:o});case"run":return Ko({creds:s,json:o,workflowId:i,input:T(e.flags.input)});case"ask":return zo({creds:s,json:o,question:e.positional.join(" "),context:T(e.flags.context)});case"backtest-corpus":return Jo({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-interception":return Qo({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-coverage":return Xo({creds:s,json:o,days:ge(e.flags.days,"days")});case"audit":return ii({creds:s,json:o,eventType:T(e.flags.event),actor:T(e.flags.actor),from:T(e.flags.from),to:T(e.flags.to),limit:ge(e.flags.limit,"limit"),cursor:T(e.flags.cursor),all:Dn(e.flags.all),verify:e.flags.verify===!0});case"people":return ci({creds:s,json:o,subcommand:i,email:e.positional[1],role:T(e.flags.role),team:T(e.flags.team),notify:e.flags["no-email"]!==!0});default:throw new
|
|
702
|
+
`)}async function Xd(t,e,n,r){let o=e.flags.json===!0,[i]=e.positional;if(t==="oracle")return bs({positional:e.positional,flags:e.flags,endpointOverride:r,endpoint:n});if(t==="acp"){let a=await Promise.resolve().then(()=>(Fn(),qn));return Is({positional:e.positional,flags:e.flags,endpointOverride:r},a.default.version)}switch(t){case"init":return go({endpoint:n,force:e.flags.force===!0});case"logout":return Ro();case"status":return Co({endpoint:r,json:o});case"whoami":return Ao({endpoint:r,json:o});case"apply-patch":return Po({patchFile:i,threeWay:e.flags["3way"]===!0,dryRun:e.flags["dry-run"]===!0});case"proxy":console.log(Kd("Local proxy mode is coming in a future release.")),console.log(Ze(" Until then, your agents talk directly to the AnyForge proxy."));return;case"skill":return fi({subcommand:i,name:e.positional[1],scope:T(e.flags.scope),client:T(e.flags.client),force:e.flags.force===!0,json:o})}let s=await $e(r);switch(t){case"approvals":return Lo({creds:s,json:o,limit:ge(e.flags.limit,"limit")});case"approve":return sr({creds:s,json:o,approvalId:i,decision:"APPROVED",notes:T(e.flags.notes)});case"reject":return sr({creds:s,json:o,approvalId:i,decision:"REJECTED",notes:T(e.flags.notes)});case"crews":return No({creds:s,json:o,status:T(e.flags.status),activeOnly:Dn(e.flags.active),limit:ge(e.flags.limit,"limit")});case"crew":{let[a,c]=e.positional;return a==="start"?jo({creds:s,json:o,workItemId:c,profileId:T(e.flags.profile),follow:e.flags.follow===!0,intervalSeconds:ge(e.flags.interval,"interval")}):a==="follow"?lr({creds:s,json:o,crewId:c,intervalSeconds:ge(e.flags.interval,"interval")}):qo({creds:s,json:o,crewId:i,messages:ge(e.flags.messages,"messages")})}case"halt":return cr({creds:s,json:o,crewId:i,action:"halt"});case"resume":return cr({creds:s,json:o,crewId:i,action:"resume"});case"studio":return Do({creds:s,json:o,command:i,id:e.positional[1],input:T(e.flags.input),context:T(e.flags.context),file:T(e.flags.file),repo:T(e.flags.repo),branch:T(e.flags.branch),initiative:T(e.flags.initiative),cursor:T(e.flags.cursor)});case"work":return Go({creds:s,json:o,status:T(e.flags.status),type:T(e.flags.type),priority:T(e.flags.priority),initiative:T(e.flags.initiative),program:T(e.flags.program),search:T(e.flags.search),assignee:T(e.flags.assignee),includeArchived:Dn(e.flags.archived),limit:ge(e.flags.limit,"limit"),all:Dn(e.flags.all)});case"workflows":return Ho({creds:s,json:o});case"run":return Ko({creds:s,json:o,workflowId:i,input:T(e.flags.input)});case"ask":return zo({creds:s,json:o,question:e.positional.join(" "),context:T(e.flags.context)});case"backtest-corpus":return Jo({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-interception":return Qo({creds:s,json:o,days:ge(e.flags.days,"days")});case"review-coverage":return Xo({creds:s,json:o,days:ge(e.flags.days,"days")});case"audit":return ii({creds:s,json:o,eventType:T(e.flags.event),actor:T(e.flags.actor),from:T(e.flags.from),to:T(e.flags.to),limit:ge(e.flags.limit,"limit"),cursor:T(e.flags.cursor),all:Dn(e.flags.all),verify:e.flags.verify===!0});case"people":return ci({creds:s,json:o,subcommand:i,email:e.positional[1],role:T(e.flags.role),team:T(e.flags.team),notify:e.flags["no-email"]!==!0});default:throw new w(`Unknown command: ${t}`,"Run `anyforge --help` for the command list.")}}async function Zd(){let t;try{t=Jd(process.argv.slice(2))}catch(r){Es(r);return}if(!t.command&&t.flags.login===!0&&(t.command="acp"),t.flags.version===!0){let r=await Promise.resolve().then(()=>(Fn(),qn));console.log(r.default.version);return}if(t.command==="oracle"&&t.flags.help===!0){Qd();return}if(t.command==="acp"&&t.flags.help===!0){console.log(_s());return}if(t.flags.help===!0||!t.command){Yd();return}let e=T(t.flags.endpoint)??process.env.ANYFORGE_ENDPOINT,n=e??Ts;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 w(`--${o} is only supported by the oracle command.`)}if(t.command!=="acp"&&t.flags.login!==void 0)throw new w("--login is only supported by the acp command.");vo(t.command),await Xd(t.command,t,n,e)}catch(r){Es(r)}}function Es(t){t instanceof f&&(process.stderr.write(Ss("Error: ")+u(t.message)+`
|
|
653
703
|
`),t.hint?process.stderr.write(Ze(u(t.hint))+`
|
|
654
|
-
`):t instanceof
|
|
704
|
+
`):t instanceof w&&process.stderr.write(Ze("Run `anyforge --help`.")+`
|
|
655
705
|
`),process.exit(t.exitCode)),process.stderr.write(Ss("Error: ")+u(t instanceof Error?t.message:String(t))+`
|
|
656
706
|
`),process.exit(m.FAILURE)}Zd();
|