pando-ai 0.3.1 → 0.3.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/dist/cli.js
CHANGED
|
@@ -1101,7 +1101,7 @@ data: ${JSON.stringify(e)}
|
|
|
1101
1101
|
`}var RF={protocol:"openai-responses",async sessionKey(n,e,t){let r=new Request("http://pando.local/v1/responses",{method:"POST",headers:n,body:JSON.stringify(e)});return await xF(r,e,t??void 0)},requestModel(n){return _F(n)},structuredAuthHeader(n){return n.get("authorization")??(process.env.OPENAI_API_KEY?`Bearer ${process.env.OPENAI_API_KEY}`:null)},async rewriteRequest(n,e,t){return(await IM(n,e,t)).body},async requestSources(n,e){return await lv(n,e)},async runUpstream(n){return await vF(n.proxyConfig,{authHeader:n.authHeader,body:n.body,logger:n.logger},n.memory,n.resolveArchivedSources,n.sessionKey)}};var kF=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade","host","content-length","content-encoding"]);function aJ(n,e,t,r=256){return new Promise((s,i)=>{let o=t,a=0,c=u=>{if(u.code==="EADDRINUSE"&&a<r){a+=1,o+=1,setImmediate(l);return}n.removeListener("error",c),i(u)},l=()=>{n.listen(o,e)};n.on("error",c),n.once("listening",()=>{n.removeListener("error",c),s(o)}),l()})}async function cJ(n){let e=[];for await(let t of n)e.push(typeof t=="string"?Buffer.from(t):t);return Buffer.concat(e)}function Hv(n){let e={};for(let[t,r]of Object.entries(n.headers))kF.has(t.toLowerCase())||r!==void 0&&(e[t]=Array.isArray(r)?r.join(", "):r);return e}async function Mg(n,e){let t={};if(n.headers.forEach((s,i)=>{kF.has(i.toLowerCase())||(t[i]=s)}),e.writeHead(n.status,t),!n.body){e.end();return}let r=n.body.getReader();try{for(;;){let{done:s,value:i}=await r.read();if(s)break;i&&e.write(Buffer.from(i))}}finally{e.end()}}async function Il(n,e=()=>{}){let t=xM(n.memoryEnabled,e),r=Os(),s=Eg(n.logFile);s.log("gateway_started",{host:n.host,memoryEnabled:n.memoryEnabled,policyDefaultAction:r.defaultAction,policyNativeTools:r.nativeTools,policyOtherMcpMode:r.otherMcp.mode}).catch(()=>{});let i=EF.createServer((u,d)=>{o(u,d).catch(p=>{e(`request failed: ${p instanceof Error?p.message:String(p)}`),d.headersSent||d.writeHead(502,{"content-type":"application/json"}),d.end(JSON.stringify({error:{message:"pando gateway upstream error"}}))})});async function o(u,d){let p=u.url||"/";if(p==="/health"||p==="/healthz"){d.writeHead(200,{"content-type":"application/json"}),d.end(JSON.stringify({status:"ok",memory:n.memoryEnabled}));return}let f=u.headers.authorization??null,m=u.headers["x-api-key"]??null,h=u.headers["x-pando-session"]??u.headers.session_id??m??f;if(p.startsWith("/v1/messages")){let g=Qi(n.anthropicUpstreamBaseUrl);await a(`${g}${p}`,u,d,"anthropic-messages",h);return}if(p.startsWith("/v1/responses")){let g=aM(n.openaiUpstreamBaseUrl,f),y=p.slice(3);await a(`${Qi(g)}${y}`,u,d,"openai-responses",h,Qi(g));return}d.writeHead(404,{"content-type":"application/json"}),d.end(JSON.stringify({error:{message:"pando gateway: unknown route"}}))}async function a(u,d,p,f,m,h){let g=crypto.randomUUID(),y={logger:s,sessionKey:m,requestId:g},b=d.method==="GET"||d.method==="HEAD"?void 0:await cJ(d),S=null;if(b&&b.length>0)try{let E=JSON.parse(b.toString("utf8"));E&&typeof E=="object"&&!Array.isArray(E)&&(S=E)}catch{S=null}if(s.log("request_received",{requestId:g,sessionKey:m,protocol:f,method:d.method,targetUrl:u,hasBody:!!(b&&b.length>0),memoryEnabled:n.memoryEnabled}).catch(()=>{}),S&&b&&b.length>0){let E=pM(f,S,r,y);if(E.response){e("request tool policy blocked provider-bound transcript item(s): "+E.blockedToolNames.join(", ")),await Mg(E.response,p);return}E.removedToolNames.length>0&&(S=E.body,b=Buffer.from(JSON.stringify(S),"utf8"),e(`request tool policy removed definition(s): ${E.removedToolNames.join(", ")}; forwarding sanitized request`))}if(n.memoryEnabled&&f==="openai-responses"&&h&&d.method==="POST"&&u.endsWith("/responses")&&S){let E=await Lv({adapter:RF,gatewayConfig:n,structuredUpstreamBaseUrl:h,targetUrl:u,headers:new Headers(Hv(d)),body:S,fallbackSessionKey:m,log:e}),x=await gg(E,f,S,r,y);await Mg(x,p);return}if(n.memoryEnabled&&f==="anthropic-messages"&&d.method==="POST"&&S&&u.includes("/v1/messages")){let E=await Lv({adapter:DM,gatewayConfig:n,structuredUpstreamBaseUrl:n.openaiUpstreamBaseUrl,targetUrl:u,headers:new Headers(Hv(d)),body:S,fallbackSessionKey:m,log:e}),x=await gg(E,f,S,r,y);await Mg(x,p);return}if(n.memoryEnabled&&b&&b.length>0&&S)try{let E=await t.rewriteRequestBody(S,{protocol:f,sessionKey:m,stateDir:n.stateDir});b=Buffer.from(JSON.stringify(E),"utf8"),s.log("memory_sieve_rewrote_request",{requestId:g,sessionKey:m,protocol:f}).catch(()=>{})}catch(E){let x=E instanceof Error?E.message:String(E);s.log("memory_sieve_rewrite_skipped",{requestId:g,sessionKey:m,protocol:f,reason:x}).catch(()=>{}),e(`memory rewrite skipped (${x}); forwarding original body`)}let _={method:d.method,headers:Hv(d),body:b&&b.length>0?b:void 0};b&&b.length>0&&(_.duplex="half");let R=await fetch(u,_),T=await gg(R,f,S,r,y);await Mg(T,p)}let c=await aJ(i,n.host,n.portStart),l=`http://${n.host}:${c}`;return e(`gateway listening on ${l} (memory=${n.memoryEnabled?"on":"off"})`),{host:n.host,port:c,openaiBaseUrl:`${l}/v1`,anthropicBaseUrl:l,close:()=>new Promise(u=>{i.close(()=>u())})}}var $g=require("child_process"),eD=q(require("fs")),tD=q(require("os")),gp=q(require("path"));var TF=q(require("fs")),hp=q(require("path"));function Wv(n=process.cwd()){let e=[".git",".hg",".pando-root","package.json","Cargo.toml","go.mod"],t=hp.default.resolve(n);for(;;){for(let s of e)if(lJ(t,s))return t;let r=hp.default.dirname(t);if(r===t)return hp.default.resolve(n);t=r}}function lJ(n,e){try{return TF.default.accessSync(hp.default.join(n,e)),!0}catch{return!1}}var Fg=q(require("fs")),NF=q(require("os")),zo=q(require("path"));var CF=q(require("fs")),Uv=q(require("path"));function Kv(n,e=[]){let t=uJ();return t&&!dJ(t)?{command:process.execPath,args:[t,n,...e]}:{command:"npx",args:["-y","pando-ai@latest",n,...e]}}function IF(n,e=[]){let t=Kv(n,e);return[PF(t.command),...t.args.map(PF)].join(" ")}function uJ(){let n=Uv.default.resolve(__dirname,"cli.js");try{return CF.default.accessSync(n),n}catch{return null}}function dJ(n){return/^(1|true|yes)$/i.test(process.env.PANDO_FORCE_NPX_SELF_INVOCATION||"")?!0:n.split(Uv.default.sep).join("/").includes("/.npm/_npx/")}function PF(n){return`'${n.replace(/'/g,"'\\''")}'`}var Dg="pando";function MF(n){return Kv("serve",[n])}function FF(n,e=null){let t=MF(n);return{mcpServers:{...pJ(n,e),[Dg]:{command:t.command,args:t.args}}}}function DF(n,e=null){return JSON.stringify(FF(n,e))}function OF(n,e=null){let t=FF(n,e),r=zo.default.join(Ds(),"mcp");Fg.default.mkdirSync(r,{recursive:!0});let s=zo.default.join(r,"claude-mcp-config.json");return Fg.default.writeFileSync(s,`${JSON.stringify(t,null,2)}
|
|
1102
1102
|
`),s}function pJ(n,e){let t=e===null?null:new Set(e.map(AF).filter(Boolean));if(t&&t.size===0)return{};let r={};for(let s of fJ(n)){let i;try{i=JSON.parse(Fg.default.readFileSync(s,"utf8"))}catch{continue}let o=mJ(i);for(let[a,c]of Object.entries(o))(t===null||t.has(AF(a)))&&!(a in r)&&(r[a]=c)}return r}function fJ(n){let e=process.env.PANDO_CLAUDE_MCP_CONFIG?[process.env.PANDO_CLAUDE_MCP_CONFIG]:[],t=NF.default.homedir();return[...e,zo.default.join(n,".mcp.json"),zo.default.join(process.cwd(),".mcp.json"),zo.default.join(t,".mcp.json"),zo.default.join(t,".claude","mcp.json"),zo.default.join(t,".config","claude","mcp.json")]}function mJ(n){if(!n||typeof n!="object"||Array.isArray(n))return{};let e=n,t=e.mcpServers??e.mcp_servers;return!t||typeof t!="object"||Array.isArray(t)?{}:t}function AF(n){return n.replace(/^mcp__/,"").replace(/__.*$/,"").trim()}function $F(n){let e=MF(n),t=`mcp_servers.${Dg}`,r=JSON.stringify(e.args);return["-c",`${t}.command=${JSON.stringify(e.command)}`,"-c",`${t}.args=${r}`,"-c",`${t}.required=true`,"-c",`${t}.enabled=true`,"-c",`${t}.default_tools_approval_mode="approve"`]}var Zv=q(require("fs")),Gv=q(require("path"));function jF(){return JSON.stringify(zF())}function LF(){let n=Gv.default.join(Ds(),"claude");Zv.default.mkdirSync(n,{recursive:!0});let e=Gv.default.join(n,"hook-settings.json");return Zv.default.writeFileSync(e,`${JSON.stringify(zF(),null,2)}
|
|
1103
1103
|
`),e}function zF(){let n=hJ();return{hooks:{PreToolUse:[{hooks:[n]}],PostToolUse:[{hooks:[n]}],PostToolUseFailure:[{hooks:[n]}],PostToolBatch:[{hooks:[n]}]}}}function hJ(){return{type:"command",command:IF("claude-hook")}}var Vv=q(require("fs")),HF=q(require("os")),WF=q(require("path")),Jv="pando";function UF(n,e){let t=gJ(),r=bJ(t);if(!r)return null;let s=!1,i=[],o=!1;SJ(r)&&(s=!0,o=!0);let a=Og(r.projects),c=a?Og(a[e]):null;if(c){let l=qF(c.enabledMcpServers),u=l.filter(d=>xJ(n,d));u.length>0&&(c.enabledMcpServers=l.filter(d=>!u.includes(d)),c.disabledMcpServers=_J([...qF(c.disabledMcpServers),...u]),i.push(...u),s=!0)}return s?(Vv.default.writeFileSync(t,`${JSON.stringify(r,null,2)}
|
|
1104
|
-
`),{file:t,disabledServers:i,removedStalePandoServer:o}):null}function gJ(){return WF.default.join(yJ(),".claude.json")}function yJ(){return process.env.PANDO_CLAUDE_HOME_OVERRIDE||process.env.PANDO_HOME_OVERRIDE||process.env.HOME||HF.default.homedir()}function bJ(n){try{let e=JSON.parse(Vv.default.readFileSync(n,"utf8"));return Og(e)}catch{return null}}function SJ(n){let e=Og(n.mcpServers);return!e||!(Jv in e)?!1:(delete e[Jv],!0)}function xJ(n,e){let t=BF(e);if(!t||t===Jv)return!1;let r=n.otherMcp.servers.map(BF);switch(n.otherMcp.mode){case"deny_all":return!0;case"allow_list":return!r.includes(t);case"deny_list":return r.includes(t);default:return!1}}function BF(n){return n.replace(/^mcp__/,"").replace(/__.*$/,"").trim()}function Og(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:null}function qF(n){return Array.isArray(n)?n.filter(e=>typeof e=="string"):[]}function _J(n){return[...new Set(n)]}function KF(n,e={}){let t=[],r={ENABLE_CLAUDEAI_MCP_SERVERS:"false"},s=[];n.nativeTools==="deny"&&(t.push("--tools",""),s.push('native tools disabled (--tools ""); MCP remains available')),s.push("Claude.ai MCP/connectors disabled (ENABLE_CLAUDEAI_MCP_SERVERS=false)");let i=e.supportedFlags;i?.disableSlashCommands&&s.push("Claude slash commands/skills left enabled (not disabled)"),i?.bare&&(t.push("--bare"),s.push("Claude bare mode enabled to skip auto-discovered customizations")),i?.noChrome&&(t.push("--no-chrome"),s.push("Claude Chrome/browser tool surface disabled"));let o=Wv(),a=UF(n,o),c=wJ(n);switch(OF(o,c),t.push("--mcp-config",DF(o,c),"--strict-mcp-config"),s.push(`Pando MCP dynamically injected via --mcp-config/--strict-mcp-config, root-scoped to ${o}`),a&&(a.disabledServers.length>0&&s.push(`Claude project MCP state updated: disabled off-policy built-in/project servers (${a.disabledServers.join(", ")})`),a.removedStalePandoServer&&s.push(`removed stale Pando MCP entry from ${a.file}`)),(e.hooksEnabled??!0)&&(LF(),t.push("--settings",jF()),s.push("Claude hooks installed for tool call/result enforcement")),n.otherMcp.mode){case"deny_all":s.push("other MCP servers blocked (strict: Pando only)");break;case"allow_list":n.otherMcp.tools.length>0&&t.push(`--allowedTools=${n.otherMcp.tools.join(",")}`),s.push(`other MCP restricted to allow-list (${n.otherMcp.servers.join(", ")||"none"}); strict config in effect`);break;case"deny_list":if(n.otherMcp.tools.length>0||n.otherMcp.servers.length>0){let l=[...n.otherMcp.tools,...n.otherMcp.servers.map(u=>`mcp__${u}`)];t.push(`--disallowedTools=${l.join(",")}`),s.push(`other MCP deny-list removed from Claude context (${l.join(", ")})`)}break;default:s.push("other MCP servers preserved in strict generated config");break}return{extraArgs:t,env:r,notes:s}}function wJ(n){switch(n.otherMcp.mode){case"deny_all":return[];case"allow_list":return n.otherMcp.servers;default:return null}}function ZF(n){let e=[],t={},r=[];n.nativeTools==="deny"&&(e.push("-c",'sandbox_mode="read-only"'),e.push("-c",'web_search="disabled"'),r.push("native tools restricted (read-only sandbox, web search disabled, best-effort); tool traffic is enforced at the gateway"));let s=Wv();switch(e.push(...$F(s)),r.push(`Pando MCP (${Dg}) dynamically injected via Codex -c overrides, root-scoped to ${s}; user MCP files are not modified`),n.otherMcp.mode){case"deny_all":case"allow_list":case"deny_list":r.push(`other MCP policy "${n.otherMcp.mode}" is best-effort for Codex (no strict-MCP flag); enforced at the gateway, not via config`);break;default:r.push("other MCP servers allowed");break}return{extraArgs:e,env:t,notes:r}}var GF=new Set(["-c","--config","--enable","--disable","--remote","--remote-auth-token-env","-i","--image","-m","--model","--local-provider","-p","--profile","-s","--sandbox","-a","--ask-for-approval","-C","--cd","--add-dir"]);function JF(n){for(let e=0;e<n.length;e+=1){let t=n[e];if(t==="--")return null;if(!vJ(t)){if(GF.has(t)){e+=1;continue}if(!t.startsWith("-"))return{name:t,index:e}}}return null}function vJ(n){let[e,t]=n.split("=",2);return t!==void 0&&GF.has(e)}var VF="pando-proxy";function RJ(n){let e=`model_providers.${VF}`;return["-c",`model_provider="${VF}"`,"-c",`model_auto_compact_token_limit=${n.codexAutoCompactTokenLimit}`,"-c",`${e}.name="Pando Firewall"`,"-c",`${e}.base_url="http://${n.host}:${n.port}/v1"`,"-c",`${e}.wire_api="responses"`,"-c",`${e}.transport="responses_http"`,"-c",`${e}.requires_openai_auth=true`]}function XF(n,e){let t=RJ(e),r=JF(n);return r?[...n.slice(0,r.index+1),...t,...n.slice(r.index+1)]:[...t,...n]}var EJ=28e4;function Mr(n){console.error(`[pando] ${n}`)}async function yp(n,e){let t=z0(n);if(!t)return Mr(`could not find the real \`${n}\` binary. Is it installed and on PATH (outside ~/.pando/bin)?`),127;let r=Os();if(n==="claude"){let m=CJ();if(m)return Mr(`refusing to launch claude: Claude hooks are disabled by ${m}. Pando requires hooks for subscription-mode tool/result enforcement.`),78}let s=vl(),i=r.proxy[n]==="enforce",o=n==="claude"?kJ():!1,a=i&&(n==="codex"||o),c=/^(1|true|yes)$/i.test(process.env.PANDO_ALLOW_UNSUPERVISED||""),l=null;if(a)try{l=await Il(s,m=>Mr(`gateway: ${m}`))}catch(m){let h=m instanceof Error?m.message:String(m);if(!c)return Mr(`refusing to launch ${n}: the Pando gateway could not start (${h}). Fix the problem, or set PANDO_ALLOW_UNSUPERVISED=1 to run ${n} without wire interception (NOT recommended \u2014 the firewall will be off).`),78;Mr(`gateway failed to start (${h}); PANDO_ALLOW_UNSUPERVISED is set, so launching ${n} WITHOUT wire interception.`)}let u=n==="claude"&&!l;i?u&&!o?Mr("Claude gateway auth not configured; using Claude hooks-only fallback for tool call/result firewalling."):u&&Mr("Claude gateway unavailable; using Claude hooks-only fallback for tool call/result firewalling."):Mr(`${n} provider proxy disabled by policy; ${n==="claude"?"using Claude hooks only":"Codex provider-bound gateway enforcement is off"}.`);let{childArgs:d,childEnv:p}=jJ(n,e,r,l,t);/^(1|true|yes)$/i.test(process.env.PANDO_DEBUG_LAUNCH_ARGS||"")&&(Mr(`debug launch args: ${JSON.stringify(d)}`),Mr(`debug launch env: ANTHROPIC_BASE_URL=${p.ANTHROPIC_BASE_URL?"set":"unset"} PANDO_ALLOW_UNSUPERVISED=${p.PANDO_ALLOW_UNSUPERVISED?"set":"unset"} PANDO_GATEWAY_MEMORY=${p.PANDO_GATEWAY_MEMORY?"set":"unset"}`)),Mr(`supervising ${n}: gateway=${l?"on":"off"} ${n==="claude"?"hooks=on ":""}pando_mcp=on policy native_tools=${r.nativeTools} other_mcp=${r.otherMcp.mode}`),await BJ(n);let f=await qJ(t,d,p);return l&&await l.close().catch(()=>{}),f}function kJ(){return!!(process.env.ANTHROPIC_API_KEY||process.env.ANTHROPIC_AUTH_TOKEN||TJ())}function TJ(){for(let n of nD()){let e=rD(n);if(!e)continue;if(typeof e.apiKeyHelper=="string"&&e.apiKeyHelper.trim())return!0;let t=e.env;if(t&&typeof t=="object"&&!Array.isArray(t)&&(QF(t.ANTHROPIC_API_KEY)||QF(t.ANTHROPIC_AUTH_TOKEN)))return!0}return!1}function nD(){let n=PJ(),e=process.env.CLAUDE_CONFIG_DIR||gp.default.join(n,".claude"),t=process.cwd();return[...process.platform==="darwin"?[...process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[],"/Library/Application Support/ClaudeCode/managed-settings.json"]:process.platform==="win32"?process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[]:[...process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[],"/etc/claude-code/managed-settings.json"],gp.default.join(t,".claude","settings.json"),gp.default.join(t,".claude","settings.local.json"),gp.default.join(e,"settings.json")]}function PJ(){return process.env.PANDO_CLAUDE_HOME_OVERRIDE||process.env.PANDO_HOME_OVERRIDE||process.env.HOME||tD.default.homedir()}function CJ(){for(let n of nD()){let e=rD(n);if(e&&IJ(e.disableAllHooks))return n}return null}function IJ(n){return typeof n=="boolean"?n:typeof n!="string"?!1:/^(1|true|yes|on)$/i.test(n.trim())}function rD(n){try{let e=JSON.parse(eD.default.readFileSync(n,"utf8"));return e&&typeof e=="object"&&!Array.isArray(e)?e:null}catch{return null}}function QF(n){return typeof n=="string"&&n.trim().length>0}var AJ=new Set(["--add-dir","--agents","--agent","--allowed-tools","--allowedTools","--append-system-prompt","--betas","--disallowed-tools","--disallowedTools","--fallback-model","--input-format","--json-schema","--mcp-config","--model","--output-format","--permission-mode","--plugin-dir","--session-id","--setting-sources","--settings","--system-prompt","--tools"]),NJ=new Set(["--add-dir","--agent","--agents","--allowed-tools","--allowedTools","--disallowed-tools","--disallowedTools","--mcp-config","--permission-mode","--plugin-dir","--setting-sources","--settings","--tools"]),MJ=new Set(["--allow-dangerously-skip-permissions","--dangerously-load-development-channels","--dangerously-skip-permissions","--ide"]);function FJ(n,e){let t=DJ(n),r=$J(t.args);return r===null?{args:[...t.args,...e],removed:t.removed}:{args:[...t.args.slice(0,r),...e,...t.args.slice(r)],removed:t.removed}}function DJ(n){let e=[],t=[];for(let r=0;r<n.length;r+=1){let s=n[r];if(!s.startsWith("--")){e.push(s);continue}let[i,o]=OJ(s);if(MJ.has(i)){t.push(i);continue}if(NJ.has(i)){if(t.push(i),o!==void 0)continue;for(;r+1<n.length&&!n[r+1].startsWith("-");)r+=1;continue}e.push(s)}return{args:e,removed:t}}function OJ(n){let e=n.indexOf("=");return e===-1?[n,void 0]:[n.slice(0,e),n.slice(e+1)]}function $J(n){for(let e=0;e<n.length;e+=1){let t=n[e];if(t==="--")return e;if(t.startsWith("--")){let[r,s]=t.split("=",2);s===void 0&&AJ.has(r)&&(e+=1);continue}if(!t.startsWith("-"))return e}return null}function jJ(n,e,t,r,s){let i={...process.env};zJ(i);let o,a;if(n==="codex")a=ZF(t),r?o=XF(e,{host:r.host,port:r.port,codexAutoCompactTokenLimit:EJ}):o=[...e],o=[...o,...a.extraArgs];else{a=KF(t,{supportedFlags:LJ(s)});let c=FJ(e,a.extraArgs);o=c.args,c.removed.length>0&&Mr(`policy: stripped Claude flags that can bypass Pando enforcement (${[...new Set(c.removed)].join(", ")})`),r&&(i.ANTHROPIC_BASE_URL=r.anthropicBaseUrl)}for(let[c,l]of Object.entries(a.env))i[c]=l;for(let c of a.notes)Mr(`policy: ${c}`);return{childArgs:o,childEnv:i}}var YF=new Map;function LJ(n){let e=YF.get(n);if(e)return e;let t=(0,$g.spawnSync)(n,["--help"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}),r=`${t.stdout||""}
|
|
1104
|
+
`),{file:t,disabledServers:i,removedStalePandoServer:o}):null}function gJ(){return WF.default.join(yJ(),".claude.json")}function yJ(){return process.env.PANDO_CLAUDE_HOME_OVERRIDE||process.env.PANDO_HOME_OVERRIDE||process.env.HOME||HF.default.homedir()}function bJ(n){try{let e=JSON.parse(Vv.default.readFileSync(n,"utf8"));return Og(e)}catch{return null}}function SJ(n){let e=Og(n.mcpServers);return!e||!(Jv in e)?!1:(delete e[Jv],!0)}function xJ(n,e){let t=BF(e);if(!t||t===Jv)return!1;let r=n.otherMcp.servers.map(BF);switch(n.otherMcp.mode){case"deny_all":return!0;case"allow_list":return!r.includes(t);case"deny_list":return r.includes(t);default:return!1}}function BF(n){return n.replace(/^mcp__/,"").replace(/__.*$/,"").trim()}function Og(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:null}function qF(n){return Array.isArray(n)?n.filter(e=>typeof e=="string"):[]}function _J(n){return[...new Set(n)]}function KF(n,e={}){let t=[],r={ENABLE_CLAUDEAI_MCP_SERVERS:"false"},s=[];n.nativeTools==="deny"&&(t.push("--tools",""),s.push('native tools disabled (--tools ""); MCP remains available')),s.push("Claude.ai MCP/connectors disabled (ENABLE_CLAUDEAI_MCP_SERVERS=false)");let i=e.supportedFlags;i?.disableSlashCommands&&s.push("Claude slash commands/skills left enabled (not disabled)"),i?.bare&&s.push("Claude bare mode left OFF (preserves subscription/OAuth auth and hooks)"),i?.noChrome&&(t.push("--no-chrome"),s.push("Claude Chrome/browser tool surface disabled"));let o=Wv(),a=UF(n,o),c=wJ(n);switch(OF(o,c),t.push("--mcp-config",DF(o,c),"--strict-mcp-config"),s.push(`Pando MCP dynamically injected via --mcp-config/--strict-mcp-config, root-scoped to ${o}`),a&&(a.disabledServers.length>0&&s.push(`Claude project MCP state updated: disabled off-policy built-in/project servers (${a.disabledServers.join(", ")})`),a.removedStalePandoServer&&s.push(`removed stale Pando MCP entry from ${a.file}`)),(e.hooksEnabled??!0)&&(LF(),t.push("--settings",jF()),s.push("Claude hooks installed for tool call/result enforcement")),n.otherMcp.mode){case"deny_all":s.push("other MCP servers blocked (strict: Pando only)");break;case"allow_list":n.otherMcp.tools.length>0&&t.push(`--allowedTools=${n.otherMcp.tools.join(",")}`),s.push(`other MCP restricted to allow-list (${n.otherMcp.servers.join(", ")||"none"}); strict config in effect`);break;case"deny_list":if(n.otherMcp.tools.length>0||n.otherMcp.servers.length>0){let l=[...n.otherMcp.tools,...n.otherMcp.servers.map(u=>`mcp__${u}`)];t.push(`--disallowedTools=${l.join(",")}`),s.push(`other MCP deny-list removed from Claude context (${l.join(", ")})`)}break;default:s.push("other MCP servers preserved in strict generated config");break}return{extraArgs:t,env:r,notes:s}}function wJ(n){switch(n.otherMcp.mode){case"deny_all":return[];case"allow_list":return n.otherMcp.servers;default:return null}}function ZF(n){let e=[],t={},r=[];n.nativeTools==="deny"&&(e.push("-c",'sandbox_mode="read-only"'),e.push("-c",'web_search="disabled"'),r.push("native tools restricted (read-only sandbox, web search disabled, best-effort); tool traffic is enforced at the gateway"));let s=Wv();switch(e.push(...$F(s)),r.push(`Pando MCP (${Dg}) dynamically injected via Codex -c overrides, root-scoped to ${s}; user MCP files are not modified`),n.otherMcp.mode){case"deny_all":case"allow_list":case"deny_list":r.push(`other MCP policy "${n.otherMcp.mode}" is best-effort for Codex (no strict-MCP flag); enforced at the gateway, not via config`);break;default:r.push("other MCP servers allowed");break}return{extraArgs:e,env:t,notes:r}}var GF=new Set(["-c","--config","--enable","--disable","--remote","--remote-auth-token-env","-i","--image","-m","--model","--local-provider","-p","--profile","-s","--sandbox","-a","--ask-for-approval","-C","--cd","--add-dir"]);function JF(n){for(let e=0;e<n.length;e+=1){let t=n[e];if(t==="--")return null;if(!vJ(t)){if(GF.has(t)){e+=1;continue}if(!t.startsWith("-"))return{name:t,index:e}}}return null}function vJ(n){let[e,t]=n.split("=",2);return t!==void 0&&GF.has(e)}var VF="pando-proxy";function RJ(n){let e=`model_providers.${VF}`;return["-c",`model_provider="${VF}"`,"-c",`model_auto_compact_token_limit=${n.codexAutoCompactTokenLimit}`,"-c",`${e}.name="Pando Firewall"`,"-c",`${e}.base_url="http://${n.host}:${n.port}/v1"`,"-c",`${e}.wire_api="responses"`,"-c",`${e}.transport="responses_http"`,"-c",`${e}.requires_openai_auth=true`]}function XF(n,e){let t=RJ(e),r=JF(n);return r?[...n.slice(0,r.index+1),...t,...n.slice(r.index+1)]:[...t,...n]}var EJ=28e4;function Mr(n){console.error(`[pando] ${n}`)}async function yp(n,e){let t=z0(n);if(!t)return Mr(`could not find the real \`${n}\` binary. Is it installed and on PATH (outside ~/.pando/bin)?`),127;let r=Os();if(n==="claude"){let m=CJ();if(m)return Mr(`refusing to launch claude: Claude hooks are disabled by ${m}. Pando requires hooks for subscription-mode tool/result enforcement.`),78}let s=vl(),i=r.proxy[n]==="enforce",o=n==="claude"?kJ():!1,a=i&&(n==="codex"||o),c=/^(1|true|yes)$/i.test(process.env.PANDO_ALLOW_UNSUPERVISED||""),l=null;if(a)try{l=await Il(s,m=>Mr(`gateway: ${m}`))}catch(m){let h=m instanceof Error?m.message:String(m);if(!c)return Mr(`refusing to launch ${n}: the Pando gateway could not start (${h}). Fix the problem, or set PANDO_ALLOW_UNSUPERVISED=1 to run ${n} without wire interception (NOT recommended \u2014 the firewall will be off).`),78;Mr(`gateway failed to start (${h}); PANDO_ALLOW_UNSUPERVISED is set, so launching ${n} WITHOUT wire interception.`)}let u=n==="claude"&&!l;i?u&&!o?Mr("Claude gateway auth not configured; using Claude hooks-only fallback for tool call/result firewalling."):u&&Mr("Claude gateway unavailable; using Claude hooks-only fallback for tool call/result firewalling."):Mr(`${n} provider proxy disabled by policy; ${n==="claude"?"using Claude hooks only":"Codex provider-bound gateway enforcement is off"}.`);let{childArgs:d,childEnv:p}=jJ(n,e,r,l,t);/^(1|true|yes)$/i.test(process.env.PANDO_DEBUG_LAUNCH_ARGS||"")&&(Mr(`debug launch args: ${JSON.stringify(d)}`),Mr(`debug launch env: ANTHROPIC_BASE_URL=${p.ANTHROPIC_BASE_URL?"set":"unset"} PANDO_ALLOW_UNSUPERVISED=${p.PANDO_ALLOW_UNSUPERVISED?"set":"unset"} PANDO_GATEWAY_MEMORY=${p.PANDO_GATEWAY_MEMORY?"set":"unset"}`)),Mr(`supervising ${n}: gateway=${l?"on":"off"} ${n==="claude"?"hooks=on ":""}pando_mcp=on policy native_tools=${r.nativeTools} other_mcp=${r.otherMcp.mode}`),await BJ(n);let f=await qJ(t,d,p);return l&&await l.close().catch(()=>{}),f}function kJ(){return!!(process.env.ANTHROPIC_API_KEY||process.env.ANTHROPIC_AUTH_TOKEN||TJ())}function TJ(){for(let n of nD()){let e=rD(n);if(!e)continue;if(typeof e.apiKeyHelper=="string"&&e.apiKeyHelper.trim())return!0;let t=e.env;if(t&&typeof t=="object"&&!Array.isArray(t)&&(QF(t.ANTHROPIC_API_KEY)||QF(t.ANTHROPIC_AUTH_TOKEN)))return!0}return!1}function nD(){let n=PJ(),e=process.env.CLAUDE_CONFIG_DIR||gp.default.join(n,".claude"),t=process.cwd();return[...process.platform==="darwin"?[...process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[],"/Library/Application Support/ClaudeCode/managed-settings.json"]:process.platform==="win32"?process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[]:[...process.env.PANDO_CLAUDE_MANAGED_SETTINGS?[process.env.PANDO_CLAUDE_MANAGED_SETTINGS]:[],"/etc/claude-code/managed-settings.json"],gp.default.join(t,".claude","settings.json"),gp.default.join(t,".claude","settings.local.json"),gp.default.join(e,"settings.json")]}function PJ(){return process.env.PANDO_CLAUDE_HOME_OVERRIDE||process.env.PANDO_HOME_OVERRIDE||process.env.HOME||tD.default.homedir()}function CJ(){for(let n of nD()){let e=rD(n);if(e&&IJ(e.disableAllHooks))return n}return null}function IJ(n){return typeof n=="boolean"?n:typeof n!="string"?!1:/^(1|true|yes|on)$/i.test(n.trim())}function rD(n){try{let e=JSON.parse(eD.default.readFileSync(n,"utf8"));return e&&typeof e=="object"&&!Array.isArray(e)?e:null}catch{return null}}function QF(n){return typeof n=="string"&&n.trim().length>0}var AJ=new Set(["--add-dir","--agents","--agent","--allowed-tools","--allowedTools","--append-system-prompt","--betas","--disallowed-tools","--disallowedTools","--fallback-model","--input-format","--json-schema","--mcp-config","--model","--output-format","--permission-mode","--plugin-dir","--session-id","--setting-sources","--settings","--system-prompt","--tools"]),NJ=new Set(["--add-dir","--agent","--agents","--allowed-tools","--allowedTools","--disallowed-tools","--disallowedTools","--mcp-config","--permission-mode","--plugin-dir","--setting-sources","--settings","--tools"]),MJ=new Set(["--allow-dangerously-skip-permissions","--dangerously-load-development-channels","--dangerously-skip-permissions","--ide"]);function FJ(n,e){let t=DJ(n),r=$J(t.args);return r===null?{args:[...t.args,...e],removed:t.removed}:{args:[...t.args.slice(0,r),...e,...t.args.slice(r)],removed:t.removed}}function DJ(n){let e=[],t=[];for(let r=0;r<n.length;r+=1){let s=n[r];if(!s.startsWith("--")){e.push(s);continue}let[i,o]=OJ(s);if(MJ.has(i)){t.push(i);continue}if(NJ.has(i)){if(t.push(i),o!==void 0)continue;for(;r+1<n.length&&!n[r+1].startsWith("-");)r+=1;continue}e.push(s)}return{args:e,removed:t}}function OJ(n){let e=n.indexOf("=");return e===-1?[n,void 0]:[n.slice(0,e),n.slice(e+1)]}function $J(n){for(let e=0;e<n.length;e+=1){let t=n[e];if(t==="--")return e;if(t.startsWith("--")){let[r,s]=t.split("=",2);s===void 0&&AJ.has(r)&&(e+=1);continue}if(!t.startsWith("-"))return e}return null}function jJ(n,e,t,r,s){let i={...process.env};zJ(i);let o,a;if(n==="codex")a=ZF(t),r?o=XF(e,{host:r.host,port:r.port,codexAutoCompactTokenLimit:EJ}):o=[...e],o=[...o,...a.extraArgs];else{a=KF(t,{supportedFlags:LJ(s)});let c=FJ(e,a.extraArgs);o=c.args,c.removed.length>0&&Mr(`policy: stripped Claude flags that can bypass Pando enforcement (${[...new Set(c.removed)].join(", ")})`),r&&(i.ANTHROPIC_BASE_URL=r.anthropicBaseUrl)}for(let[c,l]of Object.entries(a.env))i[c]=l;for(let c of a.notes)Mr(`policy: ${c}`);return{childArgs:o,childEnv:i}}var YF=new Map;function LJ(n){let e=YF.get(n);if(e)return e;let t=(0,$g.spawnSync)(n,["--help"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}),r=`${t.stdout||""}
|
|
1105
1105
|
${t.stderr||""}`,s={bare:r.includes("--bare"),noChrome:r.includes("--no-chrome"),disableSlashCommands:r.includes("--disable-slash-commands")};return YF.set(n,s),s}function zJ(n){for(let e of["PANDO_ALLOW_UNSUPERVISED","PANDO_DEBUG","PANDO_DEBUG_LAUNCH_ARGS","PANDO_GATEWAY_ANTHROPIC_BASE_URL","PANDO_GATEWAY_HOST","PANDO_GATEWAY_LOG_FILE","PANDO_GATEWAY_MEMORY","PANDO_GATEWAY_OPENAI_BASE_URL","PANDO_GATEWAY_PORT","PANDO_GATEWAY_STATE_DIR"])delete n[e]}async function BJ(n){for(let e of[3,2,1])Mr(`starting ${n} in ${e}...`),await new Promise(t=>setTimeout(t,1e3))}function qJ(n,e,t){return new Promise(r=>{let s=(0,$g.spawn)(n,e,{stdio:"inherit",env:t}),i=c=>{try{s.kill(c)}catch{}},o=()=>i("SIGINT"),a=()=>i("SIGTERM");process.on("SIGINT",o),process.on("SIGTERM",a),s.on("error",c=>{Mr(`failed to launch: ${c instanceof Error?c.message:String(c)}`),process.off("SIGINT",o),process.off("SIGTERM",a),r(127)}),s.on("exit",(c,l)=>{if(process.off("SIGINT",o),process.off("SIGTERM",a),l){r(128+({SIGINT:2,SIGTERM:15}[l]??0));return}r(c??0)})})}var Nl=q(require("fs")),Al=q(require("path")),Xv=require("child_process");var HJ=["codex","claude"];function $s(n){return n?"\u2713":"\u2717"}function WJ(){try{Nl.default.readdirSync(Zn()).length===0&&Nl.default.rmdirSync(Zn())}catch{}}function sD(n){try{return Nl.default.realpathSync.native(n)}catch{return Al.default.resolve(n)}}function UJ(n){let e=Al.default.resolve(n);for(;;){let t=Al.default.join(e,"package.json");try{if(JSON.parse(Nl.default.readFileSync(t,"utf8"))?.name===kn)return e}catch{}let r=Al.default.dirname(e);if(r===e)return null;e=r}}function iD(){let n=process.env.npm_execpath;return n&&Nl.default.existsSync(n)?{command:process.execPath,args:[n]}:{command:"npm",args:[]}}function KJ(){let n=iD(),e=(0,Xv.spawnSync)(n.command,[...n.args,"root","-g"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(e.status!==0)return null;let t=e.stdout.trim();return t||null}function ZJ(){let n=UJ(__dirname),e=KJ();if(!n||!e)return{status:"not_global"};let t=sD(Al.default.join(e,kn)),r=sD(n);if(r!==t)return{status:"not_global"};let s=iD(),i=(0,Xv.spawnSync)(s.command,[...s.args,"uninstall","-g",kn],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});return i.status===0?{status:"removed_global",packageRoot:r}:{status:"failed",message:(i.stderr||i.stdout||`exit ${i.status}`).trim()}}function jg(){console.log(""),console.log(" Pando uninstall"),console.log("");for(let s of HJ){let i=G0(s);switch(i.status){case"removed":console.log(` ${$s(!0)} ${s}: removed shim at ${i.shimPath}`);break;case"not_present":console.log(` ${s}: no Pando shim found`);break;case"failed":console.log(` ${$s(!1)} ${s}: could not remove shim \u2014 ${i.message}`);break}}let n=U0();switch(n.status){case"removed":console.log(` ${$s(!0)} pando-ai: removed command shim at ${n.shimPath}`);break;case"not_present":console.log(" pando-ai: no Pando command shim found");break;case"failed":console.log(` ${$s(!1)} pando-ai: could not remove command shim \u2014 ${n.message}`);break}for(let s of K0())switch(s.status){case"removed":console.log(` ${$s(!0)} pando-ai: removed current-terminal shim at ${s.shimPath}`);break;case"failed":console.log(` ${$s(!1)} pando-ai: could not remove current-terminal shim \u2014 ${s.message}`);break;case"not_present":break}WJ();let e=Y0();switch(e.status){case"activated":console.log(` ${$s(!0)} PATH: removed managed Pando block from ${e.target}`);break;case"already_present":console.log(" PATH: no managed Pando block found");break;case"manual_required":console.log(` PATH: manual cleanup may be required. ${e.hint??""}`.trimEnd());break;case"failed":console.log(` ${$s(!1)} PATH: could not update ${e.target} \u2014 ${e.message}`);break}let t=rM();console.log(t?` ${$s(!0)} state: removed ${Ga()}`:` ${$s(!1)} state: could not remove ${Ga()}`);let r=ZJ();switch(r.status){case"removed_global":console.log(` ${$s(!0)} npm package: removed global ${kn} package`);break;case"not_global":console.log(" npm package: no global package install detected");break;case"failed":console.log(` ${$s(!1)} npm package: could not remove global ${kn} package \u2014 ${r.message}`);break}console.log(""),console.log(" Done. Pando has been removed.")}var bp=["codex","claude"];function Qv(){return new Date().toISOString()}function oi(n){return n?"\u2713":"\u2717"}function GJ(n){return!n.realPath&&!n.shimPath?` ${n.tool.padEnd(7)} not installed`:n.proxied?` ${n.tool.padEnd(7)} ${oi(!0)} protected`:n.realPath?` ${n.tool.padEnd(7)} ${oi(!1)} unprotected (real binary present, shim not ahead on PATH)`:` ${n.tool.padEnd(7)} ${oi(!1)} shim present but real binary missing`}function Lg(n,e=process.env.PATH||""){let t=Os(),r=Vw(e);console.log(""),console.log(" Pando \u2014 AI coding firewall"),console.log("");for(let s of n)console.log(GJ(s));console.log(""),console.log(` PATH ~/.pando/bin ${r.present?r.first?"first \u2713":"present but not first \u2717":"missing \u2717"}`),console.log(` Policy native_tools=${t.nativeTools} other_mcp=${t.otherMcp.mode} default=${t.defaultAction}`),console.log(` source: ${g0()?Za():"built-in defaults"}`),console.log(""),r.first||(console.log(" Pando has configured future shells with this PATH entry:"),console.log(` ${Xw()}`),console.log(""))}function zg(n){let e=Z0(n);switch(e.status){case"installed":case"updated":return console.log(` ${oi(!0)} ${n}: shim ${e.status} at ${e.shimPath}`),nM(n,e.realPath,Qv()),!0;case"skipped_no_real_binary":return console.log(` ${n}: skipped (no real binary on PATH to supervise)`),!1;case"failed":return console.log(` ${oi(!1)} ${n}: install failed \u2014 ${e.message}`),!1}}function JJ(){let n=W0();switch(n.status){case"installed":case"updated":console.log(` ${oi(!0)} pando-ai: command shim ${n.status} at ${n.shimPath}`),n.currentPathShimPath&&console.log(` ${oi(!0)} pando-ai: current-terminal command shim ${n.currentPathShimStatus} at ${n.currentPathShimPath}`),Sp();break;case"failed":console.log(` ${oi(!1)} pando-ai: could not install command shim \u2014 ${n.message}`);break}}function Sp(){if(Vw().first)return;let n=Q0();switch(n.status){case"activated":console.log(` ${oi(!0)} PATH: added ~/.pando/bin to ${n.target}`);break;case"already_present":console.log(` PATH: ~/.pando/bin already configured in ${n.target}`);break;case"manual_required":console.log(` ${oi(!1)} PATH: manual step required.`),n.hint&&console.log(` ${n.hint}`);break;case"failed":console.log(` ${oi(!1)} PATH: could not update startup file \u2014 ${n.message}`),console.log(` Add this yourself: ${Xw()}`);break}}async function VJ(n){let e=hg(),t=n.filter(s=>s.realPath&&!s.proxied&&!e.declined[s.tool]);if(t.length===0)return!1;let r=!1;if(t.length===bp.length){if(await ev(" Pando is an AI coding firewall. Replace `codex` and `claude` with Pando-supervised launchers?"))for(let i of t)zg(i.tool)&&(r=!0);else for(let i of t)Qw(i.tool,Qv());return r&&Sp(),r}for(let s of t)await ev(` \`${s.tool}\` is not protected by Pando yet. Install the supervised launcher?`)?zg(s.tool)&&(r=!0):Qw(s.tool,Qv());return r&&Sp(),r}async function XJ(){try{let n=await Il(vl(),()=>{}),e=await fetch(`http://${n.host}:${n.port}/health`).then(t=>t.json());console.log(` gateway: ok (memory=${e.memory?"on":"off"})`),await n.close()}catch(n){console.log(` gateway: failed \u2014 ${n instanceof Error?n.message:String(n)}`)}}async function QJ(n){switch(await sM(" What would you like to do?",["Install / repair launchers","Show / edit policy","Check gateway","Launch codex now","Launch claude now","Uninstall Pando launchers","Quit"])){case 0:{let t=!1;for(let r of n)r.realPath&&zg(r.tool)&&(t=!0);return t&&Sp(),{action:"continue",refreshStatus:!0}}case 1:return console.log(""),console.log(Ow(Os())),console.log(` Edit ${Za()} to change the ruleset.`),{action:"continue",refreshStatus:!1};case 2:return await XJ(),{action:"continue",refreshStatus:!1};case 3:return await yp("codex",[]),{action:"continue",refreshStatus:!1};case 4:return await yp("claude",[]),{action:"continue",refreshStatus:!1};case 5:return jg(),{action:"uninstalled"};default:return{action:"quit"}}}async function oD(n={}){JJ();let e=process.env.PATH||"",t=bp.map(s=>Fo(s,e));if(Lg(t,e),n.force){let s=!1;for(let i of t)i.realPath&&zg(i.tool)&&(s=!0);s&&Sp(),e=process.env.PATH||"",t=bp.map(i=>Fo(i,e)),Lg(t,e)}else await VJ(t)&&(e=process.env.PATH||"",t=bp.map(i=>Fo(i,e)),Lg(t,e));let r=!1;for(;;){let s=await QJ(t);if(s.action==="quit")break;if(s.action==="uninstalled"){r=!0;break}s.refreshStatus&&(e=process.env.PATH||"",t=bp.map(i=>Fo(i,e)),Lg(t,e))}console.log(""),console.log(r?" Done. Pando launchers have been removed.":` Done. Keep using \`codex\` and \`claude\` as usual \u2014 ${kn} supervises each launch.`)}async function aD(n={}){await oD({force:n.force})}async function cD(n,e){let t=n.includes("--memory"),r=vl({memoryEnabled:t||void 0}),s=a=>console.error(`[pando-gateway] ${a}`),i=await Il(r,s);s(`OpenAI base: ${i.openaiBaseUrl}`),s(`Anthropic base: ${i.anthropicBaseUrl}`),s("Press Ctrl-C to stop.");let o=async a=>{s(`received ${a}, shutting down`),await i.close(),process.exit(0)};process.once("SIGINT",()=>{o("SIGINT")}),process.once("SIGTERM",()=>{o("SIGTERM")}),await new Promise(()=>{})}async function lD(){let n=YJ(await iV()),e=typeof n.hook_event_name=="string"?n.hook_event_name:"",t=Os();if(e==="PreToolUse"){let r=Yv(n);if(r&&Oo(r,t)){Ml({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Allowed by Pando policy."}});return}Ml({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:Bg(r??"unknown")}});return}if(e==="PostToolUse"||e==="PostToolUseFailure"){let r=Yv(n);if(!r||!Oo(r,t)){let s=Bg(r??"unknown");Ml(e==="PostToolUse"?{decision:"block",reason:s,hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:s,...tV(r,s)}}:{hookSpecificOutput:{hookEventName:"PostToolUseFailure",additionalContext:s}})}return}if(e==="PostToolBatch"){let r=eV(n,t);if(r.length>0){let s=Bg(r.join(", "));Ml({continue:!1,decision:"block",reason:s,hookSpecificOutput:{hookEventName:"PostToolBatch",additionalContext:s}})}return}if(e==="PermissionDenied"){let r=Yv(n);(!r||!Oo(r,t))&&Ml({hookSpecificOutput:{hookEventName:"PermissionDenied",retry:!0,additionalContext:Bg(r??"unknown")}})}}function YJ(n){try{let e=JSON.parse(n);return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}catch{return{}}}function Yv(n){return typeof n.tool_name=="string"&&n.tool_name.trim()?n.tool_name:null}function eV(n,e){let t=rV(n);return[...new Set(t.filter(r=>!Oo(r,e)))]}function Bg(n){return`Pando firewall blocked off-policy Claude tool use/result: ${n}. Retry using Pando MCP tools only.`}function tV(n,e){return n&&nV(n)?{updatedMCPToolOutput:e}:{updatedToolOutput:e}}function nV(n){return n.startsWith("mcp__")}function rV(n){return[...sV(n.tool_calls),...eR(n)]}function sV(n){if(!Array.isArray(n))return[];let e=[];for(let t of n){if(!t||typeof t!="object"||Array.isArray(t))continue;let r=t.tool_name;typeof r=="string"&&r.trim()&&e.push(r)}return e}function eR(n,e=0){if(e>6||!n||typeof n!="object")return[];if(Array.isArray(n))return n.flatMap(i=>eR(i,e+1));let t=n,r=typeof t.tool_name=="string"&&t.tool_name.trim()?[t.tool_name]:[],s=["tool_calls","tool_results","tool_responses","tool_uses","content","items"];return[...r,...s.flatMap(i=>eR(t[i],e+1))]}async function iV(){let n=[];for await(let e of process.stdin)n.push(typeof e=="string"?Buffer.from(e):e);return Buffer.concat(n).toString("utf8")}function Ml(n){process.stdout.write(`${JSON.stringify(n)}
|
|
1106
1106
|
`)}function oV(n,e){let t=n[0]==="serve-http"?"serve-http":"serve",r=n[1];return n.length>2&&(console.error(`Unexpected argument: ${n[2]}`),process.exit(1)),{command:t,projectPath:r?nc.default.resolve(r):null,httpHost:e.httpHost,httpPort:e.httpPort,disableAuth:e.disableAuth}}var aV=new Set(["gateway","launch"]);function cV(n){let e={help:!1,version:!1,httpHost:"127.0.0.1",httpPort:5888,disableAuth:!1,positional:[],passthrough:[],subArgs:null},t=2;for(;t<n.length;t++){let r=n[t];if(e.positional.length===0&&!r.startsWith("-")&&aV.has(r)){e.positional.push(r),e.subArgs=n.slice(t+1);break}if(r==="--"){e.passthrough=n.slice(t+1);break}else if(r==="--help"||r==="-h")e.help=!0;else if(r==="--version"||r==="-v")e.version=!0;else if(r==="--disable-auth"||r==="--no-auth")e.disableAuth=!0;else if(r==="--host"){let s=n[++t];s||(console.error("--host requires a value"),process.exit(1)),e.httpHost=s}else if(r==="--port"){let s=n[++t],i=Number(s);(!Number.isInteger(i)||i<=0||i>65535)&&(console.error("--port requires a TCP port number"),process.exit(1)),e.httpPort=i}else r.startsWith("-")?(console.error(`Unknown option: ${r}`),process.exit(1)):e.positional.push(r)}return e}function lV(){console.error(`Usage: ${kn} Install / update Pando-supervised launchers
|
|
1107
1107
|
${kn} install Re-run the installer wizard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pando-ai",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "AI coding firewall for Codex and Claude Code: supervised launchers, Pando MCP, policy enforcement, Claude hooks, and local provider gateway.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pando-ai": "bin/pando-ai.js"
|
|
Binary file
|
|
Binary file
|