pando-ai 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1011,7 +1011,7 @@ data:
1011
1011
  `),i=t.authReadFiles.filter(u=>fn.default.existsSync(u)).map(u=>`(allow file-read* (literal ${Au(u)}))`).join(`
1012
1012
  `),a=n.map(u=>`(allow file-write* (subpath ${Au(u)}))`).join(`
1013
1013
  `),c=["/dev/null","/dev/zero","/dev/random","/dev/urandom"].filter(u=>fn.default.existsSync(u)).map(u=>`(allow file-read* file-write* (literal ${Au(u)}))`).join(`
1014
- `),l=["com.apple.system.logger","com.apple.system.opendirectoryd.libinfo","com.apple.system.opendirectoryd.membership","com.apple.coreservices.launchservicesd"].map(u=>`(allow mach-lookup (global-name ${Au(u)}))`).join(`
1014
+ `),l=["com.apple.system.logger","com.apple.system.opendirectoryd.libinfo","com.apple.system.opendirectoryd.membership","com.apple.coreservices.launchservicesd","com.apple.FSEvents"].map(u=>`(allow mach-lookup (global-name ${Au(u)}))`).join(`
1015
1015
  `);return["(version 1)","(deny default)","(allow process*)","(allow sysctl*)","(allow file-read-metadata)","(allow file-read*)",s,o,i,a,c,l,"(allow network*)"].filter(Boolean).join(`
1016
1016
  `)}function Of(t){let e=new Set,n=[];for(let r of t){let s;try{if(s=fn.default.realpathSync.native?fn.default.realpathSync.native(r):fn.default.realpathSync(r),!fn.default.statSync(s).isDirectory())continue}catch{continue}e.has(s)||(e.add(s),n.push(s))}return n}function L4(t,e){let n=nn.default.relative(t,e);return n===""||!n.startsWith("..")&&!nn.default.isAbsolute(n)}function Au(t){return JSON.stringify(t)}var Iu=H(require("fs"));function z4(){return typeof process.getuid=="function"?process.getuid():null}function B4(t){return`${t.uid}:${t.gid}`}function _k(t){try{let e=Iu.default.statSync(t);return{uid:e.uid,gid:e.gid}}catch{return null}}function PF(t){let e=t.requireRoot!==!1,n=t.projectRoot,r;try{r=Iu.default.realpathSync.native(n)}catch(d){return{ok:!1,root:n,error:`cannot resolve project root: ${d?.message||d}`}}if(e&&z4()!==0)return{ok:!0,skipped:!0,root:r,reason:"not running as root"};let s=t.targetOwner??_k(r);if(!s)return{ok:!1,root:r,error:"cannot stat project root owner"};let o=new Set(t.pandoOwners.map(d=>d.uid));if(o.size===0)return{ok:!0,skipped:!0,root:r,reason:"no Pando owner ids resolved"};if(o.has(s.uid))return{ok:!0,skipped:!0,root:r,reason:`project root owner ${B4(s)} is a Pando owner`};let i=0,a=0,c=[],l=d=>{c.length<20&&c.push(d),t.onError?.(d)},u=d=>{let p;try{p=Iu.default.lstatSync(d)}catch(f){l(`${d}: lstat failed: ${f?.message||f}`);return}if(i+=1,p.isDirectory()&&!p.isSymbolicLink()){let f;try{f=Iu.default.readdirSync(d)}catch(m){l(`${d}: readdir failed: ${m?.message||m}`),f=[]}for(let m of f)u(`${d}/${m}`)}if(o.has(p.uid))try{Iu.default.chownSync(d,s.uid,s.gid),a+=1}catch(f){l(`${d}: chown failed: ${f?.message||f}`)}};return u(r),{ok:!0,root:r,visited:i,changed:a,errors:c}}var zc="/var/run/pando-security-helper.sock",kk="PANDO_INTERNAL_SERVE",Gr="PANDO_SECURITY_DATA_ROOT",Nu="PANDO_REAL_PROJECT_ROOT",CF="PANDO_HELPER_STARTUP_FAILURE ",IF={core:"pando-ai-core",workspace:"pando-ai-ws"},q4=["LANG","LC_ALL","LC_CTYPE","LC_MESSAGES"],H4=["PANDO_MCP_OUTPUT","PANDO_MCP_CLIENT","CODEX_HOME","CODEX_THREAD_ID","CODEX_SANDBOX","CODEX_MANAGED_BY_NPM"];function W4(){let t=process.env.PANDO_GATEWAY_LOG_FILE;return t?t.startsWith("~/")?us.default.join(Xr(),t.slice(2)):t:Cu(process.env.PANDO_DEBUG)===!1?null:us.default.join(Xr(),Df)}function pt(t,e={}){let n=W4();if(n)try{let r=JSON.stringify({ts:new Date().toISOString(),event:`security_helper_${t}`,pid:process.pid,...e});oi.default.mkdirSync(us.default.dirname(n),{recursive:!0}),oi.default.appendFileSync(n,`${r}
