@burtson-labs/bandit-stealth-cli 1.7.267 → 1.7.269

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.
Files changed (2) hide show
  1. package/dist/cli.js +3 -3
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1404,7 +1404,7 @@ ${(()=>{let Z=`Bandit insights \u2014 ${new Date(s).toISOString().slice(0,10)}`,
1404
1404
  <h1>You're signed in.</h1>
1405
1405
  <p>Bandit picked up your session. You can close this tab and return to your terminal.</p>
1406
1406
  </div>
1407
- </body></html>`;t(ITr,"startLoopbackListener");t(ATr,"openBrowser");t(NTr,"buildDefaultDeviceLabel");t(OTr,"runOAuthSignIn")});var Kue=Nt((XOr,FTr)=>{FTr.exports={name:"@burtson-labs/bandit-stealth-cli",version:"1.7.267",description:"Bandit \u2014 a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",keywords:["ai","agent","cli","coding-agent","llm","ollama","local-first","bandit","burtson-labs","terminal","repl","developer-tools"],homepage:"https://burtson.ai",bugs:{email:"team@burtson.ai"},license:"MIT",author:{name:"Burtson Labs",email:"team@burtson.ai",url:"https://burtson.ai"},bin:{bandit:"./dist/cli.js"},main:"dist/cli.js",files:["dist/cli.js","README.md","LICENSE"],engines:{node:">=20"},publishConfig:{access:"public"},scripts:{typecheck:"tsc -p tsconfig.json --noEmit",build:"node build.mjs","build:publish":"node build.mjs --publish",dev:"node build.mjs --watch",start:"node dist/cli.js",smoke:"node build.mjs && node dist/__smoke__/smoke.js",integration:"node build.mjs && node dist/__integration__/ollama.js",eval:"node build.mjs && node dist/__eval__/eval.js",benchmark:"node build.mjs && node dist/__eval__/benchmark.js","gen-logo":"node scripts/gen-logo.mjs","preview-banner":"node scripts/preview-banner.mjs",clean:"rm -rf dist",prepack:"node scripts/prepack.mjs",postpack:"node scripts/postpack.mjs",prepublishOnly:"pnpm run clean && pnpm run typecheck && pnpm run build:publish"},dependencies:{"pdf-parse":"^2.4.5"},devDependencies:{"@burtson-labs/agent-core":"workspace:*","@burtson-labs/host-kit":"workspace:*","@burtson-labs/stealth-core-runtime":"workspace:*","@types/node":"^20.11.0","@types/pdf-parse":"^1.1.5","@types/pngjs":"^6.0.5",esbuild:"^0.28.0",pngjs:"^7.0.0",typescript:"^5.4.0"}}});var mkr={};module.exports=zet(mkr);var Tp=du(require("fs")),Xue=du(require("os")),Ld=du(require("path")),U9=du(require("readline")),Yue=du(require("child_process")),Yp=du(S9()),w0=du(OOe());var H6=du(require("fs")),FOe=du(require("os")),fT=du(require("path")),OZ=du(require("child_process"));function xb(a){return a==="~"?FOe.homedir():a.startsWith("~/")?fT.join(FOe.homedir(),a.slice(2)):a}t(xb,"expandHome");var F9=16*1024,NZ=32*1024,A_t=1e4,a1r=3e4,MOe=200,ROe=new Set(["node_modules",".git","dist","build","out",".next",".turbo","coverage","target","__pycache__",".venv","venv"]);function s1r(a){let s=t(_=>{let u=_.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!u)return[_];let[,y,S,P]=u;return S.split(",").map(O=>`${y}${O.trim()}${P}`)},"braceExpand"),c=a.match(/^([^*{}]+?)\/\*\*\/(.+)$/);if(c){let[,_,u]=c;return{includes:s(u),subDir:_}}return{includes:s(a),subDir:""}}t(s1r,"expandGlobForGrep");var bM=class{constructor(s,c,_={}){this.workspaceRoot=s;this.languageAdapters=c;this.options=_;this._readFiles=new Set;this.customRepoRoots=_.customRepoRoots&&_.customRepoRoots.length>0?_.customRepoRoots:void 0}static{t(this,"CliToolExecutionContext")}markFileRead(s){this._readFiles.add(xb(s))}hasFileBeenRead(s){return this._readFiles.has(xb(s))}async readFile(s){return H6.promises.readFile(xb(s),"utf-8")}async writeFile(s,c){let _=xb(s);if(this.options.approveWrite&&!await this.options.approveWrite(_,c))throw new Error(`Write to ${_} rejected by user`);await H6.promises.mkdir(fT.dirname(_),{recursive:!0}),await H6.promises.writeFile(_,c,"utf-8")}async deleteFile(s){let c=fT.resolve(xb(s)),_=fT.resolve(this.workspaceRoot);if(!c.startsWith(_+fT.sep)&&c!==_)throw new Error(`Refusing to delete outside workspace: ${s}`);await H6.promises.unlink(c)}async listFiles(s,c){let _=xb(c??this.workspaceRoot),u=o1r(s),y=[];return await N_t(_,_,u,y),y.slice(0,MOe).sort()}async listDirectoryEntries(s){let c=xb(s),_=await H6.promises.readdir(c,{withFileTypes:!0}),u=[];for(let y of _){if(y.name.startsWith("."))continue;let S=y.isDirectory();if(y.isSymbolicLink())try{S=(await H6.promises.stat(fT.join(c,y.name))).isDirectory()}catch{S=!1}u.push(S?`${y.name}/`:y.name)}return u.sort()}async searchCode(s,c,_){let u=xb(c??this.workspaceRoot);return this.runRipgrep(s,u,_).catch(()=>this.runGrep(s,u,_))}async runCommand(s,c,_){let u=c.map(xb),y=_?xb(_):this.workspaceRoot,S={...process.env};if((s.split(/[\\/]/).pop()??s)==="gh")for(let O of["GITHUB_TOKEN","GH_TOKEN"]){let L=S[O];typeof L=="string"&&L.trim()===""&&delete S[O]}return new Promise(O=>{let L="",z="",Z=OZ.spawn(s,u,{cwd:y,shell:process.platform==="win32",env:S}),U=setTimeout(()=>{Z.kill("SIGTERM"),O({stdout:L.slice(0,NZ),stderr:z+`
1407
+ </body></html>`;t(ITr,"startLoopbackListener");t(ATr,"openBrowser");t(NTr,"buildDefaultDeviceLabel");t(OTr,"runOAuthSignIn")});var Kue=Nt((XOr,FTr)=>{FTr.exports={name:"@burtson-labs/bandit-stealth-cli",version:"1.7.269",description:"Bandit \u2014 a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",keywords:["ai","agent","cli","coding-agent","llm","ollama","local-first","bandit","burtson-labs","terminal","repl","developer-tools"],homepage:"https://burtson.ai",bugs:{email:"team@burtson.ai"},license:"MIT",author:{name:"Burtson Labs",email:"team@burtson.ai",url:"https://burtson.ai"},bin:{bandit:"./dist/cli.js"},main:"dist/cli.js",files:["dist/cli.js","README.md","LICENSE"],engines:{node:">=20"},publishConfig:{access:"public"},scripts:{typecheck:"tsc -p tsconfig.json --noEmit",build:"node build.mjs","build:publish":"node build.mjs --publish",dev:"node build.mjs --watch",start:"node dist/cli.js",smoke:"node build.mjs && node dist/__smoke__/smoke.js",integration:"node build.mjs && node dist/__integration__/ollama.js",eval:"node build.mjs && node dist/__eval__/eval.js",benchmark:"node build.mjs && node dist/__eval__/benchmark.js","gen-logo":"node scripts/gen-logo.mjs","preview-banner":"node scripts/preview-banner.mjs",clean:"rm -rf dist",prepack:"node scripts/prepack.mjs",postpack:"node scripts/postpack.mjs",prepublishOnly:"pnpm run clean && pnpm run typecheck && pnpm run build:publish"},dependencies:{"pdf-parse":"^2.4.5"},devDependencies:{"@burtson-labs/agent-core":"workspace:*","@burtson-labs/host-kit":"workspace:*","@burtson-labs/stealth-core-runtime":"workspace:*","@types/node":"^20.11.0","@types/pdf-parse":"^1.1.5","@types/pngjs":"^6.0.5",esbuild:"^0.28.0",pngjs:"^7.0.0",typescript:"^5.4.0"}}});var mkr={};module.exports=zet(mkr);var Tp=du(require("fs")),Xue=du(require("os")),Ld=du(require("path")),U9=du(require("readline")),Yue=du(require("child_process")),Yp=du(S9()),w0=du(OOe());var H6=du(require("fs")),FOe=du(require("os")),fT=du(require("path")),OZ=du(require("child_process"));function xb(a){return a==="~"?FOe.homedir():a.startsWith("~/")?fT.join(FOe.homedir(),a.slice(2)):a}t(xb,"expandHome");var F9=16*1024,NZ=32*1024,A_t=1e4,a1r=3e4,MOe=200,ROe=new Set(["node_modules",".git","dist","build","out",".next",".turbo","coverage","target","__pycache__",".venv","venv"]);function s1r(a){let s=t(_=>{let u=_.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!u)return[_];let[,y,S,P]=u;return S.split(",").map(O=>`${y}${O.trim()}${P}`)},"braceExpand"),c=a.match(/^([^*{}]+?)\/\*\*\/(.+)$/);if(c){let[,_,u]=c;return{includes:s(u),subDir:_}}return{includes:s(a),subDir:""}}t(s1r,"expandGlobForGrep");var bM=class{constructor(s,c,_={}){this.workspaceRoot=s;this.languageAdapters=c;this.options=_;this._readFiles=new Set;this.customRepoRoots=_.customRepoRoots&&_.customRepoRoots.length>0?_.customRepoRoots:void 0}static{t(this,"CliToolExecutionContext")}markFileRead(s){this._readFiles.add(xb(s))}hasFileBeenRead(s){return this._readFiles.has(xb(s))}async readFile(s){return H6.promises.readFile(xb(s),"utf-8")}async writeFile(s,c){let _=xb(s);if(this.options.approveWrite&&!await this.options.approveWrite(_,c))throw new Error(`Write to ${_} rejected by user`);await H6.promises.mkdir(fT.dirname(_),{recursive:!0}),await H6.promises.writeFile(_,c,"utf-8")}async deleteFile(s){let c=fT.resolve(xb(s)),_=fT.resolve(this.workspaceRoot);if(!c.startsWith(_+fT.sep)&&c!==_)throw new Error(`Refusing to delete outside workspace: ${s}`);await H6.promises.unlink(c)}async listFiles(s,c){let _=xb(c??this.workspaceRoot),u=o1r(s),y=[];return await N_t(_,_,u,y),y.slice(0,MOe).sort()}async listDirectoryEntries(s){let c=xb(s),_=await H6.promises.readdir(c,{withFileTypes:!0}),u=[];for(let y of _){if(y.name.startsWith("."))continue;let S=y.isDirectory();if(y.isSymbolicLink())try{S=(await H6.promises.stat(fT.join(c,y.name))).isDirectory()}catch{S=!1}u.push(S?`${y.name}/`:y.name)}return u.sort()}async searchCode(s,c,_){let u=xb(c??this.workspaceRoot);return this.runRipgrep(s,u,_).catch(()=>this.runGrep(s,u,_))}async runCommand(s,c,_){let u=c.map(xb),y=_?xb(_):this.workspaceRoot,S={...process.env};if((s.split(/[\\/]/).pop()??s)==="gh")for(let O of["GITHUB_TOKEN","GH_TOKEN"]){let L=S[O];typeof L=="string"&&L.trim()===""&&delete S[O]}return new Promise(O=>{let L="",z="",Z=OZ.spawn(s,u,{cwd:y,shell:process.platform==="win32",env:S}),U=setTimeout(()=>{Z.kill("SIGTERM"),O({stdout:L.slice(0,NZ),stderr:z+`
1408
1408
  [process timed out]`,exitCode:124})},a1r),ye=process.stdout.isTTY===!0,ge=t((Te,dt)=>{if(!ye)return;(dt?process.stderr:process.stdout).write("\r\x1B[2K\x1B[2m"+Te+"\x1B[0m")},"writeLive");Z.stdout?.on("data",Te=>{let dt=Te.toString();L+=dt,ge(dt,!1),L.length>NZ&&Z.kill("SIGTERM")}),Z.stderr?.on("data",Te=>{let dt=Te.toString();z+=dt,ge(dt,!0)}),Z.on("close",Te=>{clearTimeout(U);let dt=L.slice(0,NZ);if(Te===0&&/Operation cancelled/i.test(dt)&&/(create-vite|create-react-app|create-next|create-svelte|create-astro|create-remix|@clack)/i.test(`${s} ${u.join(" ")} ${dt}`)){let St=[s,...u].join(" ");O({stdout:dt,stderr:`Interactive scaffolder detected \u2014 \`${s}\` aborted with "Operation cancelled" because Bandit captures stdout/stderr (no TTY on stdin) and modern scaffolders refuse to start without one. Tell the user to run this directly in their shell: \`!${St}\`. The \`!\`-prefix runs through their terminal with real stdin, so the scaffolder's prompts work. After they finish, you can pick up from the resulting filesystem state. Do NOT retry the same command \u2014 it will loop forever.`,exitCode:1});return}O({stdout:dt,stderr:z.slice(0,4*1024),exitCode:Te??0})}),Z.on("error",Te=>{if(clearTimeout(U),Te.code==="ENOENT"){O({stdout:"",stderr:`spawn ${s} ENOENT \u2014 '${s}' not found on PATH. Verify the tool is installed (\`which ${s}\` in a fresh terminal). If you use nvm/asdf/volta, your shim PATH may not be inherited; relaunching this CLI from the same terminal session that has \`${s}\` on PATH usually fixes it.`,exitCode:127});return}O({stdout:"",stderr:Te.message,exitCode:1})})})}async watchCommand(s,c,_,u){let y=c.map(xb),S=_?xb(_):this.workspaceRoot;return new Promise(P=>{let O="",L="",z=!1,Z=!1,U=OZ.spawn(s,y,{cwd:S,shell:process.platform==="win32",env:{...process.env}}),ye=t(gt=>{Z||(Z=!0,P({stdout:O.slice(0,NZ),stderr:L.slice(0,4*1024),exitCode:gt,endedEarly:z}))},"finish"),ge=setTimeout(()=>{try{U.kill("SIGTERM")}catch{}let gt=setTimeout(()=>{try{U.kill("SIGKILL")}catch{}ye(null)},1e3);U.once("close",St=>{clearTimeout(gt),ye(typeof St=="number"?St:null)})},u),Te=process.stdout.isTTY===!0,dt=t((gt,St)=>{if(!Te)return;(St?process.stderr:process.stdout).write("\r\x1B[2K\x1B[2m"+gt+"\x1B[0m")},"writeLive");U.stdout?.on("data",gt=>{let St=gt.toString();if(O+=St,dt(St,!1),O.length>NZ)try{U.kill("SIGTERM")}catch{}}),U.stderr?.on("data",gt=>{let St=gt.toString();L+=St,dt(St,!0)}),U.on("close",gt=>{Z||(clearTimeout(ge),z=!0,ye(typeof gt=="number"?gt:null))}),U.on("error",gt=>{Z||(clearTimeout(ge),z=!0,L+=gt.message,ye(1))})})}runRipgrep(s,c,_){return new Promise((u,y)=>{let S=["--color=never","--line-number","--max-count=25","--max-filesize=1M",...[...ROe].map(z=>["--glob",`!${z}`]).flat()];_&&S.push("--glob",_),S.push(s,c);let P="",O=OZ.spawn("rg",S,{shell:!1}),L=setTimeout(()=>{O.kill("SIGTERM"),u(P.slice(0,F9))},A_t);O.stdout?.on("data",z=>{P+=z.toString(),P.length>F9&&O.kill("SIGTERM")}),O.on("close",z=>{clearTimeout(L),z!=null&&z>=2&&P.length===0?y(new Error(`rg exited with code ${z}`)):u(P.slice(0,F9))}),O.on("error",y)})}runGrep(s,c,_){return new Promise((u,y)=>{let S=[...ROe].map(ge=>["--exclude-dir",ge]).flat(),P=_?s1r(_):{includes:[],subDir:""},O=P.includes.flatMap(ge=>["--include",ge]),L=P.subDir?`${c}/${P.subDir}`:c,z=["-rn","-E","--color=never",...S,...O,s,L],Z="",U=OZ.spawn("grep",z,{shell:!1}),ye=setTimeout(()=>{U.kill("SIGTERM"),u(Z.slice(0,F9))},A_t);U.stdout?.on("data",ge=>{Z+=ge.toString(),Z.length>F9&&U.kill("SIGTERM")}),U.on("close",ge=>{clearTimeout(ye),ge!=null&&ge>=2&&Z.length===0?y(new Error(`grep exited with code ${ge}`)):u(Z.slice(0,F9))}),U.on("error",y)})}};function o1r(a){let s=c1r(a);return c=>s.test(c.replace(/\\/g,"/"))}t(o1r,"compileGlob");function c1r(a){let s="^";for(let c=0;c<a.length;c++){let _=a[c];if(_==="*")a[c+1]==="*"?(s+=".*",c++,a[c+1]==="/"&&c++):s+="[^/]*";else if(_==="?")s+="[^/]";else if(_==="{"){let u=a.indexOf("}",c);if(u===-1){s+="\\{";continue}let y=a.slice(c+1,u).split(",").map(l1r).join("|");s+=`(?:${y})`,c=u}else/[.+^$()|\\]/.test(_)?s+="\\"+_:s+=_}return s+="$",new RegExp(s)}t(c1r,"globToRegex");function l1r(a){return a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}t(l1r,"escapeRegex");async function N_t(a,s,c,_){if(_.length>=MOe)return;let u;try{u=await H6.promises.readdir(a,{withFileTypes:!0})}catch{return}for(let y of u){if(_.length>=MOe)return;if(ROe.has(y.name))continue;let S=fT.join(a,y.name),P=fT.relative(s,S);y.isDirectory()?await N_t(S,s,c,_):y.isFile()&&c(P)&&_.push(S)}}t(N_t,"walk");var F_t=du(require("child_process")),xE=du(require("fs")),M_t=du(require("os")),jOe=du(require("path")),R_t=du(require("crypto"));function LOe(){let a=R_t.randomBytes(4).toString("hex");return jOe.join(M_t.tmpdir(),`bandit-paste-${Date.now()}-${a}.png`)}t(LOe,"freshTempPath");function Eue(a,s,c={}){try{let _=F_t.spawnSync(a,s,{...c,encoding:void 0});return{stdout:Buffer.isBuffer(_.stdout)?_.stdout:Buffer.from(_.stdout??""),code:_.status}}catch{return{stdout:Buffer.alloc(0),code:null}}}t(Eue,"tryExec");async function Due(){return process.platform==="darwin"?u1r():process.platform==="linux"?f1r():process.platform==="win32"?_1r():null}t(Due,"readClipboardImage");function u1r(){let a=LOe(),s=`set pngData to (the clipboard as \xABclass PNGf\xBB)
1409
1409
  set outFile to (open for access (POSIX file "${a}") with write permission)
1410
1410
  write pngData to outFile
@@ -1459,13 +1459,13 @@ ${a}`:u}t(qOe,"buildSystemPrompt");var jue=du(require("readline"));var SM=[{key:
1459
1459
  `),process.stdout.write(re.accent("\u2570\u2500\u2500 choice [1/2/3/4/5]: ")),!a)return process.stdout.write(`
1460
1460
  `),{choice:"deny"};let s=(await a()).trim();return s==="2"?{choice:"session"}:s==="3"?{choice:"always"}:s==="4"?{choice:"deny"}:s==="5"?(process.stdout.write(re.accent("\u2570\u2500\u2500 ")+re.red("deny + follow-up: ")),{choice:"deny",notes:(await a()).trim()||void 0}):{choice:"once"}}t(w1r,"promptLegacy");async function x1r(a){let{rl:s,readLine:c}=a;s?.pause();let _=process.stdin.isRaw===!0;process.stdin.setRawMode?.(!0),jue.emitKeypressEvents(process.stdin),process.stdin.resume();let u=0,y=!0,S=2,P=t(()=>{y||process.stdout.write("\r\x1B["+(S-1)+"A\x1B[0J"),y=!1;let O=SM.map((z,Z)=>Z===u?re.accent("\u25B8 ")+re.bold(z.label):" "+re.dim(z.label));process.stdout.write(re.accent("\u2502 ")+O.join(" ")+`
1461
1461
  `),process.stdout.write(re.accent("\u2570\u2500\u2500 ")+re.dim("\u2191\u2193\u2190\u2192 or 1-5 \xB7 enter to confirm \xB7 5 (or tab on deny) adds a follow-up note"))},"render");return P(),new Promise((O,L)=>{let z=t(()=>{process.stdin.removeListener("keypress",Z),process.stdin.setRawMode?.(_),process.stdout.write(`
1462
- `)},"cleanup"),Z=t((U,ye)=>{if(!ye)return;if(ye.ctrl&&ye.name==="c"){z(),s?.resume(),L(new Error("cancelled"));return}if(ye.name==="up"||ye.name==="left"){u=(u-1+SM.length)%SM.length,P();return}if(ye.name==="down"||ye.name==="right"){u=(u+1)%SM.length,P();return}if(ye.name&&/^[1-5]$/.test(ye.name)){u=parseInt(ye.name,10)-1,P();return}let ge=t(()=>{z(),process.stdout.write(re.accent("\u2570\u2500\u2500 ")+re.red("deny + follow-up: ")),s?.resume(),(c??(()=>new Promise(dt=>{let gt=jue.createInterface({input:process.stdin,output:process.stdout});gt.once("line",St=>{gt.close(),dt(St)})})))().then(dt=>{O({choice:"deny",notes:dt.trim()||void 0})}).catch(L)},"openFollowUp");if(ye.name==="return"||ye.name==="enter"){if(SM[u].promptForNotes){ge();return}z(),s?.resume(),O({choice:SM[u].key});return}if(ye.name==="tab"&&SM[u].key==="deny"){ge();return}},"onKey");process.stdin.on("keypress",Z)})}t(x1r,"promptInteractive");function rdt(a,s,c){if(a.choice!=="deny")return`permission ${a.choice}`;let _=c?`${s} ${c}`:s;return a.notes?`User denied \`${_}\` and asked you to revise your approach: "${a.notes}". Do not retry this tool call with the same arguments \u2014 adjust your plan based on the user's guidance.`:`User denied \`${_}\`. Do not retry this tool call with the same arguments.`}t(rdt,"formatDenialReason");LZ();var Xa=du(B9());var h3e=du(require("child_process")),mT=du(require("fs")),xM=du(require("path"));LZ();var Ydt=du(require("http")),Qdt=require("child_process");var bTr="https://auth.burtson.ai";function _3e(a){let s=a.apiKey?.trim();if(!s)return null;let c=(process.env.AUTH_API_URL||a.apiUrl||bTr).replace(/\/+$/,"");return{apiKey:s,authApiUrl:c}}t(_3e,"resolveCreds");async function d3e(a,s){if(a.length>50&&a.startsWith("ey"))return a;let c=await fetch(`${s}/api/keys/validate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:a,audience:"mcp-server",tokenLifetimeMinutes:10})});if(!c.ok){let u=await c.text().catch(()=>"");throw new Error(`AuthApi rejected the API key (HTTP ${c.status}${u?`: ${u}`:""})`)}let _=await c.json();if(!_.token)throw new Error("AuthApi /api/keys/validate did not return a token.");return _.token}t(d3e,"exchangeApiKeyForJwt");function STr(a){let s=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{return(0,Qdt.spawn)(s,[a],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}t(STr,"openBrowser");function TTr(){return new Promise((a,s)=>{let c=null,_=null,u=new Promise((S,P)=>{c=S,_=P}),y=Ydt.createServer((S,P)=>{let O=new URL(S.url??"/","http://127.0.0.1");if(O.pathname!=="/done"){P.writeHead(404,{"Content-Type":"text/plain"}),P.end("Not found");return}let L={};O.searchParams.forEach((z,Z)=>{L[Z]=z}),P.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),P.end(`<!doctype html><html><head><meta charset="utf-8"><title>Bandit \xB7 connected</title>
1462
+ `)},"cleanup"),Z=t((U,ye)=>{if(!ye)return;if(ye.ctrl&&ye.name==="c"){z(),s?.resume(),L(new Error("cancelled"));return}if(ye.name==="up"||ye.name==="left"){u=(u-1+SM.length)%SM.length,P();return}if(ye.name==="down"||ye.name==="right"){u=(u+1)%SM.length,P();return}if(ye.name&&/^[1-5]$/.test(ye.name)){u=parseInt(ye.name,10)-1,P();return}let ge=t(()=>{z(),process.stdout.write(re.accent("\u2570\u2500\u2500 ")+re.red("deny + follow-up: ")),s?.resume(),(c??(()=>new Promise(dt=>{let gt=jue.createInterface({input:process.stdin,output:process.stdout});gt.once("line",St=>{gt.close(),dt(St)})})))().then(dt=>{O({choice:"deny",notes:dt.trim()||void 0})}).catch(L)},"openFollowUp");if(ye.name==="return"||ye.name==="enter"){if(SM[u].promptForNotes){ge();return}z(),s?.resume(),O({choice:SM[u].key});return}if(ye.name==="tab"&&SM[u].key==="deny"){ge();return}},"onKey");process.stdin.on("keypress",Z)})}t(x1r,"promptInteractive");function rdt(a,s,c){if(a.choice!=="deny")return`permission ${a.choice}`;let _=c?`${s} ${c}`:s;return a.notes?`User denied \`${_}\` and asked you to revise your approach: "${a.notes}". Do not retry this tool call with the same arguments \u2014 adjust your plan based on the user's guidance.`:`User denied \`${_}\`. Do not retry this tool call with the same arguments.`}t(rdt,"formatDenialReason");LZ();var Xa=du(B9());var h3e=du(require("child_process")),mT=du(require("fs")),xM=du(require("path"));LZ();var Ydt=du(require("http")),Qdt=require("child_process");var bTr="https://auth.burtson.ai";function _3e(a){let s=a.apiKey?.trim();if(!s)return null;let c=(process.env.AUTH_API_URL||bTr).replace(/\/+$/,"");return{apiKey:s,authApiUrl:c}}t(_3e,"resolveCreds");async function d3e(a,s){if(a.length>50&&a.startsWith("ey"))return a;let c=await fetch(`${s}/api/keys/validate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:a})});if(!c.ok){let u=await c.text().catch(()=>"");throw new Error(`AuthApi rejected the API key (HTTP ${c.status}${u?`: ${u}`:""})`)}let _=await c.json();if(_.valid===!1)throw new Error(`AuthApi rejected the API key: ${_.reason??"invalid"}`);if(!_.gatewayToken)throw new Error("AuthApi /api/keys/validate did not return a gateway token (key may be valid but lack gateway access).");return _.gatewayToken}t(d3e,"exchangeApiKeyForJwt");function STr(a){let s=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{return(0,Qdt.spawn)(s,[a],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}t(STr,"openBrowser");function TTr(){return new Promise((a,s)=>{let c=null,_=null,u=new Promise((S,P)=>{c=S,_=P});u.catch(()=>{});let y=Ydt.createServer((S,P)=>{let O=new URL(S.url??"/","http://127.0.0.1");if(O.pathname!=="/done"){P.writeHead(404,{"Content-Type":"text/plain"}),P.end("Not found");return}let L={};O.searchParams.forEach((z,Z)=>{L[Z]=z}),P.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),P.end(`<!doctype html><html><head><meta charset="utf-8"><title>Bandit \xB7 connected</title>
1463
1463
  <style>body{font-family:system-ui,sans-serif;background:#0a0f18;color:#e8f1ff;margin:0;display:flex;align-items:center;justify-content:center;height:100vh}
1464
1464
  .card{background:#0d1b2e;border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:32px 40px;max-width:480px;text-align:center}
1465
1465
  h1{font-size:1.4rem;margin:0 0 8px;color:#4fc3f7}
1466
1466
  p{color:#94a6bc;margin:0;line-height:1.6}</style></head><body>
1467
1467
  <div class="card"><h1>${L.google==="connected"?"\u2713 Workspace connected":"Connection complete"}</h1>
1468
- <p>You can close this tab and return to your terminal.</p></div></body></html>`),c?.(L),setTimeout(()=>y.close(),250)});y.on("error",S=>{_?.(S),s(S)}),y.listen(0,"127.0.0.1",()=>{let S=y.address();if(typeof S=="object"&&S?.port){let P=setTimeout(()=>{_?.(new Error("Timed out waiting for browser callback after 5 minutes.")),y.close()},3e5);u.finally(()=>clearTimeout(P)),a({port:S.port,awaitCallback:t(()=>u,"awaitCallback")})}else s(new Error("Could not determine bound port"))})})}t(TTr,"startCallbackListener");async function ept(a,s){let c=_3e(a);if(!c)return re.red("No Burtson Labs API key configured. Run "+re.cyan("/connect")+" first (or set "+re.cyan("bandit.apiKey")+" in ~/.bandit/config.json).");let _;try{_=await d3e(c.apiKey,c.authApiUrl)}catch(L){return re.red(`Couldn't exchange API key for a session token: ${L instanceof Error?L.message:String(L)}`)}let u;try{u=await TTr()}catch(L){return re.red(`Couldn't bind a localhost listener for the OAuth callback: ${L instanceof Error?L.message:String(L)}`)}let y;try{let L=await fetch(`${c.authApiUrl}/api/me/google/connect/ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${_}`},body:JSON.stringify({workspace:s.workspace??"",scopes:s.scopes??"gmail,docs,drive,sheets,calendar",redirect:`http://127.0.0.1:${u.port}/done`})});if(!L.ok){let Z=await L.text().catch(()=>"");throw new Error(`HTTP ${L.status}${Z?`: ${Z}`:""}`)}y=(await L.json()).connectUrl}catch(L){return re.red(`Ticket mint failed: ${L instanceof Error?L.message:String(L)}`)}let S=STr(y),P=[re.bold("Connecting Google workspace via auth.burtson.ai\u2026"),"",S?re.dim(" \u21B3 A browser tab should have opened. Complete the Google consent flow there."):re.dim(" \u21B3 Could not auto-open a browser. Open this URL manually:")];S||P.push(re.cyan(" "+y)),P.push(re.dim(" (Will time out in 5 minutes if not completed.)")),process.stdout.write(P.join(`
1468
+ <p>You can close this tab and return to your terminal.</p></div></body></html>`),c?.(L),setTimeout(()=>y.close(),250)});y.on("error",S=>{_?.(S),s(S)}),y.listen(0,"127.0.0.1",()=>{let S=y.address();if(typeof S=="object"&&S?.port){let P=setTimeout(()=>{_?.(new Error("Timed out waiting for browser callback after 5 minutes.")),y.close()},3e5);u.finally(()=>clearTimeout(P)).catch(()=>{}),a({port:S.port,awaitCallback:t(()=>u,"awaitCallback")})}else s(new Error("Could not determine bound port"))})})}t(TTr,"startCallbackListener");async function ept(a,s){let c=_3e(a);if(!c)return re.red("No Burtson Labs API key configured. Run "+re.cyan("/connect")+" first (or set "+re.cyan("bandit.apiKey")+" in ~/.bandit/config.json).");let _;try{_=await d3e(c.apiKey,c.authApiUrl)}catch(L){return re.red(`Couldn't exchange API key for a session token: ${L instanceof Error?L.message:String(L)}`)}let u;try{u=await TTr()}catch(L){return re.red(`Couldn't bind a localhost listener for the OAuth callback: ${L instanceof Error?L.message:String(L)}`)}let y;try{let L=await fetch(`${c.authApiUrl}/api/me/google/connect/ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${_}`},body:JSON.stringify({workspace:s.workspace??"",scopes:s.scopes??"gmail,docs,drive,sheets,calendar",redirect:`http://127.0.0.1:${u.port}/done`})});if(!L.ok){let Z=await L.text().catch(()=>"");throw new Error(`HTTP ${L.status}${Z?`: ${Z}`:""}`)}y=(await L.json()).connectUrl}catch(L){return re.red(`Ticket mint failed: ${L instanceof Error?L.message:String(L)}`)}let S=STr(y),P=[re.bold("Connecting Google workspace via auth.burtson.ai\u2026"),"",S?re.dim(" \u21B3 A browser tab should have opened. Complete the Google consent flow there."):re.dim(" \u21B3 Could not auto-open a browser. Open this URL manually:")];S||P.push(re.cyan(" "+y)),P.push(re.dim(" (Will time out in 5 minutes if not completed.)")),process.stdout.write(P.join(`
1469
1469
  `)+`
1470
1470
  `);let O;try{O=await u.awaitCallback()}catch(L){return re.red(`OAuth callback never landed: ${L instanceof Error?L.message:String(L)}`)}if(O.google==="connected"){let L=O.workspace??"(auto-derived)";return re.green("\u2713 Connected workspace ")+re.cyan(L)+re.green(". Run ")+re.cyan("/mcp google list")+re.green(" to verify.")}return O.error?re.red(`Google connect returned an error: ${O.error}`):re.yellow("Connection callback returned without success or error params. Re-try the flow.")}t(ept,"connectGoogleViaCli");async function tpt(a){let s=_3e(a);if(!s)return re.red("No Burtson Labs API key configured. Run "+re.cyan("/connect")+" first.");let c;try{c=await d3e(s.apiKey,s.authApiUrl)}catch(y){return re.red(`Couldn't authenticate: ${y instanceof Error?y.message:String(y)}`)}let _;try{let y=await fetch(`${s.authApiUrl}/api/me/google/connections`,{headers:{Authorization:`Bearer ${c}`}});if(!y.ok){let P=await y.text().catch(()=>"");throw new Error(`HTTP ${y.status}${P?`: ${P}`:""}`)}_=(await y.json()).connections??[]}catch(y){return re.red(`Couldn't fetch connections: ${y instanceof Error?y.message:String(y)}`)}if(_.length===0)return[re.dim("No Google workspaces connected yet."),re.dim("Run ")+re.cyan("/mcp google connect")+re.dim(" to authorize one.")].join(`
1471
1471
  `);let u=[re.bold(`Google connections (${_.length}):`)];for(let y of _){let S=y.isPrimary?re.yellow(" \u2605 primary"):"",P=y.grantedScopes.map(O=>O.replace(/^https:\/\/www\.googleapis\.com\/auth\//,"")).map(O=>O.replace(/\.modify$|\.events$/,"")).join(", ");u.push(` ${re.cyan(y.googleEmail.padEnd(28))} ${re.dim("workspace=")}${re.bold(y.workspace)}${S}`),u.push(re.dim(` id: ${y.id} \xB7 scopes: ${P}`)),y.lastRefreshedAt&&u.push(re.dim(` last used: ${new Date(y.lastRefreshedAt).toLocaleString()}`))}return u.join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burtson-labs/bandit-stealth-cli",
3
- "version": "1.7.267",
3
+ "version": "1.7.269",
4
4
  "description": "Bandit — a local-first AI coding agent for your terminal. Same runtime as the Bandit Stealth VS Code / Cursor extension.",
5
5
  "keywords": [
6
6
  "ai",