1017
1017
  `,"utf8")}catch{}}function zi(t){return{destroyed:t.destroyed,writable:t.writable,writableEnded:t.writableEnded,writableFinished:t.writableFinished,readableEnded:t.readableEnded,bytesRead:t.bytesRead,bytesWritten:t.bytesWritten}}function Ff(t){let e=["[pand\u014D] MCP startup failed"];return t.role&&e.push(`role=${t.role}`),t.projectRoot&&e.push(`projectRoot=${t.projectRoot}`),t.source&&e.push(`source=${t.source}`),`${e.join(" ")}: ${t.error}`}function Ea(t){console.error(Ff(t))}function U4(t){let n=(Buffer.isBuffer(t)?t.toString("utf8"):t).trimStart();if(!n.startsWith("{"))return!1;try{let r=n.split(/\r?\n/,1)[0],s=JSON.parse(r);return!!(s&&typeof s=="object")}catch{return!1}}function jf(t){return IF[t]}function wk(t,e){let n=t.indexOf(e);return n===-1?null:t[n+1]||null}function K4(t){let e=new Set(["--role","--project-root","--mcp-client","--stdio"]);for(let i=0;i<t.length;i+=1){let a=t[i];if(!e.has(a))return{ok:!1,error:`Unknown security launch option: ${a}`};if((a==="--role"||a==="--project-root"||a==="--mcp-client")&&(i+=1,!t[i]||t[i].startsWith("--")))return{ok:!1,error:`${a} requires a value`}}let n=wk(t,"--role");if(n!=="core"&&n!=="workspace")return{ok:!1,error:"--role must be either 'core' or 'workspace'"};let r=wk(t,"--project-root");if(!r)return{ok:!1,error:"--project-root is required"};let s=wk(t,"--mcp-client");if(s&&s!=="codex"&&s!=="claude")return{ok:!1,error:"--mcp-client must be either 'codex' or 'claude'"};let o=s==="codex"||s==="claude"?s:void 0;return t.includes("--stdio")?{ok:!0,request:{role:n,projectRoot:r,...o?{mcpClient:o}:{},stdio:!0}}:{ok:!1,error:"--stdio is required; no other transport is supported by the helper"}}function Z4(t){let e=process.env.PANDO_SECURITY_HELPER_SOCKET||zc;for(let n=0;n<t.length;n+=1){let r=t[n];if(r!=="--socket")return{ok:!1,error:`Unknown security daemon option: ${r}`};let s=t[++n];if(!s||s.startsWith("--"))return{ok:!1,error:"--socket requires a value"};e=s}return us.default.isAbsolute(e)?{ok:!0,socketPath:e}:{ok:!1,error:"--socket must be an absolute path"}}function NF(t){let e=kt(t.projectRoot),n;try{n=oi.default.statSync(e)}catch{return{ok:!1,error:`Project root does not exist: ${t.projectRoot}`}}if(!n.isDirectory())return{ok:!1,error:`Project root is not a directory: ${t.projectRoot}`};let r=go(e);return r?{ok:!1,error:`Refusing to launch Pando for forbidden project root '${e}' (matches protected path '${r}')`}:{ok:!0,request:t,canonicalProjectRoot:e}}function DF(t){let e=(0,$f.spawnSync)("id",["-u",t],{encoding:"utf8"});if(e.status!==0)return{ok:!1,error:`Dedicated user '${t}' does not exist`};let n=(0,$f.spawnSync)("id",["-g",t],{encoding:"utf8"});if(n.status!==0)return{ok:!1,error:`Could not resolve primary group for '${t}'`};let r=Number(String(e.stdout||"").trim()),s=Number(String(n.stdout||"").trim());return!Number.isInteger(r)||!Number.isInteger(s)?{ok:!1,error:`Invalid uid/gid for '${t}'`}:{ok:!0,uid:r,gid:s}}function G4(){let t=[];for(let e of Object.values(IF)){let n=DF(e);n.ok&&t.push({uid:n.uid,gid:n.gid,label:e})}return t}function AF(t,e){let n=_k(t),r=PF({projectRoot:t,targetOwner:n,pandoOwners:G4(),onError:s=>pt("daemon_ownership_normalize_error",{projectRoot:t,phase:e,error:s})});if(r.ok&&r.skipped){pt("daemon_ownership_normalize_skipped",{projectRoot:r.root,phase:e,reason:r.reason});return}if(!r.ok){pt("daemon_ownership_normalize_failed",{projectRoot:r.root,phase:e,error:r.error});return}pt("daemon_ownership_normalize_complete",{projectRoot:r.root,phase:e,visited:r.visited,changed:r.changed,errors:r.errors.length})}function J4(){return typeof process.getuid=="function"?process.getuid():null}function V4(t){return t.PATH||["/opt/homebrew/bin","/opt/homebrew/sbin","/usr/local/bin","/usr/local/sbin","/usr/bin","/bin","/usr/sbin","/sbin"].join(us.default.delimiter)}function X4(t){let e=t[Gr];return e&&e.trim()?us.default.resolve(e):null}function Q4(t){let e=X4(t);if(!e)return null;try{let n=oi.default.statSync(e);return{uid:n.uid,gid:n.gid}}catch{return null}}function OF(t,e){try{let n=kt(t);return pt("direct_workspace_ready",{realRoot:t,sandboxRoot:n}),{ok:!0,sandboxRoot:n}}catch(n){let r=n?.message||String(n);return pt("direct_workspace_failed",{realRoot:t,error:r,code:n?.code,syscall:n?.syscall,path:n?.path}),{ok:!1,error:`Failed to prepare Pando direct workspace for '${t}': ${r}`}}}function Y4(t,e=process.env,n){let r={PATH:V4(e),PANDO_PROCESS_ROLE:t,PANDO_SECURITY_HELPER_LAUNCHED:"1",[kk]:"1",PANDO_CLI_DISABLE_AUTH:"1",PANDO_DISABLE_AUTH:"1",PANDO_BROKERED_AUTH:"1"};for(let s of q4)e[s]&&(r[s]=e[s]);for(let s of H4)e[s]&&(r[s]=e[s]);return n&&(r.PANDO_MCP_CLIENT=n),e[Nu]&&(r[Nu]=e[Nu]),e[Gr]&&(r[Gr]=e[Gr]),e.PANDO_DATA_DIR&&(r.PANDO_DATA_DIR=e.PANDO_DATA_DIR),e.PANDO_WORKSPACE_CONFIG_DIR&&(r.PANDO_WORKSPACE_CONFIG_DIR=e.PANDO_WORKSPACE_CONFIG_DIR),r}function eX(t){let e=jf(t.request.role),n=t.env?.[Gr]||us.default.join(Rk.default.homedir(),".pando-data"),r=us.default.join(ms(n,t.canonicalProjectRoot),"config"),s=Y4(t.request.role,{...t.env,PANDO_DATA_DIR:n,PANDO_WORKSPACE_CONFIG_DIR:r,...t.realProjectRoot?{[Nu]:t.realProjectRoot}:{},...t.env?.[Gr]?{[Gr]:t.env[Gr]}:{}},t.request.mcpClient),o={command:t.execPath||process.execPath,args:[t.cliPath,"serve",t.canonicalProjectRoot],cwd:t.canonicalProjectRoot,env:s,targetUser:e};return typeof t.uid=="number"&&(o.uid=t.uid),typeof t.gid=="number"&&(o.gid=t.gid),o}function tX(t){let e=NF(t.request);if(!e.ok)return e;let n=jf(e.request.role),r=DF(n);if(!r.ok)return r;let s=e.request.realProjectRoot?{ok:!0,sandboxRoot:e.canonicalProjectRoot}:OF(e.canonicalProjectRoot,t.env??process.env);if(!s.ok)return s;let o=J4();return o!==0&&o!==r.uid?{ok:!1,error:`security launch must run as root or as '${n}' (current uid: ${o??"unknown"})`}:{ok:!0,spec:eX({request:e.request,canonicalProjectRoot:s.sandboxRoot,realProjectRoot:e.request.realProjectRoot??e.canonicalProjectRoot,cliPath:t.cliPath,execPath:t.execPath,env:t.env,uid:o===0?r.uid:void 0,gid:o===0?r.gid:void 0})}}function nX(t){let e;try{e=JSON.parse(t)}catch{return{ok:!1,error:"Invalid helper request JSON"}}if(!e||typeof e!="object"||Array.isArray(e))return{ok:!1,error:"Invalid helper request"};let n=e;if(n.command!=="launch")return{ok:!1,error:"Unsupported helper command"};if(n.role!=="core"&&n.role!=="workspace")return{ok:!1,error:"Invalid helper launch role"};if(typeof n.projectRoot!="string"||!n.projectRoot)return{ok:!1,error:"Invalid helper projectRoot"};if(n.realProjectRoot!==void 0&&(typeof n.realProjectRoot!="string"||!n.realProjectRoot))return{ok:!1,error:"Invalid helper realProjectRoot"};if(n.mcpClient!==void 0&&n.mcpClient!=="codex"&&n.mcpClient!=="claude")return{ok:!1,error:"Invalid helper mcpClient"};if(n.stdio!==!0)return{ok:!1,error:"Only stdio helper launch is supported"};let r=new Set(["command","role","projectRoot","realProjectRoot","mcpClient","stdio"]);for(let s of Object.keys(n))if(!r.has(s))return{ok:!1,error:`Unknown helper request field: ${s}`};return{ok:!0,request:{role:n.role,projectRoot:n.projectRoot,realProjectRoot:n.realProjectRoot,...n.mcpClient?{mcpClient:n.mcpClient}:{},stdio:!0}}}function rX(){return process.env.PANDO_SECURITY_HELPER_SOCKET||zc}function sX(t){return new Promise((e,n)=>{let r=vk.default.createConnection(t);r.once("connect",()=>e(r)),r.once("error",n)})}function oX(t){return t.toString("utf8").replace(/\s+/g," ").slice(0,500)}function iX(t){let e=t.toString("utf8");return e.startsWith(CF)?e.slice(CF.length).trim():null}function MF(t,e){pt("client_launch_start",{argCount:t.length});let n=K4(t);if(!n.ok)return pt("client_parse_failed",{error:n.error}),Ea({error:n.error,source:"security launch argument parsing"}),Promise.resolve(1);let r=NF(n.request);if(!r.ok)return pt("client_validation_failed",{role:n.request.role,projectRoot:n.request.projectRoot,error:r.error}),Ea({role:n.request.role,projectRoot:n.request.projectRoot,error:r.error,source:"project validation"}),Promise.resolve(1);let s=rX(),o=OF(r.canonicalProjectRoot,process.env);return o.ok?(pt("client_connect_start",{role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,sandboxRoot:o.sandboxRoot,socketPath:s}),sX(s).then(i=>new Promise(a=>{let c=!1,l=!1;pt("client_connected",{socketPath:s,socket:zi(i)});let u=d=>{l||(l=!0,a(d))};i.on("error",d=>{pt("client_socket_error",{socketPath:s,error:d.message,socket:zi(i)}),Ea({role:r.request.role,projectRoot:r.canonicalProjectRoot,error:`security helper connection failed: ${d.message}`,source:s}),u(1)}),i.on("data",d=>{if(!c){let p=iX(d);if(p){pt("client_startup_failure",{socketPath:s,role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,preview:p.slice(0,700)}),console.error(p),i.destroy(),u(1);return}c=!0;let f=oX(d);if(pt("client_first_output",{socketPath:s,role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,bytes:d.length,preview:f}),!U4(d)){Ea({role:r.request.role,projectRoot:r.canonicalProjectRoot,error:f||"security helper closed before returning an MCP initialize response",source:s}),i.destroy(),u(1);return}process.stdout.write(d,m=>{m&&pt("client_stdout_write_error",{socketPath:s,role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,bytes:d.length,error:m.message,socket:zi(i)})});return}process.stdout.write(d,p=>{p&&pt("client_stdout_write_error",{socketPath:s,role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,bytes:d.length,error:p.message,socket:zi(i)})})}),i.on("close",()=>{if(!l){if(pt("client_socket_close",{socketPath:s,role:r.request.role,canonicalProjectRoot:r.canonicalProjectRoot,sawOutput:c,socket:zi(i)}),!c){Ea({role:r.request.role,projectRoot:r.canonicalProjectRoot,error:"security helper closed before producing any MCP output",source:s}),u(1);return}u(0)}}),i.write(`${JSON.stringify({command:"launch",role:r.request.role,projectRoot:o.sandboxRoot,realProjectRoot:r.canonicalProjectRoot,...r.request.mcpClient?{mcpClient:r.request.mcpClient}:{},stdio:!0})}
@@ -33225,7 +33225,7 @@ ${f.join(`
33225
33225
  `),P=o.authReadFiles.filter(Q=>CI.default.existsSync(Q)).map(Q=>`(allow file-read* (literal ${Q8e(Q)}))`).join(`
33226
33226
  `),R=a.map(Q=>`(allow file-write* (subpath ${Q8e(Q)}))`).join(`
33227
33227
  `),z=["/dev/null","/dev/zero","/dev/random","/dev/urandom"].filter(Q=>CI.default.existsSync(Q)).map(Q=>`(allow file-read* file-write* (literal ${Q8e(Q)}))`).join(`
33228
- `),H=["com.apple.system.logger","com.apple.system.opendirectoryd.libinfo","com.apple.system.opendirectoryd.membership","com.apple.coreservices.launchservicesd"].map(Q=>`(allow mach-lookup (global-name ${Q8e(Q)}))`).join(`
33228
+ `),H=["com.apple.system.logger","com.apple.system.opendirectoryd.libinfo","com.apple.system.opendirectoryd.membership","com.apple.coreservices.launchservicesd","com.apple.FSEvents"].map(Q=>`(allow mach-lookup (global-name ${Q8e(Q)}))`).join(`
33229
33229
  `);return["(version 1)","(deny default)","(allow process*)","(allow sysctl*)","(allow file-read-metadata)","(allow file-read*)",g,x,P,R,z,H,"(allow network*)"].filter(Boolean).join(`
33230
33230
  `)}function Rtt(o){let i=new Set,a=[];for(let f of o){let g;try{if(g=CI.default.realpathSync.native?CI.default.realpathSync.native(f):CI.default.realpathSync(f),!CI.default.statSync(g).isDirectory())continue}catch{continue}i.has(g)||(i.add(g),a.push(g))}return a}function Wjn(o,i){let a=pD.default.relative(o,i);return a===""||!a.startsWith("..")&&!pD.default.isAbsolute(a)}function Q8e(o){return JSON.stringify(o)}var Mtt=ia(require("fs")),Dgn=ia(require("os")),Xk=ia(require("path")),Z8e=require("child_process");S4e();zA();var Vjn=ia(require("fs")),Hjn=ia(require("net")),$jn=ia(require("os")),Jjn=ia(require("path")),Cgn=require("child_process");zA();S4e();var Gjn=ia(require("os")),Ujn=ia(require("path"));var jjn=ia(require("os")),zjn=ia(require("path"));zA();var qjn={core:"pando-ai-core",workspace:"pando-ai-ws"};function XUt(o){return qjn[o]}var Q0e=XUt("workspace"),Y8e=XUt("core");var eVt="ai.pando.security-helper",Jpr=`/Library/LaunchDaemons/${eVt}.plist`,qpr=`/Library/PrivilegedHelperTools/${eVt}`,Kpr=`/Library/PrivilegedHelperTools/${eVt}-runtime`;var Kjn="security-acls.json";function Ign(){return typeof process.getuid=="function"&&process.getuid()===0}function KDe(o,i,a){let f=process.env.CI||/^(1|true|yes)$/i.test(process.env.PANDO_SUDO_NONINTERACTIVE||"")?["-n",o,...i]:[o,...i],g=Ign()?{command:o,args:i}:{command:"sudo",args:f};if(a)return console.log([g.command,...g.args].join(" ")),{ok:!0};let x=(0,Z8e.spawnSync)(g.command,g.args,{encoding:"utf8",stdio:"pipe"});if(x.status===0)return{ok:!0};let P=(x.stderr||x.stdout||x.error?.message||"").trim();return{ok:!1,error:`${[g.command,...g.args].join(" ")} failed${P?`: ${P}`:""}`}}function Agn(o){Mtt.default.mkdirSync(o,{recursive:!0})}function YUt(o,i){return i==="write"?`${o} allow list,add_file,search,delete,add_subdirectory,delete_child,file_inherit,directory_inherit,readattr,writeattr,readextattr,writeextattr,readsecurity,read,write,append,execute`:i==="read"?`${o} allow list,search,file_inherit,directory_inherit,readattr,readextattr,readsecurity,read,execute`:`${o} allow search,readattr,readextattr,readsecurity`}function kgn(o){return Xk.default.join(Xk.default.resolve(o),Kjn)}function Ngn(o){try{let i=Mtt.default.readFileSync(kgn(o),"utf8"),a=JSON.parse(i);return Array.isArray(a?.entries)?a.entries.filter(f=>f&&typeof f.target=="string"&&typeof f.user=="string"&&typeof f.acl=="string"&&typeof f.recursive=="boolean"&&typeof f.platform=="string"):[]}catch{return[]}}function Xjn(o,i,a){let f=kgn(o);if(a){console.log(`write ${f} # ${i.length} tracked ACL entr${i.length===1?"y":"ies"}`);return}Mtt.default.mkdirSync(Xk.default.dirname(f),{recursive:!0}),Mtt.default.writeFileSync(f,`${JSON.stringify({version:1,entries:i},null,2)}
33231
33231
  `,"utf8")}function Qjn(o,i,a){let f=Ngn(o).filter(g=>!(g.platform===i.platform&&g.target===i.target&&g.user===i.user&&g.kind===i.kind&&g.recursive===i.recursive&&g.acl===i.acl));f.push(i),Xjn(o,f,a)}function QUt(o,i){return o.ok?o:i&&/No ACL entry|Invalid file mode|No such file|not found|No such file or directory|Invalid argument/i.test(o.error)?{ok:!0}:o}function Rgn(o,i,a,f){return KDe("find",[a,"-xdev","(","-type","f","-o","-type","d",")","-exec","chmod",o,i,"{}","+"],f)}function Yjn(o,i,a=!1){if(o.platform==="darwin"){let f=o.recursive?Rgn("-a",o.acl,o.target,i):KDe("chmod",["-a",o.acl,o.target],i);return QUt(f,a)}if(o.platform==="linux"){let f=KDe("setfacl",[...o.recursive?["-R"]:[],"-x",`u:${o.user}`,o.target],i);return f=QUt(f,a),f.ok?o.recursive?(f=KDe("setfacl",["-R","-d","-x",`u:${o.user}`,o.target],i),QUt(f,!0)):{ok:!0}:f}return{ok:!0}}function ZUt(o,i){return`u:${o}:${i==="write"?"rwx":i==="read"?"rx":"x"}`}function Pgn(o){let i=Date.now(),a=Xk.default.resolve(o.target),f=process.platform==="darwin"?YUt(o.user,o.kind):ZUt(o.user,o.kind),g={platform:process.platform,target:a,user:o.user,kind:o.kind,recursive:o.recursive,acl:f},x={target:a,user:o.user,kind:o.kind,recursive:o.recursive,platform:process.platform,dryRun:o.dryRun};o.onProgress?.("acl step begin",x);let P=Date.now(),R=Yjn(g,o.dryRun,!0),z=Date.now()-P;if(o.onProgress?.("acl remove existing complete",{...x,ok:R.ok,elapsedMs:z,error:R.ok?void 0:R.error}),!R.ok)return R;let H,Q=Date.now();process.platform==="darwin"?H=o.recursive?Rgn("+a",f,a,o.dryRun):KDe("chmod",["+a",f,a],o.dryRun):process.platform==="linux"?(H=KDe("setfacl",[...o.recursive?["-R"]:[],"-m",f,a],o.dryRun),H.ok&&o.recursive&&(H=KDe("setfacl",["-R","-d","-m",f,a],o.dryRun))):H={ok:!1,error:`Tracked ACL setup is not implemented on ${process.platform}`};let se=Date.now()-Q;return o.onProgress?.("acl add complete",{...x,ok:H.ok,elapsedMs:se,error:H.ok?void 0:H.error}),H.ok?(Qjn(o.dataRoot,g,o.dryRun),o.onProgress?.("acl step complete",{...x,elapsedMs:Date.now()-i,removeElapsedMs:z,addElapsedMs:se}),{ok:!0}):H}function Mgn(o){let i=Xk.default.resolve(o),a=Xk.default.resolve(Dgn.default.homedir()),f=[],g=Xk.default.dirname(i);for(;g!==Xk.default.dirname(g);){let x=Xk.default.relative(a,g);(x===""||x&&!x.startsWith("..")&&!Xk.default.isAbsolute(x))&&f.push(g),g=Xk.default.dirname(g)}return f.reverse()}function Lgn(o,i){let a=[];if(i){let f=YZ(o,i);a.unshift({target:i,user:Q0e,kind:"write",recursive:!0},{target:i,user:Y8e,kind:"read",recursive:!0},{target:f,user:Q0e,kind:"write",recursive:!0},{target:f,user:Y8e,kind:"read",recursive:!0})}return a}function Zjn(o,i){let a=Lgn(o,i),f=new Set;for(let{target:x,user:P}of a)for(let R of Mgn(x))f.add(`${R}\0${P}`);let g=[];for(let x of f){let[P,R]=x.split("\0"),z="traverse";g.push({platform:process.platform,target:Xk.default.resolve(P),user:R,kind:z,recursive:!1,acl:process.platform==="darwin"?YUt(R,z):ZUt(R,z)})}for(let x of a)g.push({platform:process.platform,target:Xk.default.resolve(x.target),user:x.user,kind:x.kind,recursive:x.recursive,acl:process.platform==="darwin"?YUt(x.user,x.kind):ZUt(x.user,x.kind)});return g}function ezn(o,i){return o.platform===i.platform&&o.target===i.target&&o.user===i.user&&o.kind===i.kind&&o.recursive===i.recursive&&o.acl===i.acl}function tzn(o,i){let a=Xk.default.resolve(o),f=Xk.default.resolve(i);if(Fve(f))return!1;let x=Ngn(a);return Zjn(a,f).every(P=>x.some(R=>ezn(R,P)))}function nzn(o){let i=!!o.dryRun,a=Xk.default.resolve(o.dataRoot),f=o.realRoot?Xk.default.resolve(o.realRoot):null;if(!f)return{ok:!1,message:"Project root is required for repo ACL setup"};let g=Fve(f);if(g)return{ok:!1,message:`Refusing to configure Pando security for project root '${f}' (matches protected path '${g}'). Run Pando from a real project folder, not your home directory, /Users, a system directory, or a credential/config directory.`};Agn(a),Agn(YZ(a,f));let x=Lgn(a,f),P=new Set;for(let{target:R,user:z}of x)for(let H of Mgn(R))P.add(`${H}\0${z}`);for(let R of P){let[z,H]=R.split("\0");o.onProgress?.(`granting ${H} traverse access to ${z}`);let Q=Pgn({dataRoot:a,target:z,user:H,kind:"traverse",recursive:!1,dryRun:i,onProgress:o.onProgress});if(!Q.ok)return{ok:!1,message:Q.error}}for(let R of x){o.onProgress?.(`granting ${R.user} ${R.kind} access to ${R.target}`);let z=Pgn({dataRoot:a,...R,dryRun:i,onProgress:o.onProgress});if(!z.ok)return{ok:!1,message:z.error}}return{ok:!0,message:`Pando repo/workspace ACLs configured for ${f}: ${Q0e}, ${Y8e}`}}function rzn(o){let i=(0,Z8e.spawnSync)("id",["-u",o],{encoding:"utf8"}),a=(0,Z8e.spawnSync)("id",["-g",o],{encoding:"utf8"});if(i.status!==0||a.status!==0)return{ok:!1,error:`Dedicated user '${o}' does not exist`};let f=Number(String(i.stdout||"").trim()),g=Number(String(a.stdout||"").trim());return!Number.isInteger(f)||!Number.isInteger(g)?{ok:!1,error:`Invalid uid/gid for '${o}'`}:{ok:!0,uid:f,gid:g}}function izn(o,i,a){let f=rzn(o);if(!f.ok)return f;let g="/bin/test",x=[i,a],P;return Ign()?P=(0,Z8e.spawnSync)(g,x,{stdio:"pipe",uid:f.uid,gid:f.gid}):P=(0,Z8e.spawnSync)("sudo",["-n","-u",o,g,...x],{stdio:"pipe"}),P.status===0?{ok:!0}:{ok:!1,error:`${o} cannot ${i} ${a}`}}function wgn(o,i){let a=Xk.default.resolve(o),f=Xk.default.resolve(i),g=YZ(a,f),x=[];tzn(a,f)||x.push(`tracked ACL registry is incomplete for ${f}`);let P=[{user:Q0e,flag:"-x",target:Xk.default.dirname(f)},{user:Q0e,flag:"-r",target:f},{user:Q0e,flag:"-w",target:f},{user:Q0e,flag:"-r",target:g},{user:Q0e,flag:"-w",target:g},{user:Y8e,flag:"-x",target:Xk.default.dirname(f)},{user:Y8e,flag:"-r",target:f},{user:Y8e,flag:"-r",target:g}];for(let R of P){let z=izn(R.user,R.flag,R.target);z.ok||x.push(z.error)}return x.length>0?{ok:!1,message:`Project ACL verification failed: ${x.join("; ")}`}:{ok:!0,message:`Project ACL verification passed for ${f}`}}function Ogn(o){if(!o.dryRun&&o.realRoot){let x=Date.now(),P=wgn(o.dataRoot,o.realRoot);if(o.onProgress?.("project ACL preflight verification complete",{ok:P.ok,elapsedMs:Date.now()-x,message:P.message}),P.ok)return o.onProgress?.("project ACL setup skipped",{reason:"existing-access-verified",realRoot:Xk.default.resolve(o.realRoot)}),P}let i=Date.now(),a=nzn(o);if(o.onProgress?.("project ACL apply complete",{ok:a.ok,elapsedMs:Date.now()-i,message:a.message}),!a.ok||o.dryRun||!o.realRoot)return a;let f=Date.now(),g=wgn(o.dataRoot,o.realRoot);return o.onProgress?.("project ACL post-apply verification complete",{ok:g.ok,elapsedMs:Date.now()-f,message:g.message}),g}var szn="/Library/PrivilegedHelperTools/ai.pando.security-helper-runtime/dist/cli.js",ozn="/var/run/pando-security-helper.sock",azn="PANDO_INTERNAL_SERVE",lzn="PANDO_REAL_PROJECT_ROOT",Y0e="PANDO_SECURITY_DATA_ROOT",czn="PANDO_HELPER_STARTUP_FAILURE ",jgn={core:"pando-ai-core",workspace:"pando-ai-ws"},uzn=["LANG","LC_ALL","LC_CTYPE","LC_MESSAGES"],dzn=["PANDO_MCP_OUTPUT","PANDO_MCP_CLIENT","CODEX_HOME","CODEX_THREAD_ID","CODEX_SANDBOX","CODEX_MANAGED_BY_NPM"],fzn=1;function fE(o,i={}){let a=process.env[Y0e]||YH.default.join(GTt.default.homedir(),".pando-data"),f=process.env.PANDO_GATEWAY_LOG_FILE||YH.default.join(a,"gateway-debug.jsonl");try{Lee.default.mkdirSync(YH.default.dirname(f),{recursive:!0}),Lee.default.appendFileSync(f,`${JSON.stringify({ts:new Date().toISOString(),event:`security_daemon_${o}`,pid:process.pid,...i})}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pando-ai",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "AI coding firewall for Codex and Claude Code",
5
5
  "bin": {
6
6
  "pando-ai": "bin/pando-ai.js"