@radhya/mach 2.0.43 → 2.0.45

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{a as Zt,b as Ve,c as Yo,d as Bt,e as ve,f as Me,g as si,h as ai,i as Qt,j as qo,l as An}from"./chunk-ZPETQEMG.js";import{a as m,b as Wo,c as Ne,d as Jo,e as Xt,f as It,g as at,h as N}from"./chunk-S64YFTRR.js";import{Command as Hh,Option as Xe}from"commander";import{configDotenv as Gh}from"dotenv";import{intro as de,outro as qc}from"@clack/prompts";import ae from"chalk";import{text as Xo,confirm as nd,spinner as id,isCancel as fr,cancel as wn,note as rd,outro as od}from"@clack/prompts";import In from"chalk";async function Zo(){try{let e=await Xo({message:"Enter Bucket Name:",placeholder:"e.g. my-app-dev",validate(r){if(r.length===0)return"Bucket name is required"}});fr(e)&&(wn("Operation cancelled."),process.exit(0));let t=await Xo({message:"Enter Region:",initialValue:"ap-south-1"});fr(t)&&(wn("Operation cancelled."),process.exit(0));let n=await nd({message:"Do you want to create a CloudFront Distribution for this bucket?",initialValue:!0});fr(n)&&(wn("Operation cancelled."),process.exit(0));let i=id();i.start("Setting up hosting infrastructure...");try{let r=await N.post("/hosting/setup",{bucketName:e,region:t,createCloudFront:n});if(r.data.status!=="success")throw new Error(r.data.message||"Hosting setup failed");i.stop("Hosting setup complete!");let o=r.data,s=`Bucket Name: ${In.cyan(o.bucketName)}
2
+ import{a as Zt,b as Ve,c as Yo,d as Bt,e as ve,f as Me,g as si,h as ai,i as Qt,j as qo,l as An}from"./chunk-DX4FCPCX.js";import{a as m,b as Wo,c as Ne,d as Jo,e as Xt,f as It,g as at,h as N}from"./chunk-S64YFTRR.js";import{Command as jh,Option as Xe}from"commander";import{configDotenv as Hh}from"dotenv";import{intro as de,outro as qc}from"@clack/prompts";import ae from"chalk";import{text as Xo,confirm as nd,spinner as id,isCancel as fr,cancel as wn,note as rd,outro as od}from"@clack/prompts";import In from"chalk";async function Zo(){try{let e=await Xo({message:"Enter Bucket Name:",placeholder:"e.g. my-app-dev",validate(r){if(r.length===0)return"Bucket name is required"}});fr(e)&&(wn("Operation cancelled."),process.exit(0));let t=await Xo({message:"Enter Region:",initialValue:"ap-south-1"});fr(t)&&(wn("Operation cancelled."),process.exit(0));let n=await nd({message:"Do you want to create a CloudFront Distribution for this bucket?",initialValue:!0});fr(n)&&(wn("Operation cancelled."),process.exit(0));let i=id();i.start("Setting up hosting infrastructure...");try{let r=await N.post("/hosting/setup",{bucketName:e,region:t,createCloudFront:n});if(r.data.status!=="success")throw new Error(r.data.message||"Hosting setup failed");i.stop("Hosting setup complete!");let o=r.data,s=`Bucket Name: ${In.cyan(o.bucketName)}
3
3
  `;s+=`Region: ${In.cyan(o.region)}
4
4
  `,s+=`S3 Website URL: ${In.blue(o.s3WebsiteUrl)}`,o.cloudFrontId&&(s+=`
5
5
 
@@ -1546,17 +1546,17 @@ ${r}
1546
1546
  `).forEach(n=>{if(n.includes("List of devices")||!n.trim())return;let i=n.split(/\s+/);if(i.length<2||i[1]!=="device")return;let r=i[0],o=n.match(/model:([^\s]+)/);e.push({id:r,platform:"android",type:r.startsWith("emulator-")?"emulator":"physical",name:o?o[1].replace(/_/g," "):r,status:"online",details:n.trim()})})}catch{}try{let n=(await Di(rl(),["-list-avds"])).split(`
1547
1547
  `).filter(r=>!!r.trim()),i=e.some(r=>r.platform==="android"&&r.id.startsWith("emulator-"));n.forEach(r=>{i||e.push({id:r,platform:"android",type:"emulator",name:r.replace(/_/g," "),status:"offline",details:`Android Virtual Device: ${r}`})})}catch{}return e}async function cl(){if(process.platform!=="darwin")return[];try{let e=await Di("/usr/bin/xcrun",["simctl","list","devices","--json"]),t=JSON.parse(e),n=[];return Object.entries(t.devices||{}).forEach(([i,r])=>{r.forEach(o=>{o.isAvailable&&n.push({id:o.udid,platform:"ios",type:"simulator",name:o.name,status:o.state?.toLowerCase()==="booted"?"online":"offline",runtime:i,details:`${o.name} (${i})`})})}),n}catch{return[]}}async function om(){if(process.platform!=="darwin")return[];try{let e=await Di("/usr/bin/xcrun",["devicectl","list","devices","--json-output","-"]);return(JSON.parse(e).result?.devices||[]).filter(n=>{let i=n.connectionProperties?.transportType;return i==="wired"||i==="localNetwork"}).map(n=>{let i=n.connectionProperties?.transportType,r=n.deviceProperties?.name||"iPhone";return{id:n.identifier,platform:"ios",type:"physical",name:r,status:"online",details:`${r} (${n.model?.name||n.hardwareModel||""}) - ${i}`}})}catch{return[]}}async function sm(e){let t=await cl(),n=t.find(r=>r.id===e);if(n)return n;let i=t.filter(r=>r.status==="online");return i.length===1?i[0]:null}async function am(){let[e,t,n]=await Promise.all([rm(),cl(),om()]);return[...e,...t,...n]}async function lm(){let e=Date.now();return $i&&e-$i.updatedAt<Hf?$i.targets:(Ri||(Ri=am().then(t=>($i={targets:t,updatedAt:Date.now()},t)).finally(()=>{Ri=null})),Ri)}async function Ja(e){let t=[];for await(let n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return t.length===0?{}:JSON.parse(Buffer.concat(t).toString("utf-8"))}async function cm(e,t){let n=await Bf({method:"GET",url:e,responseType:"stream",timeout:12e4}),i=ce.createWriteStream(t);n.data.pipe(i),await new Promise((r,o)=>{i.on("finish",r),i.on("error",o)})}function Ya(e,t){let n=e?.stderr?.toString?.().trim();if(n)return n;let i=e?.stdout?.toString?.().trim();return i||e?.message||t}function dm(e,t){return kt("/usr/libexec/PlistBuddy",["-c",`Print :${t}`,e],{encoding:"utf-8"}).trim()}function um(e){let t=q.join(e,"Info.plist");if(!ce.existsSync(t))throw new Error(`Could not validate iOS app executable: ${t} not found.`);let n=dm(t,"CFBundleExecutable"),i=q.join(e,n);if(!ce.existsSync(i))throw new Error(`Could not validate iOS app executable: ${i} not found.`);return i}function pm(){return process.arch==="arm64"?"arm64":"x86_64"}function fm(e){return ce.existsSync(e)?ce.readdirSync(e).map(t=>q.join(e,t)).filter(t=>t.endsWith(".app")&&ce.statSync(t).isDirectory()):[]}function qa(e,t){let n=fm(e);if(n.length===1)return n[0];if(n.length>1)throw new Error(`Multiple .app bundles found in ${t}: ${n.map(i=>q.basename(i)).join(", ")}. Mach cannot safely choose which app to install.`);return""}function mm(e){let t=um(e),n=pm(),i="",r="";try{i=kt("lipo",["-info",t],{encoding:"utf-8"}).trim()}catch(o){throw new Error(`Could not inspect iOS simulator app architecture for ${q.basename(e)}: ${o.message}`)}if(!new RegExp(`\\b${n}\\b`).test(i))throw new Error(`This iOS simulator artifact cannot be installed because ${q.basename(e)} does not include the ${n} simulator architecture. lipo output: ${i}. Rebuild the app with the latest Mach CLI and make sure the profile is a simulator build.`);try{r=kt("/usr/bin/xcrun",["vtool","-show-build",t],{encoding:"utf-8"}).trim()}catch(o){throw new Error(`Could not inspect iOS simulator app platform for ${q.basename(e)}: ${o.message}`)}if(!/\bplatform\s+IOSSIMULATOR\b/i.test(r))throw new Error(`This iOS simulator artifact cannot be installed because ${q.basename(e)} was built for the wrong Apple platform. A simulator artifact must contain platform IOSSIMULATOR. vtool output: ${r}. Rebuild the app with the latest Mach CLI.`)}async function gm(e,t,n){let i=n||(t.toLowerCase().includes(".apk")?"android":"ios"),r=t.toLowerCase().includes(".ipa"),o=ce.mkdtempSync(q.join(Qe.tmpdir(),`${bt}-install-`)),s=q.join(o,i==="android"?"install.apk":r?"install.ipa":"install.zip");try{if(await cm(t,s),i==="android"){kt(il(),["-s",e,"install","-r",s],{stdio:"pipe"});return}if(process.platform!=="darwin")throw new Error("iOS local install requires macOS.");Pi(`unzip -o ${JSON.stringify(s)} -d ${JSON.stringify(o)}`,{stdio:"pipe"});let l=q.join(o,"Payload"),a=ce.existsSync(l),c=r||a,u="";if(c?u=qa(l,"Payload"):u=qa(o,"the simulator artifact"),!u)throw new Error(`No .app bundle found inside the ${r?"IPA":"archive"}.`);let d=c?null:await sm(e);if(d){try{mm(u),kt("/usr/bin/xcrun",["simctl","install",d.id,u],{stdio:"pipe"})}catch(f){throw new Error(Ya(f,"iOS Simulator install failed."))}return}if(!c)throw new Error("Selected iOS Simulator was not found. Refresh Install Hub devices or boot one simulator and try again.");try{Pi(`/usr/bin/xcrun devicectl device install app --device ${JSON.stringify(e)} ${JSON.stringify(u)}`,{stdio:"pipe"})}catch(f){throw new Error(Ya(f,"iOS device install failed."))}}finally{ce.rmSync(o,{recursive:!0,force:!0})}}function hm(e,t){if(!e)throw new Error("Device id is required.");if(t==="ios"){if(process.platform!=="darwin")throw new Error("iOS Simulator launch requires macOS.");return Pi(`/usr/bin/xcrun simctl boot ${JSON.stringify(e)}`,{stdio:"pipe"}),za("open",["-a","Simulator"],{detached:!0,stdio:"ignore"}).unref(),"Simulator booting..."}if(t==="android")return za(rl(),["-avd",e],{detached:!0,stdio:"ignore",windowsHide:!0}).unref(),"Emulator booting in background...";throw new Error("Invalid platform.")}async function ym(){process.platform!=="darwin"&&Z.warn("Running on non-macOS \u2014 iOS discovery unavailable, Android only.");let e=Ci.createServer((i,r)=>{if(r.setHeader("Access-Control-Allow-Origin","*"),r.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),r.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),i.method==="OPTIONS"){r.writeHead(204),r.end();return}let o=new URL(i.url||"/",`http://localhost:${bt}`);if(i.method==="GET"&&o.pathname==="/local/devices"){lm().then(s=>{r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"success",targets:s}))}).catch(s=>{r.writeHead(500,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"error",message:s.message}))});return}if(i.method==="GET"&&o.pathname==="/health"){r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"success",service:"mach-agent",port:bt,platform:process.platform}));return}if(i.method==="POST"&&o.pathname==="/install-local"){t(i,r);return}if(i.method==="POST"&&o.pathname==="/local/boot"){n(i,r);return}r.writeHead(404),r.end()}),t=async(i,r)=>{try{let{deviceId:o,artifactUrl:s,platform:l}=await Ja(i);if(!o||!s){r.writeHead(400,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"error",message:"deviceId and artifactUrl are required"}));return}await gm(o,s,l),r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"success",message:`Installed on ${o}`}))}catch(o){r.writeHead(500,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"error",message:o.message||"Local install failed"}))}},n=async(i,r)=>{try{let{id:o,platform:s}=await Ja(i),l=hm(o,s);r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"success",message:l}))}catch(o){r.writeHead(500,{"Content-Type":"application/json"}),r.end(JSON.stringify({status:"error",message:o.message||"Local device launch failed"}))}};e.listen(bt,"127.0.0.1",()=>{Z.success(ye.green(`Local device agent running on http://localhost:${bt}`)),Z.info(ye.dim("Keep this running while using the Install Hub on the dashboard.")),Z.info(ye.dim("Press Ctrl+C to stop."))}),e.on("error",i=>{i.code==="EADDRINUSE"?Z.error(`Port ${bt} is already in use \u2014 agent may already be running.`):Z.error(`Agent error: ${i.message}`),process.exit(1)}),await new Promise(()=>{})}async function dl(e="start"){let t=String(e||"start").toLowerCase();if(t==="start"||t==="run"){await ym();return}if(t==="install"){await Ka();return}if(t==="status"){await im();return}if(t==="stop"){await Wa();return}if(t==="uninstall"){await nm();return}if(t==="restart"){await Wa(),await Ka();return}Z.error(`Unknown agent action: ${e}`),Z.info(ye.dim(`Use: ${m.nameLower} agent start | install | status | restart | stop | uninstall`)),process.exit(1)}import{createRequire as Sm}from"module";var bm=Sm(import.meta.url),Em=bm("../package.json"),Tt=String(Em.version||"0.0.0");import{text as wm,password as Im,spinner as vm,isCancel as fl}from"@clack/prompts";import uo from"chalk";import Fn from"fs";import ul from"path";import Am from"os";var ao=ul.join(Am.homedir(),m.configDir),lo=ul.join(ao,"config.json");function co(){if(!Fn.existsSync(lo))return{};try{return JSON.parse(Fn.readFileSync(lo,"utf-8"))}catch{return{}}}function pl(e){Fn.existsSync(ao)||Fn.mkdirSync(ao,{recursive:!0});let n={...co(),...e};Fn.writeFileSync(lo,JSON.stringify(n,null,2))}async function ml(){let e=co(),t=await wm({message:"Enter your email:",placeholder:"user@example.com",initialValue:e.lastLoginEmail,validate(r){if(r.length===0)return"Email is required!"}});if(fl(t))return;let n=await Im({message:"Enter your password:",validate(r){if(r.length===0)return"Password is required!"}});if(fl(n))return;let i=vm();i.start("Authenticating...");try{let o=await(await fetch(`${It}/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t,password:n})})).json();o.status==="success"&&o.user.token?(Wo({id:o.user.id,email:t,token:o.user.token,refreshToken:o.user.refreshToken}),pl({lastLoginEmail:t}),i.stop(uo.green("Login successful!"))):i.stop(uo.red("Login failed: "+(o.message||"Unknown error")))}catch(r){i.stop(uo.red("Network error: "+r.message))}}import{log as ki,outro as _m}from"@clack/prompts";import Bn from"chalk";async function gl(){let e=Jo(),t=`${m.envPrefix}TOKEN`;e?ki.success(Bn.green(`Logged out. Removed saved credentials from ~/${m.configDir}/credentials.json.`)):ki.warn(Bn.yellow(`No saved ${m.name} credentials found.`)),process.env[t]&&(ki.warn(Bn.yellow(`${t} is still set in this shell. Commands will continue to authenticate with that token until you unset it.`)),ki.info(Bn.dim(`Run: unset ${t}`))),_m(Bn.blue("---"))}import{outro as $m,select as Rm,spinner as Pm,isCancel as Om}from"@clack/prompts";import Ti from"chalk";import po from"fs";import Dm from"path";var hl=e=>e==="1234567890"||e==="ABC123DEFG"||e==="XXXXXXXXXX"?"":typeof e=="string"?e:"",Cm=()=>process.versions.node.split(".")[0]||"24";async function yl(){if(!Ne()){console.log(Ti.red(`You are not logged in. Run \`${m.nameLower} login\` first.`));return}let t=Pm();t.start("Fetching projects...");try{let{data:n}=await N.get("/projects");if(t.stop("Projects loaded."),n.status!=="success"||!n.projects||n.projects.length===0){console.log(Ti.yellow(`No projects found. Run \`${m.nameLower} init\` to create one.`));return}let i=n.projects.map(g=>({value:g.projectId,label:`${g.name} (${g.slug})`})),r=await Rm({message:"Select a project to link:",options:i});if(Om(r))return;let o=Dm.join(process.cwd(),m.configFileName),s={};if(po.existsSync(o))try{s=JSON.parse(po.readFileSync(o,"utf-8"))}catch{}let l=n.projects.find(g=>g.projectId===r),a=s.android?.package||yt("android"),c=s.ios?.bundleIdentifier||yt("ios"),u=s.android||{},d=s.submit?.production?.android||{},f=s.submit?.production?.ios||{},p={projectId:r,framework:Ve(s.framework)||Zt,name:l?.name,slug:l?.slug,nodeVersion:s.nodeVersion||Cm(),scheme:l?.slug?.toLowerCase()||"app",android:{...u,package:a||"",versionCode:u.versionCode||"auto"},ios:{bundleIdentifier:c||"",buildNumber:s.ios?.buildNumber||"auto"},build:{development:{environment:"development",distribution:"development"},staging:{environment:"staging",distribution:"internal"},production:{environment:"production",distribution:"store"}},submit:{production:{android:{...d,track:d.track||"internal",releaseStatus:d.releaseStatus||"completed"},ios:{ascAppId:hl(f.ascAppId),appleTeamId:hl(f.appleTeamId)}}}};po.writeFileSync(o,JSON.stringify(p,null,2)),$m(Ti.green(`Linked to project ${p.name} (${p.slug})`))}catch(n){t.stop(Ti.red("Error fetching projects: "+n.message))}}import{outro as km,text as Ni,select as Tm,spinner as Nm,isCancel as jn}from"@clack/prompts";import Hn from"chalk";import xm from"fs";import Lm from"path";import{randomUUID as Mm}from"crypto";var Um=()=>process.versions.node.split(".")[0]||"24";async function Sl(){if(!Ne()){console.log(Hn.red(`You are not logged in. Run \`${m.nameLower} login\` first.`));return}let t=await Ni({message:"Project Name:",placeholder:"My Awesome App",validate(l){if(l.length===0)return"Name is required!"}});if(jn(t))return;let n=await Ni({message:"Project Slug (URL friendly):",placeholder:"my-awesome-app",validate(l){if(l.length===0)return"Slug is required!";if(!/^[a-z0-9-]+$/.test(l))return"Slug must be lowercase alphanumeric with dashes."}});if(jn(n))return;let i=await Tm({message:"Platform:",options:[{value:"android",label:"Android"},{value:"ios",label:"iOS"},{value:"web",label:"Web"},{value:"all",label:"All Platforms"}]});if(jn(i))return;let r="";if(i==="android"||i==="all"){let l=yt("android");if(r=await Ni({message:"Android Package Name:",initialValue:l||"",placeholder:"com.example.app",validate(a){if(a.length===0)return"Package name is required for Android!"}}),jn(r))return}let o="";if(i==="ios"||i==="all"){let l=yt("ios");if(o=await Ni({message:"iOS Bundle Identifier:",initialValue:l||"",placeholder:"com.example.app",validate(a){if(a.length===0)return"Bundle ID is required for iOS!"}}),jn(o))return}let s=Nm();s.start("Creating project...");try{let l=Mm(),c={projectId:l,name:t,slug:n,storageType:"managed",platforms:i==="all"?["android","ios","web"]:[i]},{data:u}=await N.post("/projects",c);if(u.status==="success"){s.stop(Hn.green("Project created successfully!"));let d=Lm.join(process.cwd(),m.configFileName);xm.writeFileSync(d,JSON.stringify({projectId:l,framework:Zt,name:t,slug:n,nodeVersion:Um(),...r?{android:{package:r,versionCode:"auto"}}:{},...o?{ios:{bundleIdentifier:o,buildNumber:"auto"}}:{},build:{development:{environment:"development",distribution:"development"},staging:{environment:"staging",distribution:"internal"},production:{environment:"production",distribution:"store"}},submit:{production:{android:{track:"internal",releaseStatus:"completed"},ios:{ascAppId:"",appleTeamId:""}}}},null,2)),km(Hn.green(`Initialized project ${t} and linked locally!`))}else s.stop(Hn.red("creation failed: "+u.message))}catch(l){s.stop(Hn.red("Network error: "+l.message))}}import{spinner as fo,log as Le}from"@clack/prompts";import Gn from"chalk";import xi from"fs";import bl from"path";function mo(e,t,n,i){let r=e?.response?.status,o=e?.response?.data?.message||e?.response?.data?.error,s=i?` ${i}`:"";return r===401?`Not logged in or token expired while trying to ${n}${s}. Run \`${m.nameLower} login\` and try again.`:r===403?[`Permission denied while trying to ${n}${s}.`,o?`API says: ${o}.`:void 0,`Current Mach account/token does not have access to project ${t}.`,`Run \`${m.nameLower} login\` with the correct account, or ask a project owner to grant access.`].filter(Boolean).join(" "):r===404?[`Project or environment endpoint was not found while trying to ${n}${s}.`,`Project ID: ${t}.`,o?`API says: ${o}.`:void 0].filter(Boolean).join(" "):r?`Failed to ${n}${s}: ${o||`API returned HTTP ${r}`}`:`Network error while trying to ${n}${s}: ${e?.message||"Unknown error"}`}function Fm(e){let t=String(e??"");return t===""?'""':/^[A-Za-z0-9_./:@%+-]+$/.test(t)?t:JSON.stringify(t)}function Bm(e,t){return`${[`# Generated by ${m.nameLower} env pull --environment ${t}`,"# Do not commit this file.","",...Object.keys(e).sort((i,r)=>i.localeCompare(r)).map(i=>`${i}=${Fm(e[i])}`)].join(`
1548
1548
  `)}
1549
- `}async function El(e,t,n={}){let i=Ne();if(!i){Le.error(`You are not logged in. Run \`${m.nameLower} login\` first.`);return}let r=bl.join(process.cwd(),m.configFileName);if(!xi.existsSync(r)){Le.error(`No ${m.configFileName} found. Run \`${m.nameLower} init\` or \`${m.nameLower} link\` first.`);return}let o;try{o=JSON.parse(xi.readFileSync(r,"utf-8"))}catch{Le.error(`Failed to parse ${m.configFileName}`);return}let{projectId:s}=o;if(!s){Le.error(`${m.configFileName} is missing projectId.`);return}e==="set"?await jm(s,t,i):e==="list"?await Hm(s,i):e==="pull"||e==="download"||e==="export"?await Gm(s,n):Le.error(`Unknown env action: ${e}. Use 'set', 'list', or 'pull'.`)}async function jm(e,t,n){if(t.length===0){Le.error(`Usage: ${m.nameLower} env set KEY=VALUE [KEY2=VALUE2 ...]`);return}let i=fo();i.start("Setting environment variables...");let r=0,o=0;for(let s of t){let[l,...a]=s.split("="),c=a.join("=");if(!l||c===void 0){Le.warn(`Invalid format for argument: ${s}. Expected KEY=VALUE.`),o++;continue}try{let u={key:l.trim(),value:c,type:"env",environment:"global",visibility:"secret"},{data:d}=await N.post(`/projects/${e}/secrets`,u);d.status==="success"?r++:(Le.error(`Failed to set ${l}: ${d.message}`),o++)}catch(u){Le.error(mo(u,e,"set",l)),o++}}i.stop(`Set ${r} variable(s). ${o>0?`${o} failed.`:""}`)}async function Hm(e,t){let n=fo();n.start("Fetching environment variables...");try{let{data:i}=await N.get(`/projects/${e}/secrets`);if(i.status==="success"){n.stop("Fetched variables.");let r=i.secrets||[];r.length===0?Le.info("No environment variables found for this project."):(Le.info(Gn.bold("Environment Variables (Global & Scoped):")),r.forEach(o=>{let s=o.visibility==="plain"?o.value:"********";console.log(` ${Gn.cyan(o.key)} = ${s} ${Gn.gray(`[${o.environment}]`)}`)}))}else n.stop(Gn.red(`Failed to fetch variables: ${i.message}`))}catch(i){n.stop("Failed to fetch variables."),Le.error(mo(i,e,"list"))}}async function Gm(e,t){let n=(t.environment||"development").trim(),i=t.out||".env.local",r=bl.resolve(process.cwd(),i);if(xi.existsSync(r)&&!t.overwrite){Le.error(`${i} already exists. Re-run with \`--overwrite\` to replace it.`);return}let o=fo();o.start(`Downloading ${n} environment variables...`);try{let{data:s}=await N.get(`/projects/${e}/secrets/export?env=${encodeURIComponent(n)}`);if(s.status!=="success"){o.stop(Gn.red(`Failed to download variables: ${s.message||"Unknown API error"}`));return}let l=s.secrets||{},a=Bm(l,n);xi.writeFileSync(r,a,{encoding:"utf-8",mode:384}),o.stop(`Wrote ${Object.keys(l).length} variable(s) to ${i}.`)}catch(s){o.stop("Failed to download variables."),Le.error(mo(s,e,"pull"))}}import{cancel as Vm,confirm as kl,isCancel as Ui,log as Ke,note as zm,outro as un,select as Km,spinner as Wm,text as Jm}from"@clack/prompts";import tt from"chalk";import ie from"fs";import fe from"path";import{spawnSync as Tl}from"child_process";import yo from"crypto";import Ym from"axios";import wl from"crypto";import Vn from"fs";import zn from"path";var Al=e=>String(e||"").replace(/\\/g,"/").replace(/^\/+/,"");function Il(e,t){let n=zn.join(e,"metadata.json");if(!Vn.existsSync(n))throw new Error(`Expo export did not create ${n}.`);let r=JSON.parse(Vn.readFileSync(n,"utf-8"))?.fileMetadata?.[t],o=Al(r?.bundle);if(!o)throw new Error(`Expo export metadata is missing the ${t} launch bundle.`);let s=new Set,l=(Array.isArray(r?.assets)?r.assets:[]).flatMap(a=>{let c=Al(a?.path);return!c||s.has(c)?[]:(s.add(c),[{path:c,ext:String(a?.ext||"").replace(/^\./,"")||void 0}])});for(let a of[o,...l.map(c=>c.path)])if(!Vn.existsSync(zn.join(e,a)))throw new Error(`Expo export metadata references a missing file: ${a}`);return{bundle:o,assets:l}}function vl(e,t){return[zn.join(e,t.bundle),...t.assets.map(n=>zn.join(e,n.path)),zn.join(e,"metadata.json")]}function go(e){return wl.createHash("md5").update(Vn.readFileSync(e)).digest("hex")}function ho(e){return wl.createHash("sha256").update(Vn.readFileSync(e)).digest("base64url")}function _l(e){let t=String(e||"").replace(/^\./,"").trim().toLowerCase();return t?`.${t}`:void 0}function Li(e,t=!1){if(t)return"application/javascript";switch(String(e||"").replace(/^\./,"").trim().toLowerCase()){case"js":case"bundle":case"hbc":return"application/javascript";case"json":return"application/json";case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"webp":return"image/webp";case"svg":return"image/svg+xml";case"ttf":return"font/ttf";case"otf":return"font/otf";case"woff":return"font/woff";case"woff2":return"font/woff2";case"mp3":return"audio/mpeg";case"m4a":return"audio/mp4";case"aac":return"audio/aac";case"wav":return"audio/wav";case"mp4":return"video/mp4";case"mov":return"video/quicktime";case"pdf":return"application/pdf";default:return"application/octet-stream"}}function $l(e){if(!e||typeof e!="object"||Array.isArray(e))return;let{_internal:t,hooks:n,mods:i,...r}=e;return r}var Re=`${m.npmScope}/mach/expo-plugin`,So=()=>process.stdin.isTTY&&process.stdout.isTTY,Fi=()=>{Vm("Operation cancelled."),process.exit(0)},xe=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0};function pn(e){if(!ie.existsSync(e))return null;try{return JSON.parse(ie.readFileSync(e,"utf-8"))}catch{return null}}function Nl(e,t){ie.writeFileSync(e,`${JSON.stringify(t,null,2)}
1550
- `)}function Kn(){let e=ve();return e?.projectId||(Ke.error(`No ${m.configFileName} found or missing projectId. Run \`${m.nameLower} link\` first.`),process.exit(1)),e}function qm(e){Nl(fe.join(process.cwd(),m.configFileName),e)}function bo(e){let t=Ve(e.framework);if(t)return t;let n=pn(fe.join(process.cwd(),"package.json"))||{},i={...n.dependencies||{},...n.devDependencies||{}};return i.expo?"expo":(i["react-native"],"react-native")}function Eo(e,t){let n=String(e||"").trim().toLowerCase();return n==="expo"||n==="expo-updates"?"expo-updates":n==="react-native"||n==="mach-react-native"||n==="rn"?"mach-react-native":"expo-updates"}function xl(e){if(e==="mach-react-native")throw new Error("mach-react-native is reserved for a future Mach-native OTA client. Use expo-updates for Expo and bare React Native projects.");return e}function Ao(e){let t=String(e||"all").trim().toLowerCase();if(t==="ios")return["ios"];if(t==="android")return["android"];if(t==="all"||t==="both")return["ios","android"];throw new Error("Platform must be ios, android, or all.")}function Xm(e){return ie.existsSync(fe.join(e,"pnpm-lock.yaml"))?{command:"pnpm",args:["add","-D",`${m.npmScope}/mach@latest`],label:`pnpm add -D ${m.npmScope}/mach@latest`}:ie.existsSync(fe.join(e,"yarn.lock"))?{command:"yarn",args:["add","-D",`${m.npmScope}/mach@latest`],label:`yarn add -D ${m.npmScope}/mach@latest`}:ie.existsSync(fe.join(e,"bun.lockb"))||ie.existsSync(fe.join(e,"bun.lock"))?{command:"bun",args:["add","-d",`${m.npmScope}/mach@latest`],label:`bun add -d ${m.npmScope}/mach@latest`}:{command:"npm",args:["install","--save-dev",`${m.npmScope}/mach@latest`],label:`npm install --save-dev ${m.npmScope}/mach@latest`}}function Bi(e){let t=pn(fe.join(e,"package.json"))||{};return!!(t.dependencies?.[`${m.npmScope}/mach`]||t.devDependencies?.[`${m.npmScope}/mach`])}function Mi(e){let t=pn(fe.join(e,"node_modules",m.npmScope,"mach","package.json"));return xe(t?.version)}function Ll(e,t){let n=o=>o.replace(/^v/,"").split(/[.-]/).slice(0,3).map(s=>Number.parseInt(s,10)||0),i=n(e),r=n(t);for(let o=0;o<3;o+=1)if(i[o]!==r[o])return i[o]>r[o]?1:-1;return 0}function Zm(e){let t=Mi(e);return!Bi(e)||!t||Ll(t,Tt)<0}function Qm(e){let t=Xm(e),n=Tl(t.command,t.args,{cwd:e,stdio:"inherit",env:{...process.env}});if(n.error)return{ok:!1,label:t.label,error:n.error.message};if(n.status!==0)return{ok:!1,label:t.label,error:`${t.command} exited with code ${n.status}`};let i=Mi(e),r=!!i&&Ll(i,Tt)>=0;return{ok:Bi(e)&&r,label:t.label,error:r?void 0:`installed ${i||"unknown"}, requires ${Tt} or newer`}}function Rl(e){if(typeof e=="string")return e;if(Array.isArray(e)&&typeof e[0]=="string")return e[0]}function eg(e,t){let n=["app.config.json","app.json"].find(l=>ie.existsSync(fe.join(e,l)));if(!n)return null;let i=fe.join(e,n),r=pn(i);if(!r)return null;let o=r.expo&&typeof r.expo=="object"?r.expo:r;if(t==="react-native"&&n==="app.json"&&(!r.expo||typeof r.expo!="object")){let l=pn(fe.join(e,"package.json"))||{},a=xe(r.displayName)||xe(r.name)||xe(l.name)||"App",c=(xe(l.name)||a).toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/^-+|-+$/g,"")||"app";r.expo={name:a,slug:c,version:xe(l.version)||"1.0.0"},o=r.expo}let s=Array.isArray(o.plugins)?o.plugins:[];return s.some(l=>Rl(l)===Re)||(o.plugins=[...s.filter(l=>{let a=Rl(l);return a!=="./plugins/withMachDeepLinks"&&a!=="plugins/withMachDeepLinks"}),Re],Nl(i,r)),n}function tg(e){let t=["app.config.ts","app.config.js"].find(p=>ie.existsSync(fe.join(e,p)));if(!t)return"missing";let n=fe.join(e,t),i=ie.readFileSync(n,"utf-8");if(i.includes(Re))return"already";let r=/(['"`])(?:\.\/)?plugins\/withMachDeepLinks\1/g;if(r.test(i))return ie.writeFileSync(n,i.replace(r,(p,g)=>`${g}${Re}${g}`)),"inserted";let o=/(^[ \t]*)plugins\s*:\s*\[([\s\S]*?)\]/m.exec(i);if(!o||o.index===void 0)return"manual";let s=i.includes(`\r
1549
+ `}async function El(e,t,n={}){let i=Ne();if(!i){Le.error(`You are not logged in. Run \`${m.nameLower} login\` first.`);return}let r=bl.join(process.cwd(),m.configFileName);if(!xi.existsSync(r)){Le.error(`No ${m.configFileName} found. Run \`${m.nameLower} init\` or \`${m.nameLower} link\` first.`);return}let o;try{o=JSON.parse(xi.readFileSync(r,"utf-8"))}catch{Le.error(`Failed to parse ${m.configFileName}`);return}let{projectId:s}=o;if(!s){Le.error(`${m.configFileName} is missing projectId.`);return}e==="set"?await jm(s,t,i):e==="list"?await Hm(s,i):e==="pull"||e==="download"||e==="export"?await Gm(s,n):Le.error(`Unknown env action: ${e}. Use 'set', 'list', or 'pull'.`)}async function jm(e,t,n){if(t.length===0){Le.error(`Usage: ${m.nameLower} env set KEY=VALUE [KEY2=VALUE2 ...]`);return}let i=fo();i.start("Setting environment variables...");let r=0,o=0;for(let s of t){let[l,...a]=s.split("="),c=a.join("=");if(!l||c===void 0){Le.warn(`Invalid format for argument: ${s}. Expected KEY=VALUE.`),o++;continue}try{let u={key:l.trim(),value:c,type:"env",environment:"global",visibility:"secret"},{data:d}=await N.post(`/projects/${e}/secrets`,u);d.status==="success"?r++:(Le.error(`Failed to set ${l}: ${d.message}`),o++)}catch(u){Le.error(mo(u,e,"set",l)),o++}}i.stop(`Set ${r} variable(s). ${o>0?`${o} failed.`:""}`)}async function Hm(e,t){let n=fo();n.start("Fetching environment variables...");try{let{data:i}=await N.get(`/projects/${e}/secrets`);if(i.status==="success"){n.stop("Fetched variables.");let r=i.secrets||[];r.length===0?Le.info("No environment variables found for this project."):(Le.info(Gn.bold("Environment Variables (Global & Scoped):")),r.forEach(o=>{let s=o.visibility==="plain"?o.value:"********";console.log(` ${Gn.cyan(o.key)} = ${s} ${Gn.gray(`[${o.environment}]`)}`)}))}else n.stop(Gn.red(`Failed to fetch variables: ${i.message}`))}catch(i){n.stop("Failed to fetch variables."),Le.error(mo(i,e,"list"))}}async function Gm(e,t){let n=(t.environment||"development").trim(),i=t.out||".env.local",r=bl.resolve(process.cwd(),i);if(xi.existsSync(r)&&!t.overwrite){Le.error(`${i} already exists. Re-run with \`--overwrite\` to replace it.`);return}let o=fo();o.start(`Downloading ${n} environment variables...`);try{let{data:s}=await N.get(`/projects/${e}/secrets/export?env=${encodeURIComponent(n)}`);if(s.status!=="success"){o.stop(Gn.red(`Failed to download variables: ${s.message||"Unknown API error"}`));return}let l=s.secrets||{},a=Bm(l,n);xi.writeFileSync(r,a,{encoding:"utf-8",mode:384}),o.stop(`Wrote ${Object.keys(l).length} variable(s) to ${i}.`)}catch(s){o.stop("Failed to download variables."),Le.error(mo(s,e,"pull"))}}import{cancel as Vm,confirm as kl,isCancel as Ui,log as Ke,note as zm,outro as un,select as Km,spinner as Wm,text as Jm}from"@clack/prompts";import tt from"chalk";import ie from"fs";import fe from"path";import{spawnSync as Tl}from"child_process";import yo from"crypto";import Ym from"axios";import wl from"crypto";import Vn from"fs";import zn from"path";var Al=e=>String(e||"").replace(/\\/g,"/").replace(/^\/+/,"");function Il(e,t){let n=zn.join(e,"metadata.json");if(!Vn.existsSync(n))throw new Error(`Expo export did not create ${n}.`);let r=JSON.parse(Vn.readFileSync(n,"utf-8"))?.fileMetadata?.[t],o=Al(r?.bundle);if(!o)throw new Error(`Expo export metadata is missing the ${t} launch bundle.`);let s=new Set,l=(Array.isArray(r?.assets)?r.assets:[]).flatMap(a=>{let c=Al(a?.path);return!c||s.has(c)?[]:(s.add(c),[{path:c,ext:String(a?.ext||"").replace(/^\./,"")||void 0}])});for(let a of[o,...l.map(c=>c.path)])if(!Vn.existsSync(zn.join(e,a)))throw new Error(`Expo export metadata references a missing file: ${a}`);return{bundle:o,assets:l}}function vl(e,t){return[zn.join(e,t.bundle),...t.assets.map(n=>zn.join(e,n.path)),zn.join(e,"metadata.json")]}function go(e){return wl.createHash("md5").update(Vn.readFileSync(e)).digest("hex")}function ho(e){return wl.createHash("sha256").update(Vn.readFileSync(e)).digest("base64url")}function _l(e){let t=String(e||"").replace(/^\./,"").trim().toLowerCase();return t?`.${t}`:void 0}function Li(e,t=!1){if(t)return"application/javascript";switch(String(e||"").replace(/^\./,"").trim().toLowerCase()){case"js":case"bundle":case"hbc":return"application/javascript";case"json":return"application/json";case"png":return"image/png";case"jpg":case"jpeg":return"image/jpeg";case"gif":return"image/gif";case"webp":return"image/webp";case"svg":return"image/svg+xml";case"ttf":return"font/ttf";case"otf":return"font/otf";case"woff":return"font/woff";case"woff2":return"font/woff2";case"mp3":return"audio/mpeg";case"m4a":return"audio/mp4";case"aac":return"audio/aac";case"wav":return"audio/wav";case"mp4":return"video/mp4";case"mov":return"video/quicktime";case"pdf":return"application/pdf";default:return"application/octet-stream"}}function $l(e){if(!e||typeof e!="object"||Array.isArray(e))return;let{_internal:t,hooks:n,mods:i,...r}=e;return r}var _e=`${m.npmScope}/mach/expo-plugin`,So=()=>process.stdin.isTTY&&process.stdout.isTTY,Fi=()=>{Vm("Operation cancelled."),process.exit(0)},xe=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0};function pn(e){if(!ie.existsSync(e))return null;try{return JSON.parse(ie.readFileSync(e,"utf-8"))}catch{return null}}function Nl(e,t){ie.writeFileSync(e,`${JSON.stringify(t,null,2)}
1550
+ `)}function Kn(){let e=ve();return e?.projectId||(Ke.error(`No ${m.configFileName} found or missing projectId. Run \`${m.nameLower} link\` first.`),process.exit(1)),e}function qm(e){Nl(fe.join(process.cwd(),m.configFileName),e)}function bo(e){let t=Ve(e.framework);if(t)return t;let n=pn(fe.join(process.cwd(),"package.json"))||{},i={...n.dependencies||{},...n.devDependencies||{}};return i.expo?"expo":(i["react-native"],"react-native")}function Eo(e,t){let n=String(e||"").trim().toLowerCase();return n==="expo"||n==="expo-updates"?"expo-updates":n==="react-native"||n==="mach-react-native"||n==="rn"?"mach-react-native":"expo-updates"}function xl(e){if(e==="mach-react-native")throw new Error("mach-react-native is reserved for a future Mach-native OTA client. Use expo-updates for Expo and bare React Native projects.");return e}function Ao(e){let t=String(e||"all").trim().toLowerCase();if(t==="ios")return["ios"];if(t==="android")return["android"];if(t==="all"||t==="both")return["ios","android"];throw new Error("Platform must be ios, android, or all.")}function Xm(e){return ie.existsSync(fe.join(e,"pnpm-lock.yaml"))?{command:"pnpm",args:["add","-D",`${m.npmScope}/mach@latest`],label:`pnpm add -D ${m.npmScope}/mach@latest`}:ie.existsSync(fe.join(e,"yarn.lock"))?{command:"yarn",args:["add","-D",`${m.npmScope}/mach@latest`],label:`yarn add -D ${m.npmScope}/mach@latest`}:ie.existsSync(fe.join(e,"bun.lockb"))||ie.existsSync(fe.join(e,"bun.lock"))?{command:"bun",args:["add","-d",`${m.npmScope}/mach@latest`],label:`bun add -d ${m.npmScope}/mach@latest`}:{command:"npm",args:["install","--save-dev",`${m.npmScope}/mach@latest`],label:`npm install --save-dev ${m.npmScope}/mach@latest`}}function Bi(e){let t=pn(fe.join(e,"package.json"))||{};return!!(t.dependencies?.[`${m.npmScope}/mach`]||t.devDependencies?.[`${m.npmScope}/mach`])}function Mi(e){let t=pn(fe.join(e,"node_modules",m.npmScope,"mach","package.json"));return xe(t?.version)}function Ll(e,t){let n=o=>o.replace(/^v/,"").split(/[.-]/).slice(0,3).map(s=>Number.parseInt(s,10)||0),i=n(e),r=n(t);for(let o=0;o<3;o+=1)if(i[o]!==r[o])return i[o]>r[o]?1:-1;return 0}function Zm(e){let t=Mi(e);return!Bi(e)||!t||Ll(t,Tt)<0}function Qm(e){let t=Xm(e),n=Tl(t.command,t.args,{cwd:e,stdio:"inherit",env:{...process.env}});if(n.error)return{ok:!1,label:t.label,error:n.error.message};if(n.status!==0)return{ok:!1,label:t.label,error:`${t.command} exited with code ${n.status}`};let i=Mi(e),r=!!i&&Ll(i,Tt)>=0;return{ok:Bi(e)&&r,label:t.label,error:r?void 0:`installed ${i||"unknown"}, requires ${Tt} or newer`}}function Rl(e){if(typeof e=="string")return e;if(Array.isArray(e)&&typeof e[0]=="string")return e[0]}function eg(e,t){let n=["app.config.json","app.json"].find(l=>ie.existsSync(fe.join(e,l)));if(!n)return null;let i=fe.join(e,n),r=pn(i);if(!r)return null;let o=r.expo&&typeof r.expo=="object"?r.expo:r;if(t==="react-native"&&n==="app.json"&&(!r.expo||typeof r.expo!="object")){let l=pn(fe.join(e,"package.json"))||{},a=xe(r.displayName)||xe(r.name)||xe(l.name)||"App",c=(xe(l.name)||a).toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/^-+|-+$/g,"")||"app";r.expo={name:a,slug:c,version:xe(l.version)||"1.0.0"},o=r.expo}let s=Array.isArray(o.plugins)?o.plugins:[];return s.some(l=>Rl(l)===_e)||(o.plugins=[...s.filter(l=>{let a=Rl(l);return a!=="./plugins/withMachDeepLinks"&&a!=="plugins/withMachDeepLinks"}),_e],Nl(i,r)),n}function tg(e){let t=["app.config.ts","app.config.js"].find(p=>ie.existsSync(fe.join(e,p)));if(!t)return"missing";let n=fe.join(e,t),i=ie.readFileSync(n,"utf-8");if(i.includes(_e))return"already";let r=/(['"`])(?:\.\/)?plugins\/withMachDeepLinks\1/g;if(r.test(i))return ie.writeFileSync(n,i.replace(r,(p,g)=>`${g}${_e}${g}`)),"inserted";let o=/(^[ \t]*)plugins\s*:\s*\[([\s\S]*?)\]/m.exec(i);if(!o||o.index===void 0)return"manual";let s=i.includes(`\r
1551
1551
  `)?`\r
1552
1552
  `:`
1553
- `,l=o[1]||"",a=o[2]||"",u=a.match(/\r?\n([ \t]+)\S/)?.[1]||`${l} `,d=a.trim().length===0?`${s}${u}'${Re}',${s}${l}`:a.includes(`
1554
- `)||a.includes("\r")?`${s}${u}'${Re}',${a}`:`${s}${u}'${Re}',${s}${u}${a.trim()}${s}${l}`,f=`${o[0].slice(0,o[0].indexOf("[")+1)}${d}]`;return ie.writeFileSync(n,`${i.slice(0,o.index)}${f}${i.slice(o.index+o[0].length)}`),"inserted"}async function ng(e,t,n){let i=[];if(Zm(e)){let o=Mi(e),s=!!(n.installPlugin||n.yes);if(!s&&!n.skipInstall&&So()){let l=await kl({message:o?`Update the local ${m.npmScope}/mach Expo plugin (${o} \u2192 ${Tt})?`:`Install ${m.npmScope}/mach locally so Expo can use ${Re}?`,initialValue:!0});Ui(l)&&Fi(),s=!!l}if(s&&!n.skipInstall){let l=Qm(e);l.ok?i.push(`Installed a compatible ${Re} with ${l.label}.`):i.push(`Could not install ${m.npmScope}/mach with ${l.label}: ${l.error||"unknown error"}.`)}else i.push(`Install ${m.npmScope}/mach@${Tt} or newer in this app so Expo can resolve ${Re}.`)}else i.push(`Local ${Re} is compatible (${Mi(e)}).`);let r=tg(e);if(r==="already"&&i.push(`Expo config already includes ${Re}.`),r==="inserted"&&i.push(`Expo config updated to include ${Re}.`),r==="manual"&&i.push(`Add "${Re}" to the plugins array in app.config.ts/app.config.js.`),r==="missing"){let o=eg(e,t);o?i.push(`${o} updated to include ${Re}.`):i.push(`Add "${Re}" to your Expo config plugins array.`)}return i}async function Pl(e,t){if(t.skipInstall)return!1;if(t.yes)return!0;if(!So())return!1;let n=await kl({message:e,initialValue:!0});return Ui(n)&&Fi(),!!n}async function ig(e,t,n){let i=[];if(t==="react-native"&&!jt(e,"expo"))if(await Pl("Install Expo Modules required by the Mach OTA runtime? This updates package and native project files.",n)){let o=as(e);if(!o.ok||!jt(e,"expo"))throw new Error(`Expo Modules installation failed: ${o.error||o.command}`);i.push("Installed and configured Expo Modules for the existing React Native project.")}else i.push("Expo Modules are required. Run `npx install-expo-modules@latest`, then run `mach ota setup` again.");if(!jt(e,"expo-updates"))if(await Pl("Install the expo-updates runtime?",n)){let o=ls(e);if(!o.ok||!jt(e,"expo-updates"))throw new Error(`expo-updates installation failed: ${o.error||o.command}`);i.push("Installed the Expo-compatible expo-updates version.")}else i.push("Install expo-updates with `npx expo install expo-updates`, then run `mach ota setup` again.");return i}async function Ol(e,t,n,i,r){if(t)return t;if(r||!So()){if(i)throw new Error(`${e.replace(/:$/,"")} is required.`);return}let o=await Jm({message:e,placeholder:n,validate(s){if(i&&s.trim().length===0)return"Required."}});return Ui(o)&&Fi(),xe(o)}function wo(e,t,n){return Rn(e,{runtimeVersion:t.runtimeVersion,framework:Ve(t.framework)||Ve(e.framework),platform:n})}function rg(e){let t=$n(process.cwd(),e);if(!t)return["Expo config could not be resolved"];let n=[];return t.runtimeVersion!==e.runtimeVersion&&n.push(`runtime is ${t.runtimeVersion||"missing"} (expected ${e.runtimeVersion})`),t.channel!==e.channel&&n.push(`channel is ${t.channel||"missing"} (expected ${e.channel})`),t.url!==e.url&&n.push(`URL is ${t.url||"missing"} (expected ${e.url})`),n}function og(e){let t=rg(e);if(t.length!==0)throw new Error(`Resolved Expo OTA configuration does not match this publish: ${t.join("; ")}. Run \`mach ota setup\` to update ${Re}, then create a new native build if its embedded runtime changes.`)}function sg(e){return yo.createHash("sha256").update(ie.readFileSync(e)).digest("hex")}function ag(e){let t=fe.extname(e).toLowerCase();return t===".json"?"application/json":t===".js"||t===".bundle"||t===".hbc"?"application/javascript":t===".map"?"application/json":t===".png"?"image/png":t===".jpg"||t===".jpeg"?"image/jpeg":t===".webp"?"image/webp":t===".gif"?"image/gif":t===".svg"?"image/svg+xml":t===".ttf"?"font/ttf":t===".otf"?"font/otf":t===".woff"?"font/woff":t===".woff2"?"font/woff2":"application/octet-stream"}function Ml(e){let t=[];if(!ie.existsSync(e))return t;for(let n of ie.readdirSync(e)){let i=fe.join(e,n),r=ie.statSync(i);r.isDirectory()?t.push(...Ml(i)):r.isFile()&&t.push(i)}return t}function lg(e,t){let n=e.filter(i=>{let r=i.toLowerCase();return r.endsWith(".map")?!1:r.endsWith(".js")||r.endsWith(".bundle")||r.endsWith(".hbc")});return n.find(i=>i.toLowerCase().includes(t))||n.sort((i,r)=>ie.statSync(r).size-ie.statSync(i).size)[0]}async function Dl(e){let t=await N.post("/ota/upload-url",{projectId:e.projectId,updateId:e.updateId,platform:e.platform,fileName:e.fileName,contentType:e.contentType});if(t.data.status!=="success")throw new Error(t.data.message||`Could not create upload URL for ${e.fileName}`);let n=ie.statSync(e.filePath).size;return await Ym.put(t.data.uploadUrl,ie.createReadStream(e.filePath),{headers:{"Content-Type":e.contentType,"Content-Length":n},maxBodyLength:1/0,maxContentLength:1/0}),{key:t.data.key,url:t.data.publicUrl,contentType:e.contentType,hash:sg(e.filePath),size:n}}function Ul(e,t,n,i={}){let r=Tl(e,t,{cwd:n,stdio:"inherit",env:{...process.env,CI:process.env.CI||"1",EXPO_NO_TELEMETRY:"1",...i}});if(r.error)throw new Error(r.error.message);if(r.status!==0)throw new Error(`${e} ${t.join(" ")} exited with code ${r.status}`)}function cg(e,t,n,i,r){Ul("npx",["expo","export","--platform",e,"--output-dir",t],process.cwd(),{MACH_OTA_RUNTIME_VERSION:n,MACH_OTA_CHANNEL:i,MACH_OTA_URL:r})}function dg(e,t){let n=e==="ios"?"main.jsbundle":"index.android.bundle",r=["index.js","index.ts","index.tsx"].find(s=>ie.existsSync(fe.join(process.cwd(),s)))||"index.js",o=fe.join(t,"assets");ie.mkdirSync(o,{recursive:!0}),Ul("npx",["react-native","bundle","--platform",e,"--dev","false","--entry-file",r,"--bundle-output",fe.join(t,n),"--assets-dest",o],process.cwd())}async function ug(e){let t=e.config.projectId,n=`ota_${Date.now()}_${e.platform}_${yo.randomUUID().slice(0,8)}`,i=fe.join(process.cwd(),".mach","ota",n,e.platform);ie.rmSync(i,{recursive:!0,force:!0}),ie.mkdirSync(i,{recursive:!0}),e.engine==="expo-updates"?cg(e.platform,i,e.runtimeVersion,e.channel,e.config.ota?.url||""):dg(e.platform,i);let r=e.engine==="expo-updates"?Il(i,e.platform):void 0,o=r?vl(i,r):Ml(i),s=r?fe.join(i,r.bundle):lg(o,e.platform);if(!s)throw new Error(`Could not find a ${e.platform} JS bundle in ${i}.`);let l=new Map((r?.assets||[]).map(S=>[S.path,S])),a=[];for(let S of o){let P=fe.relative(i,S).replace(/\\/g,"/"),v=l.get(P),F=S===s?"application/javascript":v?Li(v.ext):ag(S);a.push({filePath:S,relative:P,uploaded:await Dl({projectId:t,updateId:n,platform:e.platform,filePath:S,fileName:P,contentType:F})})}let c=a.find(S=>S.filePath===s),u=e.engine==="expo-updates"?yo.randomUUID():void 0,d=e.engine==="expo-updates"?$l(readResolvedExpoConfig(process.cwd(),{MACH_OTA_RUNTIME_VERSION:e.runtimeVersion,MACH_OTA_CHANNEL:e.channel,MACH_OTA_URL:e.config.ota?.url||""})):void 0;if(e.engine==="expo-updates"&&(!d||Object.keys(d).length===0))throw new Error("Could not resolve the public Expo config required by the OTA manifest. Run `mach ota verify` and try again.");let f=r?r.assets.map(S=>{let P=a.find(v=>v.relative===S.path);if(!P)throw new Error(`Could not find uploaded Expo asset ${S.path}.`);return{key:go(P.filePath),url:P.uploaded.url,contentType:Li(S.ext),fileExtension:_l(S.ext),hash:ho(P.filePath),size:P.uploaded.size}}):a.filter(S=>S.filePath!==s&&!S.relative.endsWith(".map")).map(S=>({key:S.relative,url:S.uploaded.url,contentType:S.uploaded.contentType,hash:S.uploaded.hash,size:S.uploaded.size})),p=r?{key:go(c.filePath),url:c.uploaded.url,contentType:Li(void 0,!0),hash:ho(c.filePath),size:c.uploaded.size}:{key:fe.basename(c.relative),url:c.uploaded.url,contentType:c.uploaded.contentType,hash:c.uploaded.hash,size:c.uploaded.size},g={id:u||n,createdAt:new Date().toISOString(),runtimeVersion:e.runtimeVersion,launchAsset:p,assets:f,metadata:{projectId:t,channel:e.channel,...e.message?{message:e.message}:{},engine:e.engine},extra:{...d?{expoClient:d}:{},mach:{projectId:t,updateId:n,...u?{expoUpdateId:u}:{},channel:e.channel,platform:e.platform}}},h=fe.join(i,"mach-manifest.json");ie.writeFileSync(h,JSON.stringify(g,null,2));let y=await Dl({projectId:t,updateId:n,platform:e.platform,filePath:h,fileName:"manifest.json",contentType:"application/json"}),E=await N.post("/ota/publish",{projectId:t,updateId:n,expoUpdateId:u,channelName:e.channel,runtimeVersion:e.runtimeVersion,platform:e.platform,manifestKey:y.key,message:e.message,launchAsset:p,assets:f});if(E.data.status!=="success")throw new Error(E.data.message||`Failed to publish ${e.platform} OTA update.`);return E.data.update}async function pg(e){let t=Kn(),n=Ve(e.framework)||bo(t),i;try{i=xl(Eo(e.engine,n))}catch(f){Ke.error(f.message),process.exit(1)}let r=!!e.yes,o=await Ol("OTA channel:",xe(e.channel||e.branch||t.ota?.channel)||"production","production",!0,r),s=`${It.replace(/\/$/,"")}/api/ota/expo/${t.projectId}`,l=xe(e.url||t.ota?.url),a=l?.includes("/api/ota/react-native/")?void 0:l,c=await Ol("Mach OTA update URL:",a||s,s,!0,r);t.framework=n;let u={...t.ota||{},enabled:!0,engine:i,channel:o,url:c,deploymentUrl:c,runtimeVersionPolicy:t.ota?.runtimeVersionPolicy||"appVersion"};delete u.statsUrl,t.ota=u,delete t.update,qm(t);let d=[`${m.configFileName} updated.`];if(d.push(...await ig(process.cwd(),n,e)),d.push(...await ng(process.cwd(),n,e)),n==="react-native"&&jt(process.cwd(),"expo")&&jt(process.cwd(),"expo-updates")&&Bi(process.cwd())){let f=Ao(e.platform||"all"),p=hr(process.cwd()).filter(g=>f.includes(g));p.length===0&&d.push("No matching committed ios/android project was found for native configuration sync.");for(let g of p){let h=wo(t,e,g),E=li({cwd:process.cwd(),platforms:[g],runtimeVersion:h,channel:o,url:c}).find(S=>!S.ok);if(E)throw new Error(`Could not synchronize ${g} Expo Updates configuration: ${E.error||E.command}`);d.push(`Synchronized ${g} native Expo Updates configuration for runtime ${h}.`)}}d.push("Create and install one new native build after initial OTA setup. Existing app binaries keep their embedded update URL."),zm(d.map(f=>`- ${f}`).join(`
1555
- `),"OTA setup"),await Fl({...e,action:"verify"})}async function fg(e){Xt();let t=Kn(),n=Ve(e.framework)||bo(t),i;try{i=xl(Eo(e.engine||t.ota?.engine,n))}catch(l){Ke.error(`${l.message} Run \`mach ota setup\` to migrate this project.`),process.exit(1)}let r=xe(e.channel||e.branch||t.ota?.channel)||"production",o=Ao(e.platform||"all"),s=Wm();s.start(`Publishing OTA update (${o.join("+")}, ${r})`);try{let l=[];for(let a of o){let c=wo(t,e,a);og({platform:a,runtimeVersion:c,channel:r,url:xe(t.ota?.url)||""}),s.message(`Exporting and uploading ${a} OTA bundle for runtime ${c}...`),l.push(await ug({config:t,engine:i,platform:a,channel:r,runtimeVersion:c,message:e.message}))}if(s.stop("OTA update published."),e.json)Ke.info(JSON.stringify({status:"success",updates:l},null,2));else{let a=Array.from(new Set(l.map(c=>c.runtimeVersion).filter(Boolean)));un(tt.green(`\u2713 OTA update live on "${r}" for runtime ${a.join(", ")||"unknown"}.`));for(let c of l)Ke.info(tt.dim(`${c.platform}: ${c.updateId}`))}}catch(l){s.stop("OTA publish failed."),Ke.error(l.response?.data?.message||l.message),process.exit(1)}}async function Fl(e){let t=Kn(),n=Ve(e.framework)||bo(t),i=Eo(e.engine||t.ota?.engine,n),r=[],o=t.ota;if(r.push(o?.enabled?{level:"pass",message:"mach.config.json has ota.enabled."}:{level:"fail",message:"mach.config.json is missing ota.enabled. Run `mach ota setup`."}),r.push(o?.url?{level:"pass",message:`OTA URL configured: ${o.url}`}:{level:"fail",message:"OTA URL is missing. Run `mach ota setup`."}),r.push(o?.channel?{level:"pass",message:`Default channel: ${o.channel}`}:{level:"warn",message:"No default OTA channel configured; publish will use production."}),r.push(o?.runtimeVersionPolicy||o?.runtimeVersion?{level:"pass",message:"Runtime version policy configured."}:{level:"warn",message:"No explicit runtime policy configured; Mach will use Expo runtimeVersion or package.json version."}),i==="expo-updates"){let l=pn(fe.join(process.cwd(),"package.json"))||{},a={...l.dependencies||{},...l.devDependencies||{}};n==="react-native"&&r.push(a.expo?{level:"pass",message:"Expo Modules are installed for the bare React Native project."}:{level:"fail",message:"Expo Modules are missing. Run `mach ota setup` to install the bare React Native OTA runtime."}),r.push(a["expo-updates"]?{level:"pass",message:"expo-updates is installed."}:{level:"fail",message:"expo-updates is missing. Run `mach ota setup`."}),r.push(Bi(process.cwd())?{level:"pass",message:`${m.npmScope}/mach is installed locally for the Expo plugin.`}:{level:"fail",message:`${m.npmScope}/mach is not installed locally; run \`mach ota setup\`.`});let c=Ao(e.platform||"all"),u=xe(e.channel||e.branch||o?.channel)||"production",d=xe(o?.url)||"",f=c.map(g=>{let h=wo(t,e,g);return{platform:g,runtimeVersion:h,delivery:$n(process.cwd(),{platform:g,runtimeVersion:h,channel:u,url:d})}}),p=f[0]?.delivery;if(p){let g=p.expoConfig.updates?.enabled!==!1,h=xe(p.expoConfig.updates?.checkAutomatically)?.toUpperCase()||"ON_LOAD";r.push(p.url===o?.url?{level:"pass",message:"Expo Updates URL resolves to Mach."}:{level:"warn",message:`Expo Updates URL resolves to ${p.url||"none"} instead of ${o?.url||"the Mach OTA URL"}.`}),r.push(p.channel===u?{level:"pass",message:`Expo update channel header resolves to ${u}.`}:{level:"warn",message:`Expo update channel header is ${p.channel||"not set"}; install the latest ${m.npmScope}/mach package and rebuild.`}),r.push(g?{level:"pass",message:"Expo Updates is enabled."}:{level:"fail",message:"Expo config has updates.enabled=false, so this app cannot receive OTA updates."}),r.push(h!=="NEVER"&&h!=="ON_ERROR_RECOVERY"?{level:"pass",message:`Expo checks for updates automatically (${h}).`}:{level:"fail",message:`Expo checkAutomatically is ${h}; use ON_LOAD to receive Mach updates when the app starts.`})}else r.push({level:"fail",message:`Could not resolve Expo config. Confirm ${Re} is present and run \`mach ota setup\` again.`});for(let{platform:g,runtimeVersion:h,delivery:y}of f)r.push(y?.runtimeVersion===h?{level:"pass",message:`${g} Expo runtime resolves to ${h}.`}:{level:"fail",message:`${g} Expo runtime resolves to ${y?.runtimeVersion||"none"} instead of ${h}. Run \`mach ota setup\`, then rebuild the app.`});if(n==="react-native"){let g=c,h=hr(process.cwd()).filter(E=>g.includes(E)),y=cs(process.cwd());if(h.includes("android")){let E=!!(y.androidManifest&&y.androidStrings)&&ie.readFileSync(y.androidManifest,"utf-8").includes("expo.modules.updates.EXPO_UPDATE_URL")&&ie.readFileSync(y.androidStrings,"utf-8").includes("expo_runtime_version");r.push(E?{level:"pass",message:"Android native Expo Updates configuration is synchronized."}:{level:"fail",message:"Android native Expo Updates configuration is missing. Run `mach ota setup --platform android`."})}if(h.includes("ios")){let E=!!y.iosExpoPlist&&ie.readFileSync(y.iosExpoPlist,"utf-8").includes("EXUpdatesURL")&&ie.readFileSync(y.iosExpoPlist,"utf-8").includes("EXUpdatesRuntimeVersion");r.push(E?{level:"pass",message:"iOS native Expo Updates configuration is synchronized."}:{level:"fail",message:"iOS native Expo Updates configuration is missing. Run `mach ota setup --platform ios`."})}}}else r.push({level:"fail",message:"mach-react-native is not available yet. Run `mach ota setup` to migrate this project to expo-updates."});for(let l of r){let a=l.level==="pass"?tt.green("PASS"):l.level==="warn"?tt.yellow("WARN"):tt.red("FAIL");Ke.info(`${a} ${l.message}`)}let s=r.filter(l=>l.level==="fail").length;s>0&&(un(tt.red(`OTA verification failed with ${s} issue(s).`)),process.exit(1)),un(tt.green("OTA verification passed."))}async function mg(e){Xt();let t=Kn(),n=await N.get(`/ota/${t.projectId}/updates`),i=n.data.updates||[];if(e.json){Ke.info(JSON.stringify(n.data,null,2));return}if(i.length===0){un("No OTA updates found.");return}for(let r of i){let o=r.stats?.total||{};Ke.info(`${tt.bold(r.updateId)} ${tt.dim(`${r.platform} ${r.channelName} runtime ${r.runtimeVersion}`)} ${r.status||"active"}`),Ke.info(tt.dim(` downloads ios=${o.iosDownloaded||0} android=${o.androidDownloaded||0} applied ios=${o.iosApplied||0} android=${o.androidApplied||0}`))}un(`Found ${i.length} OTA update(s).`)}async function Cl(e,t){Xt();let n=Kn(),i=xe(t.updateId);i||(Ke.error(`Missing update id. Use \`${m.nameLower} ota ${e} --update-id <id>\`.`),process.exit(1));let r=await N.post(`/ota/${n.projectId}/updates/${encodeURIComponent(i)}/${e}`);t.json?Ke.info(JSON.stringify(r.data,null,2)):un(tt.green(`\u2713 OTA update ${e==="rollback"?"rolled back":"promoted"}: ${i}`))}async function Nt(e,t={}){let n=e||t.action||"publish";if(n==="setup")return pg({...t,action:n});if(n==="publish")return fg({...t,action:n});if(n==="verify")return Fl({...t,action:n});if(n==="list")return mg({...t,action:n});if(n==="rollback")return Cl("rollback",{...t,action:n});if(n==="promote")return Cl("promote",{...t,action:n});let i=await Km({message:"What do you want to do?",options:[{value:"setup",label:"Setup OTA"},{value:"publish",label:"Publish OTA update"},{value:"verify",label:"Verify OTA setup"},{value:"list",label:"List OTA updates"}]});return Ui(i)&&Fi(),Nt(i,t)}import{intro as gg,outro as Bl,log as fn}from"@clack/prompts";import ct from"chalk";async function jl(){gg(ct.blue(`${m.name} CLI - User Profile`));let e=Ne();if(!e){fn.warn(ct.yellow("You are not logged in.")),fn.info(`Run \`${m.nameLower} login\` to authenticate.`),Bl();return}fn.info(`${ct.bold("Logged in as:")} ${ct.green(e.email)}`);try{await N.get("/projects");let n=Ne().token.split(".");if(n.length===3){let i=JSON.parse(Buffer.from(n[1],"base64").toString());if(i.exp){let r=new Date(i.exp*1e3),o=new Date,s=r.getTime()-o.getTime();if(s>0){let l=Math.round(s/6e4),a=(l/60).toFixed(1);fn.info(`${ct.bold("Token status:")} Valid (Expires in ~${a}h / ${l}m)`)}else fn.warn(`${ct.bold("Token status:")} ${ct.red("Expired")} (Auto-refresh failed)`)}}}catch{fn.warn(`${ct.bold("Token status:")} ${ct.red("Unreachable")} (Session might be truly expired)${It}`)}Bl(ct.blue("---"))}import{spawn as Sg}from"child_process";import{log as xt}from"@clack/prompts";import We from"chalk";import bg from"fs";import hg from"net";async function yg(e){return new Promise(t=>{let n=hg.createServer();n.once("error",i=>{i.code==="EADDRINUSE"?t(!0):t(!1)}),n.once("listening",()=>{n.close(),t(!1)}),n.listen(e,"0.0.0.0")})}async function Hl(e,t=20){let n=e;for(;n<e+t;){if(!await yg(n))return n;n++}throw new Error(`Could not find an available port after ${t} attempts starting from ${e}.`)}var Gl=async e=>{xt.info(We.blue(`Starting ${m.name} Dev Orchestrator...`));let t=ve(),n=e.buildProfile||"development",i=t?Me(t,n):null,r={...process.env};if(t?.projectId)try{xt.info(We.dim(`Fetching managed secrets for profile: ${n}...`));let d=await N.get(`/projects/${t.projectId}/secrets/export?env=${n}`);d.data?.status==="success"&&d.data.secrets&&(Object.assign(r,d.data.secrets),xt.info(We.magenta(`\u2713 Injected secrets from ${m.name} Dashboard.`)))}catch{xt.warn("Could not fetch managed secrets. Running with local env only.")}t&&Object.assign(r,Qt(t,m.envPrefix)),i&&(Object.assign(r,Qt(i,`${m.envPrefix}PROFILE_`)),i.env&&Object.assign(r,i.env));let o=!1;try{let d=JSON.parse(bg.readFileSync("package.json","utf-8"));o=!!(d.dependencies?.expo||d.devDependencies?.expo)}catch{xt.warn("Could not parse package.json. Defaulting to Bare React Native mode.")}let s=Number(e.port||i?.port||8081),l=await Hl(s);l!==s&&xt.info(We.yellow(`Port ${s} is busy. ${m.name} is using ${l} instead.`));let a=process.platform==="win32"?"npx.cmd":"npx",c=[];if(o?(c=["expo","start","--port",String(l)],e.clear&&c.push("--clear"),e.tunnel&&c.push("--tunnel"),e.lan&&c.push("--lan"),e.localhost&&c.push("--localhost"),e.ios&&c.push("--ios"),e.android&&c.push("--android"),e.web&&c.push("--web")):(c=["react-native","start","--port",String(l)],e.clear&&c.push("--clear")),o){let d=[`${We.cyan("i")} open iOS simulator`,`${We.cyan("a")} open Android emulator/device`,`${We.cyan("w")} open web`,`${We.cyan("r")} reload app`];xt.info([We.bold("Launch controls"),...d.map(f=>` ${f}`),"",We.dim("Direct launch: mach start --ios | --android | --web"),We.dim(`Metro URL: http://localhost:${l}`)].join(`
1556
- `))}else xt.info(We.dim(`Metro URL: http://localhost:${l}`));let u=Sg(a,c,{stdio:"inherit",env:r});return new Promise((d,f)=>{u.on("exit",p=>{p===0?d():f(new Error(`Dev server exited with code ${p}`))}),u.on("error",p=>{f(p)})})};import{log as je}from"@clack/prompts";import nt from"chalk";var Vl=async e=>{let t=ve();t||(je.error(nt.red(`No ${m.configFileName} found in the current directory.`)),process.exit(1));let n=e.profile||"development",i=Me(t,n);if(e.json){console.log(JSON.stringify({project:t,resolved:i},null,2));return}je.info(nt.bgBlue.white(` Resolved ${m.name} Config [${n}] `)),je.info(nt.dim("----------------------------------------")),je.info(`${nt.blue("Project ID:")} ${t.projectId}`),je.info(`${nt.blue("Name:")} ${t.name}`),je.info(`${nt.blue("Scheme:")} ${t.scheme}`),i?(je.info(nt.dim(`
1553
+ `,l=o[1]||"",a=o[2]||"",u=a.match(/\r?\n([ \t]+)\S/)?.[1]||`${l} `,d=a.trim().length===0?`${s}${u}'${_e}',${s}${l}`:a.includes(`
1554
+ `)||a.includes("\r")?`${s}${u}'${_e}',${a}`:`${s}${u}'${_e}',${s}${u}${a.trim()}${s}${l}`,f=`${o[0].slice(0,o[0].indexOf("[")+1)}${d}]`;return ie.writeFileSync(n,`${i.slice(0,o.index)}${f}${i.slice(o.index+o[0].length)}`),"inserted"}async function ng(e,t,n){let i=[];if(Zm(e)){let o=Mi(e),s=!!(n.installPlugin||n.yes);if(!s&&!n.skipInstall&&So()){let l=await kl({message:o?`Update the local ${m.npmScope}/mach Expo plugin (${o} \u2192 ${Tt})?`:`Install ${m.npmScope}/mach locally so Expo can use ${_e}?`,initialValue:!0});Ui(l)&&Fi(),s=!!l}if(s&&!n.skipInstall){let l=Qm(e);l.ok?i.push(`Installed a compatible ${_e} with ${l.label}.`):i.push(`Could not install ${m.npmScope}/mach with ${l.label}: ${l.error||"unknown error"}.`)}else i.push(`Install ${m.npmScope}/mach@${Tt} or newer in this app so Expo can resolve ${_e}.`)}else i.push(`Local ${_e} is compatible (${Mi(e)}).`);let r=tg(e);if(r==="already"&&i.push(`Expo config already includes ${_e}.`),r==="inserted"&&i.push(`Expo config updated to include ${_e}.`),r==="manual"&&i.push(`Add "${_e}" to the plugins array in app.config.ts/app.config.js.`),r==="missing"){let o=eg(e,t);o?i.push(`${o} updated to include ${_e}.`):i.push(`Add "${_e}" to your Expo config plugins array.`)}return i}async function Pl(e,t){if(t.skipInstall)return!1;if(t.yes)return!0;if(!So())return!1;let n=await kl({message:e,initialValue:!0});return Ui(n)&&Fi(),!!n}async function ig(e,t,n){let i=[];if(t==="react-native"&&!jt(e,"expo"))if(await Pl("Install Expo Modules required by the Mach OTA runtime? This updates package and native project files.",n)){let o=as(e);if(!o.ok||!jt(e,"expo"))throw new Error(`Expo Modules installation failed: ${o.error||o.command}`);i.push("Installed and configured Expo Modules for the existing React Native project.")}else i.push("Expo Modules are required. Run `npx install-expo-modules@latest`, then run `mach ota setup` again.");if(!jt(e,"expo-updates"))if(await Pl("Install the expo-updates runtime?",n)){let o=ls(e);if(!o.ok||!jt(e,"expo-updates"))throw new Error(`expo-updates installation failed: ${o.error||o.command}`);i.push("Installed the Expo-compatible expo-updates version.")}else i.push("Install expo-updates with `npx expo install expo-updates`, then run `mach ota setup` again.");return i}async function Ol(e,t,n,i,r){if(t)return t;if(r||!So()){if(i)throw new Error(`${e.replace(/:$/,"")} is required.`);return}let o=await Jm({message:e,placeholder:n,validate(s){if(i&&s.trim().length===0)return"Required."}});return Ui(o)&&Fi(),xe(o)}function wo(e,t,n){return Rn(e,{runtimeVersion:t.runtimeVersion,framework:Ve(t.framework)||Ve(e.framework),platform:n})}function rg(e){let t=$n(process.cwd(),e);if(!t)throw new Error(`Resolved Expo OTA configuration does not match this publish: Expo config could not be resolved. Run \`mach ota setup\` to update ${_e}, then create a new native build if its embedded runtime changes.`);let n=[];if(t.runtimeVersion!==e.runtimeVersion&&n.push(`runtime is ${t.runtimeVersion||"missing"} (expected ${e.runtimeVersion})`),t.channel!==e.channel&&n.push(`channel is ${t.channel||"missing"} (expected ${e.channel})`),t.url!==e.url&&n.push(`URL is ${t.url||"missing"} (expected ${e.url})`),n.length>0)throw new Error(`Resolved Expo OTA configuration does not match this publish: ${n.join("; ")}. Run \`mach ota setup\` to update ${_e}, then create a new native build if its embedded runtime changes.`);return t}function og(e){return yo.createHash("sha256").update(ie.readFileSync(e)).digest("hex")}function sg(e){let t=fe.extname(e).toLowerCase();return t===".json"?"application/json":t===".js"||t===".bundle"||t===".hbc"?"application/javascript":t===".map"?"application/json":t===".png"?"image/png":t===".jpg"||t===".jpeg"?"image/jpeg":t===".webp"?"image/webp":t===".gif"?"image/gif":t===".svg"?"image/svg+xml":t===".ttf"?"font/ttf":t===".otf"?"font/otf":t===".woff"?"font/woff":t===".woff2"?"font/woff2":"application/octet-stream"}function Ml(e){let t=[];if(!ie.existsSync(e))return t;for(let n of ie.readdirSync(e)){let i=fe.join(e,n),r=ie.statSync(i);r.isDirectory()?t.push(...Ml(i)):r.isFile()&&t.push(i)}return t}function ag(e,t){let n=e.filter(i=>{let r=i.toLowerCase();return r.endsWith(".map")?!1:r.endsWith(".js")||r.endsWith(".bundle")||r.endsWith(".hbc")});return n.find(i=>i.toLowerCase().includes(t))||n.sort((i,r)=>ie.statSync(r).size-ie.statSync(i).size)[0]}async function Dl(e){let t=await N.post("/ota/upload-url",{projectId:e.projectId,updateId:e.updateId,platform:e.platform,fileName:e.fileName,contentType:e.contentType});if(t.data.status!=="success")throw new Error(t.data.message||`Could not create upload URL for ${e.fileName}`);let n=ie.statSync(e.filePath).size;return await Ym.put(t.data.uploadUrl,ie.createReadStream(e.filePath),{headers:{"Content-Type":e.contentType,"Content-Length":n},maxBodyLength:1/0,maxContentLength:1/0}),{key:t.data.key,url:t.data.publicUrl,contentType:e.contentType,hash:og(e.filePath),size:n}}function Ul(e,t,n,i={}){let r=Tl(e,t,{cwd:n,stdio:"inherit",env:{...process.env,CI:process.env.CI||"1",EXPO_NO_TELEMETRY:"1",...i}});if(r.error)throw new Error(r.error.message);if(r.status!==0)throw new Error(`${e} ${t.join(" ")} exited with code ${r.status}`)}function lg(e,t,n,i,r){Ul("npx",["expo","export","--platform",e,"--output-dir",t],process.cwd(),{MACH_OTA_RUNTIME_VERSION:n,MACH_OTA_CHANNEL:i,MACH_OTA_URL:r})}function cg(e,t){let n=e==="ios"?"main.jsbundle":"index.android.bundle",r=["index.js","index.ts","index.tsx"].find(s=>ie.existsSync(fe.join(process.cwd(),s)))||"index.js",o=fe.join(t,"assets");ie.mkdirSync(o,{recursive:!0}),Ul("npx",["react-native","bundle","--platform",e,"--dev","false","--entry-file",r,"--bundle-output",fe.join(t,n),"--assets-dest",o],process.cwd())}async function dg(e){let t=e.config.projectId,n=`ota_${Date.now()}_${e.platform}_${yo.randomUUID().slice(0,8)}`,i=fe.join(process.cwd(),".mach","ota",n,e.platform);ie.rmSync(i,{recursive:!0,force:!0}),ie.mkdirSync(i,{recursive:!0}),e.engine==="expo-updates"?lg(e.platform,i,e.runtimeVersion,e.channel,e.config.ota?.url||""):cg(e.platform,i);let r=e.engine==="expo-updates"?Il(i,e.platform):void 0,o=r?vl(i,r):Ml(i),s=r?fe.join(i,r.bundle):ag(o,e.platform);if(!s)throw new Error(`Could not find a ${e.platform} JS bundle in ${i}.`);let l=new Map((r?.assets||[]).map(S=>[S.path,S])),a=[];for(let S of o){let P=fe.relative(i,S).replace(/\\/g,"/"),v=l.get(P),F=S===s?"application/javascript":v?Li(v.ext):sg(S);a.push({filePath:S,relative:P,uploaded:await Dl({projectId:t,updateId:n,platform:e.platform,filePath:S,fileName:P,contentType:F})})}let c=a.find(S=>S.filePath===s),u=e.engine==="expo-updates"?yo.randomUUID():void 0,d=e.engine==="expo-updates"?$l(e.expoConfig):void 0;if(e.engine==="expo-updates"&&(!d||Object.keys(d).length===0))throw new Error("Could not resolve the public Expo config required by the OTA manifest. Run `mach ota verify` and try again.");let f=r?r.assets.map(S=>{let P=a.find(v=>v.relative===S.path);if(!P)throw new Error(`Could not find uploaded Expo asset ${S.path}.`);return{key:go(P.filePath),url:P.uploaded.url,contentType:Li(S.ext),fileExtension:_l(S.ext),hash:ho(P.filePath),size:P.uploaded.size}}):a.filter(S=>S.filePath!==s&&!S.relative.endsWith(".map")).map(S=>({key:S.relative,url:S.uploaded.url,contentType:S.uploaded.contentType,hash:S.uploaded.hash,size:S.uploaded.size})),p=r?{key:go(c.filePath),url:c.uploaded.url,contentType:Li(void 0,!0),hash:ho(c.filePath),size:c.uploaded.size}:{key:fe.basename(c.relative),url:c.uploaded.url,contentType:c.uploaded.contentType,hash:c.uploaded.hash,size:c.uploaded.size},g={id:u||n,createdAt:new Date().toISOString(),runtimeVersion:e.runtimeVersion,launchAsset:p,assets:f,metadata:{projectId:t,channel:e.channel,...e.message?{message:e.message}:{},engine:e.engine},extra:{...d?{expoClient:d}:{},mach:{projectId:t,updateId:n,...u?{expoUpdateId:u}:{},channel:e.channel,platform:e.platform}}},h=fe.join(i,"mach-manifest.json");ie.writeFileSync(h,JSON.stringify(g,null,2));let y=await Dl({projectId:t,updateId:n,platform:e.platform,filePath:h,fileName:"manifest.json",contentType:"application/json"}),E=await N.post("/ota/publish",{projectId:t,updateId:n,expoUpdateId:u,channelName:e.channel,runtimeVersion:e.runtimeVersion,platform:e.platform,manifestKey:y.key,message:e.message,launchAsset:p,assets:f});if(E.data.status!=="success")throw new Error(E.data.message||`Failed to publish ${e.platform} OTA update.`);return E.data.update}async function ug(e){let t=Kn(),n=Ve(e.framework)||bo(t),i;try{i=xl(Eo(e.engine,n))}catch(f){Ke.error(f.message),process.exit(1)}let r=!!e.yes,o=await Ol("OTA channel:",xe(e.channel||e.branch||t.ota?.channel)||"production","production",!0,r),s=`${It.replace(/\/$/,"")}/api/ota/expo/${t.projectId}`,l=xe(e.url||t.ota?.url),a=l?.includes("/api/ota/react-native/")?void 0:l,c=await Ol("Mach OTA update URL:",a||s,s,!0,r);t.framework=n;let u={...t.ota||{},enabled:!0,engine:i,channel:o,url:c,deploymentUrl:c,runtimeVersionPolicy:t.ota?.runtimeVersionPolicy||"appVersion"};delete u.statsUrl,t.ota=u,delete t.update,qm(t);let d=[`${m.configFileName} updated.`];if(d.push(...await ig(process.cwd(),n,e)),d.push(...await ng(process.cwd(),n,e)),n==="react-native"&&jt(process.cwd(),"expo")&&jt(process.cwd(),"expo-updates")&&Bi(process.cwd())){let f=Ao(e.platform||"all"),p=hr(process.cwd()).filter(g=>f.includes(g));p.length===0&&d.push("No matching committed ios/android project was found for native configuration sync.");for(let g of p){let h=wo(t,e,g),E=li({cwd:process.cwd(),platforms:[g],runtimeVersion:h,channel:o,url:c}).find(S=>!S.ok);if(E)throw new Error(`Could not synchronize ${g} Expo Updates configuration: ${E.error||E.command}`);d.push(`Synchronized ${g} native Expo Updates configuration for runtime ${h}.`)}}d.push("Create and install one new native build after initial OTA setup. Existing app binaries keep their embedded update URL."),zm(d.map(f=>`- ${f}`).join(`
1555
+ `),"OTA setup"),await Fl({...e,action:"verify"})}async function pg(e){Xt();let t=Kn(),n=Ve(e.framework)||bo(t),i;try{i=xl(Eo(e.engine||t.ota?.engine,n))}catch(l){Ke.error(`${l.message} Run \`mach ota setup\` to migrate this project.`),process.exit(1)}let r=xe(e.channel||e.branch||t.ota?.channel)||"production",o=Ao(e.platform||"all"),s=Wm();s.start(`Publishing OTA update (${o.join("+")}, ${r})`);try{let l=[];for(let a of o){let c=wo(t,e,a),u=rg({platform:a,runtimeVersion:c,channel:r,url:xe(t.ota?.url)||""});s.message(`Exporting and uploading ${a} OTA bundle for runtime ${c}...`),l.push(await dg({config:t,engine:i,platform:a,channel:r,runtimeVersion:c,message:e.message,expoConfig:u.expoConfig}))}if(s.stop("OTA update published."),e.json)Ke.info(JSON.stringify({status:"success",updates:l},null,2));else{let a=Array.from(new Set(l.map(c=>c.runtimeVersion).filter(Boolean)));un(tt.green(`\u2713 OTA update live on "${r}" for runtime ${a.join(", ")||"unknown"}.`));for(let c of l)Ke.info(tt.dim(`${c.platform}: ${c.updateId}`))}}catch(l){s.stop("OTA publish failed."),Ke.error(l.response?.data?.message||l.message),process.exit(1)}}async function Fl(e){let t=Kn(),n=Ve(e.framework)||bo(t),i=Eo(e.engine||t.ota?.engine,n),r=[],o=t.ota;if(r.push(o?.enabled?{level:"pass",message:"mach.config.json has ota.enabled."}:{level:"fail",message:"mach.config.json is missing ota.enabled. Run `mach ota setup`."}),r.push(o?.url?{level:"pass",message:`OTA URL configured: ${o.url}`}:{level:"fail",message:"OTA URL is missing. Run `mach ota setup`."}),r.push(o?.channel?{level:"pass",message:`Default channel: ${o.channel}`}:{level:"warn",message:"No default OTA channel configured; publish will use production."}),r.push(o?.runtimeVersionPolicy||o?.runtimeVersion?{level:"pass",message:"Runtime version policy configured."}:{level:"warn",message:"No explicit runtime policy configured; Mach will use Expo runtimeVersion or package.json version."}),i==="expo-updates"){let l=pn(fe.join(process.cwd(),"package.json"))||{},a={...l.dependencies||{},...l.devDependencies||{}};n==="react-native"&&r.push(a.expo?{level:"pass",message:"Expo Modules are installed for the bare React Native project."}:{level:"fail",message:"Expo Modules are missing. Run `mach ota setup` to install the bare React Native OTA runtime."}),r.push(a["expo-updates"]?{level:"pass",message:"expo-updates is installed."}:{level:"fail",message:"expo-updates is missing. Run `mach ota setup`."}),r.push(Bi(process.cwd())?{level:"pass",message:`${m.npmScope}/mach is installed locally for the Expo plugin.`}:{level:"fail",message:`${m.npmScope}/mach is not installed locally; run \`mach ota setup\`.`});let c=Ao(e.platform||"all"),u=xe(e.channel||e.branch||o?.channel)||"production",d=xe(o?.url)||"",f=c.map(g=>{let h=wo(t,e,g);return{platform:g,runtimeVersion:h,delivery:$n(process.cwd(),{platform:g,runtimeVersion:h,channel:u,url:d})}}),p=f[0]?.delivery;if(p){let g=p.expoConfig.updates?.enabled!==!1,h=xe(p.expoConfig.updates?.checkAutomatically)?.toUpperCase()||"ON_LOAD";r.push(p.url===o?.url?{level:"pass",message:"Expo Updates URL resolves to Mach."}:{level:"warn",message:`Expo Updates URL resolves to ${p.url||"none"} instead of ${o?.url||"the Mach OTA URL"}.`}),r.push(p.channel===u?{level:"pass",message:`Expo update channel header resolves to ${u}.`}:{level:"warn",message:`Expo update channel header is ${p.channel||"not set"}; install the latest ${m.npmScope}/mach package and rebuild.`}),r.push(g?{level:"pass",message:"Expo Updates is enabled."}:{level:"fail",message:"Expo config has updates.enabled=false, so this app cannot receive OTA updates."}),r.push(h!=="NEVER"&&h!=="ON_ERROR_RECOVERY"?{level:"pass",message:`Expo checks for updates automatically (${h}).`}:{level:"fail",message:`Expo checkAutomatically is ${h}; use ON_LOAD to receive Mach updates when the app starts.`})}else r.push({level:"fail",message:`Could not resolve Expo config. Confirm ${_e} is present and run \`mach ota setup\` again.`});for(let{platform:g,runtimeVersion:h,delivery:y}of f)r.push(y?.runtimeVersion===h?{level:"pass",message:`${g} Expo runtime resolves to ${h}.`}:{level:"fail",message:`${g} Expo runtime resolves to ${y?.runtimeVersion||"none"} instead of ${h}. Run \`mach ota setup\`, then rebuild the app.`});if(n==="react-native"){let g=c,h=hr(process.cwd()).filter(E=>g.includes(E)),y=cs(process.cwd());if(h.includes("android")){let E=!!(y.androidManifest&&y.androidStrings)&&ie.readFileSync(y.androidManifest,"utf-8").includes("expo.modules.updates.EXPO_UPDATE_URL")&&ie.readFileSync(y.androidStrings,"utf-8").includes("expo_runtime_version");r.push(E?{level:"pass",message:"Android native Expo Updates configuration is synchronized."}:{level:"fail",message:"Android native Expo Updates configuration is missing. Run `mach ota setup --platform android`."})}if(h.includes("ios")){let E=!!y.iosExpoPlist&&ie.readFileSync(y.iosExpoPlist,"utf-8").includes("EXUpdatesURL")&&ie.readFileSync(y.iosExpoPlist,"utf-8").includes("EXUpdatesRuntimeVersion");r.push(E?{level:"pass",message:"iOS native Expo Updates configuration is synchronized."}:{level:"fail",message:"iOS native Expo Updates configuration is missing. Run `mach ota setup --platform ios`."})}}}else r.push({level:"fail",message:"mach-react-native is not available yet. Run `mach ota setup` to migrate this project to expo-updates."});for(let l of r){let a=l.level==="pass"?tt.green("PASS"):l.level==="warn"?tt.yellow("WARN"):tt.red("FAIL");Ke.info(`${a} ${l.message}`)}let s=r.filter(l=>l.level==="fail").length;s>0&&(un(tt.red(`OTA verification failed with ${s} issue(s).`)),process.exit(1)),un(tt.green("OTA verification passed."))}async function fg(e){Xt();let t=Kn(),n=await N.get(`/ota/${t.projectId}/updates`),i=n.data.updates||[];if(e.json){Ke.info(JSON.stringify(n.data,null,2));return}if(i.length===0){un("No OTA updates found.");return}for(let r of i){let o=r.stats?.total||{};Ke.info(`${tt.bold(r.updateId)} ${tt.dim(`${r.platform} ${r.channelName} runtime ${r.runtimeVersion}`)} ${r.status||"active"}`),Ke.info(tt.dim(` downloads ios=${o.iosDownloaded||0} android=${o.androidDownloaded||0} applied ios=${o.iosApplied||0} android=${o.androidApplied||0}`))}un(`Found ${i.length} OTA update(s).`)}async function Cl(e,t){Xt();let n=Kn(),i=xe(t.updateId);i||(Ke.error(`Missing update id. Use \`${m.nameLower} ota ${e} --update-id <id>\`.`),process.exit(1));let r=await N.post(`/ota/${n.projectId}/updates/${encodeURIComponent(i)}/${e}`);t.json?Ke.info(JSON.stringify(r.data,null,2)):un(tt.green(`\u2713 OTA update ${e==="rollback"?"rolled back":"promoted"}: ${i}`))}async function Nt(e,t={}){let n=e||t.action||"publish";if(n==="setup")return ug({...t,action:n});if(n==="publish")return pg({...t,action:n});if(n==="verify")return Fl({...t,action:n});if(n==="list")return fg({...t,action:n});if(n==="rollback")return Cl("rollback",{...t,action:n});if(n==="promote")return Cl("promote",{...t,action:n});let i=await Km({message:"What do you want to do?",options:[{value:"setup",label:"Setup OTA"},{value:"publish",label:"Publish OTA update"},{value:"verify",label:"Verify OTA setup"},{value:"list",label:"List OTA updates"}]});return Ui(i)&&Fi(),Nt(i,t)}import{intro as mg,outro as Bl,log as fn}from"@clack/prompts";import ct from"chalk";async function jl(){mg(ct.blue(`${m.name} CLI - User Profile`));let e=Ne();if(!e){fn.warn(ct.yellow("You are not logged in.")),fn.info(`Run \`${m.nameLower} login\` to authenticate.`),Bl();return}fn.info(`${ct.bold("Logged in as:")} ${ct.green(e.email)}`);try{await N.get("/projects");let n=Ne().token.split(".");if(n.length===3){let i=JSON.parse(Buffer.from(n[1],"base64").toString());if(i.exp){let r=new Date(i.exp*1e3),o=new Date,s=r.getTime()-o.getTime();if(s>0){let l=Math.round(s/6e4),a=(l/60).toFixed(1);fn.info(`${ct.bold("Token status:")} Valid (Expires in ~${a}h / ${l}m)`)}else fn.warn(`${ct.bold("Token status:")} ${ct.red("Expired")} (Auto-refresh failed)`)}}}catch{fn.warn(`${ct.bold("Token status:")} ${ct.red("Unreachable")} (Session might be truly expired)${It}`)}Bl(ct.blue("---"))}import{spawn as yg}from"child_process";import{log as xt}from"@clack/prompts";import We from"chalk";import Sg from"fs";import gg from"net";async function hg(e){return new Promise(t=>{let n=gg.createServer();n.once("error",i=>{i.code==="EADDRINUSE"?t(!0):t(!1)}),n.once("listening",()=>{n.close(),t(!1)}),n.listen(e,"0.0.0.0")})}async function Hl(e,t=20){let n=e;for(;n<e+t;){if(!await hg(n))return n;n++}throw new Error(`Could not find an available port after ${t} attempts starting from ${e}.`)}var Gl=async e=>{xt.info(We.blue(`Starting ${m.name} Dev Orchestrator...`));let t=ve(),n=e.buildProfile||"development",i=t?Me(t,n):null,r={...process.env};if(t?.projectId)try{xt.info(We.dim(`Fetching managed secrets for profile: ${n}...`));let d=await N.get(`/projects/${t.projectId}/secrets/export?env=${n}`);d.data?.status==="success"&&d.data.secrets&&(Object.assign(r,d.data.secrets),xt.info(We.magenta(`\u2713 Injected secrets from ${m.name} Dashboard.`)))}catch{xt.warn("Could not fetch managed secrets. Running with local env only.")}t&&Object.assign(r,Qt(t,m.envPrefix)),i&&(Object.assign(r,Qt(i,`${m.envPrefix}PROFILE_`)),i.env&&Object.assign(r,i.env));let o=!1;try{let d=JSON.parse(Sg.readFileSync("package.json","utf-8"));o=!!(d.dependencies?.expo||d.devDependencies?.expo)}catch{xt.warn("Could not parse package.json. Defaulting to Bare React Native mode.")}let s=Number(e.port||i?.port||8081),l=await Hl(s);l!==s&&xt.info(We.yellow(`Port ${s} is busy. ${m.name} is using ${l} instead.`));let a=process.platform==="win32"?"npx.cmd":"npx",c=[];if(o?(c=["expo","start","--port",String(l)],e.clear&&c.push("--clear"),e.tunnel&&c.push("--tunnel"),e.lan&&c.push("--lan"),e.localhost&&c.push("--localhost"),e.ios&&c.push("--ios"),e.android&&c.push("--android"),e.web&&c.push("--web")):(c=["react-native","start","--port",String(l)],e.clear&&c.push("--clear")),o){let d=[`${We.cyan("i")} open iOS simulator`,`${We.cyan("a")} open Android emulator/device`,`${We.cyan("w")} open web`,`${We.cyan("r")} reload app`];xt.info([We.bold("Launch controls"),...d.map(f=>` ${f}`),"",We.dim("Direct launch: mach start --ios | --android | --web"),We.dim(`Metro URL: http://localhost:${l}`)].join(`
1556
+ `))}else xt.info(We.dim(`Metro URL: http://localhost:${l}`));let u=yg(a,c,{stdio:"inherit",env:r});return new Promise((d,f)=>{u.on("exit",p=>{p===0?d():f(new Error(`Dev server exited with code ${p}`))}),u.on("error",p=>{f(p)})})};import{log as je}from"@clack/prompts";import nt from"chalk";var Vl=async e=>{let t=ve();t||(je.error(nt.red(`No ${m.configFileName} found in the current directory.`)),process.exit(1));let n=e.profile||"development",i=Me(t,n);if(e.json){console.log(JSON.stringify({project:t,resolved:i},null,2));return}je.info(nt.bgBlue.white(` Resolved ${m.name} Config [${n}] `)),je.info(nt.dim("----------------------------------------")),je.info(`${nt.blue("Project ID:")} ${t.projectId}`),je.info(`${nt.blue("Name:")} ${t.name}`),je.info(`${nt.blue("Scheme:")} ${t.scheme}`),i?(je.info(nt.dim(`
1557
1557
  Profile Settings:`)),je.info(`${nt.cyan("Distribution:")} ${i.distribution||"none"}`),je.info(`${nt.cyan("Development Client:")} ${i.developmentClient||"false"}`),i.env&&Object.keys(i.env).length>0&&(je.info(nt.dim(`
1558
- Environment Variables:`)),Object.entries(i.env).forEach(([r,o])=>{je.info(` ${r}=${o}`)}))):je.warn(`Profile '${n}' not found.`),je.info(nt.dim("----------------------------------------"))};import{cancel as Bg,confirm as lc,isCancel as qn,log as qi,note as jg,outro as Xi,select as Hg,text as cc}from"@clack/prompts";import rt from"chalk";import Gg from"fs";import sc from"path";import Jt from"fs";import mn from"path";var gn="./plugins/withMachDeepLinks",Io="mach.deep-links.json",Kl=["/*"],vo=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},Hi=e=>{let t=new Set,n=[];for(let i of e){let r=vo(i);!r||t.has(r)||(t.add(r),n.push(r))}return n},it=(...e)=>{for(let t of e){let n=vo(t);if(n)return n}},zl=(e,t)=>e.length!==t.length?!1:e.every((n,i)=>n===t[i]);function Wl(e){if(typeof e!="string")return;let t=e.trim().toLowerCase();if(t==="ios"||t==="android"||t==="all")return t}function Wn(e){let t=e.includes("://")?e:`https://${e}`,n=new URL(t),i=n.pathname==="/"?"":n.pathname.replace(/\/+$/,"");return`${n.protocol}//${n.host}${i}`}function ji(e){return new URL(Wn(e)).hostname}function Gi(e){return e.trim().toLowerCase().replace(/[^a-z0-9+.-]/g,"-").replace(/^-+|-+$/g,"")||"app"}function Jl(e=process.cwd()){let t=mn.join(e,"package.json"),n=["app.json","app.config.json","app.config.js","app.config.ts"].some(r=>Jt.existsSync(mn.join(e,r))),i=!1;if(Jt.existsSync(t))try{let r=JSON.parse(Jt.readFileSync(t,"utf-8"));i=!!(r.dependencies?.expo||r.devDependencies?.expo)}catch{i=!1}return n&&i?"expo":Jt.existsSync(mn.join(e,"ios"))||Jt.existsSync(mn.join(e,"android"))?"react-native":Zt}function Yl(e,t,n){if(t)return t;let i=!!(e.ios?.bundleIdentifier||n?.ios?.bundleIdentifier||e.deepLinks?.ios?.bundleIdentifier),r=!!(e.android?.package||n?.android?.package||n?.android?.applicationId||e.deepLinks?.android?.package);return i&&!r?"ios":r&&!i?"android":"all"}function Vi(e=process.cwd(),t){let n=t?.deepLinks?.web?.outputDir;return n||(Jt.existsSync(mn.join(e,"public"))?"public":"deep-links")}function ql(e,t){let n=e.deepLinks,i=Wn(t.baseUrl||n?.baseUrl||(t.domain?`https://${t.domain}`:"")),r=vo(t.domain)||(t.baseUrl?ji(i):n?.domain)||ji(i),o=t.profile||n?.profile||"production",s=Me(e,o)||{},l=s.ios||{},a=s.android||{},c=e.submit?.[o]?.ios||{},u=e.submit?.[o]?.android||{},d=Gi(t.scheme||n?.scheme||l.scheme||e.scheme||e.slug||e.name||"app"),f=Bt(e,o),p=Hi([...t.androidFingerprint||[],...n?.android?.sha256CertFingerprints||[],...a.sha256CertFingerprints||[],...e.android?.sha256CertFingerprints||[]]);return{baseUrl:i,domain:r,scheme:d,profile:o!=="production"?o:void 0,paths:n?.paths?.length?n.paths:["/*"],ios:{appStoreId:it(t.appStoreId,n?.ios?.appStoreId,l.ascAppId,c.ascAppId,e.ios?.ascAppId),appleTeamId:it(t.appleTeamId,n?.ios?.appleTeamId,l.appleTeamId,l.developerTeamId,c.appleTeamId,c.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,f.teamId),bundleIdentifier:it(t.iosBundleId,n?.ios?.bundleIdentifier,l.bundleIdentifier,l.bundleId,e.ios?.bundleIdentifier,e.ios?.bundleId,e.expo?.ios?.bundleIdentifier),webCredentials:!!n?.ios?.webCredentials,activityContinuation:!!n?.ios?.activityContinuation},android:{package:it(t.androidPackage,n?.android?.package,a.package,a.applicationId,e.android?.package,e.expo?.android?.package,u.packageName,u.package),sha256CertFingerprints:p},web:{smartAppBanner:n?.web?.smartAppBanner??!0,outputDir:t.output||n?.web?.outputDir||Vi(process.cwd(),e)}}}function zi(e,t={}){let n=e.deepLinks,i=t.profile||n?.profile||"production",r=t.expoHints||null,o=it(n?.baseUrl,r?.domain?`https://${r.domain}`:void 0);if(!o)return null;let s=Wn(o),l=Me(e,i)||{},a=l.ios||{},c=l.android||{},u=e.submit?.[i]?.ios||{},d=e.submit?.[i]?.android||{},f=Bt(e,i),p=it(n?.domain,r?.domain)||ji(s),g=Gi(it(n?.scheme,a.scheme,e.scheme,r?.scheme,e.slug,e.name,"app")||"app"),h=n?.paths?.length?n.paths:[...Kl];return{baseUrl:s,domain:p,scheme:g,profile:i!=="production"?i:void 0,paths:h,ios:{appStoreId:it(n?.ios?.appStoreId,a.ascAppId,u.ascAppId,e.ios?.ascAppId),appleTeamId:it(n?.ios?.appleTeamId,a.appleTeamId,a.developerTeamId,u.appleTeamId,u.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,f.teamId),bundleIdentifier:it(n?.ios?.bundleIdentifier,a.bundleIdentifier,a.bundleId,e.ios?.bundleIdentifier,e.ios?.bundleId,e.expo?.ios?.bundleIdentifier,r?.iosBundleIdentifier),webCredentials:!!n?.ios?.webCredentials,activityContinuation:!!n?.ios?.activityContinuation},android:{package:it(n?.android?.package,c.package,c.applicationId,e.android?.package,e.expo?.android?.package,d.packageName,d.package,r?.androidPackage),sha256CertFingerprints:Hi([...n?.android?.sha256CertFingerprints||[],...c.sha256CertFingerprints||[],...e.android?.sha256CertFingerprints||[]])},web:{smartAppBanner:n?.web?.smartAppBanner??!0,outputDir:n?.web?.outputDir||Vi(t.cwd||process.cwd(),e)}}}function Ki(e,t,n="production",i=process.cwd()){return t?.domain?zi({...e,deepLinks:{baseUrl:`https://${t.domain}`,domain:t.domain,scheme:t.scheme}},{profile:n,cwd:i,expoHints:t}):null}function Eg(e,t,n=t.profile||"production"){let i=zi({...e,deepLinks:{baseUrl:t.baseUrl}},{profile:n}),r={baseUrl:t.baseUrl};n!=="production"&&(r.profile=n),t.domain&&t.domain!==ji(t.baseUrl)&&(r.domain=t.domain),t.scheme&&t.scheme!==i?.scheme&&(r.scheme=t.scheme),t.paths?.length&&!zl(t.paths,Kl)&&(r.paths=t.paths);let o={};t.ios?.bundleIdentifier&&t.ios.bundleIdentifier!==i?.ios?.bundleIdentifier&&(o.bundleIdentifier=t.ios.bundleIdentifier),t.ios?.appleTeamId&&t.ios.appleTeamId!==i?.ios?.appleTeamId&&(o.appleTeamId=t.ios.appleTeamId),t.ios?.appStoreId&&t.ios.appStoreId!==i?.ios?.appStoreId&&(o.appStoreId=t.ios.appStoreId),t.ios?.webCredentials&&(o.webCredentials=!0),t.ios?.activityContinuation&&(o.activityContinuation=!0),Object.keys(o).length>0&&(r.ios=o);let s={};t.android?.package&&t.android.package!==i?.android?.package&&(s.package=t.android.package);let l=t.android?.sha256CertFingerprints||[],a=i?.android?.sha256CertFingerprints||[];l.length>0&&!zl(l,a)&&(s.sha256CertFingerprints=l),Object.keys(s).length>0&&(r.android=s);let c={outputDir:t.web?.outputDir||"deep-links"};return t.web?.smartAppBanner===!1&&(c.smartAppBanner=!1),r.web=c,r}function Xl(e,t,n,i=n.profile||"production"){let r={...e,framework:t,scheme:e.scheme||n.scheme};return{...r,deepLinks:Eg(r,n,i)}}function Zl(e,t=process.cwd()){let n=mn.join(t,"mach.config.json");return Jt.writeFileSync(n,`${JSON.stringify(e,null,2)}
1559
- `),n}import _e from"fs";import Ce from"path";import{spawnSync as Ql}from"child_process";var Jn="@radhya/mach/expo-plugin",Ag=`const fs = require('fs')
1558
+ Environment Variables:`)),Object.entries(i.env).forEach(([r,o])=>{je.info(` ${r}=${o}`)}))):je.warn(`Profile '${n}' not found.`),je.info(nt.dim("----------------------------------------"))};import{cancel as Fg,confirm as lc,isCancel as qn,log as qi,note as Bg,outro as Xi,select as jg,text as cc}from"@clack/prompts";import rt from"chalk";import Hg from"fs";import sc from"path";import Jt from"fs";import mn from"path";var gn="./plugins/withMachDeepLinks",Io="mach.deep-links.json",Kl=["/*"],vo=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},Hi=e=>{let t=new Set,n=[];for(let i of e){let r=vo(i);!r||t.has(r)||(t.add(r),n.push(r))}return n},it=(...e)=>{for(let t of e){let n=vo(t);if(n)return n}},zl=(e,t)=>e.length!==t.length?!1:e.every((n,i)=>n===t[i]);function Wl(e){if(typeof e!="string")return;let t=e.trim().toLowerCase();if(t==="ios"||t==="android"||t==="all")return t}function Wn(e){let t=e.includes("://")?e:`https://${e}`,n=new URL(t),i=n.pathname==="/"?"":n.pathname.replace(/\/+$/,"");return`${n.protocol}//${n.host}${i}`}function ji(e){return new URL(Wn(e)).hostname}function Gi(e){return e.trim().toLowerCase().replace(/[^a-z0-9+.-]/g,"-").replace(/^-+|-+$/g,"")||"app"}function Jl(e=process.cwd()){let t=mn.join(e,"package.json"),n=["app.json","app.config.json","app.config.js","app.config.ts"].some(r=>Jt.existsSync(mn.join(e,r))),i=!1;if(Jt.existsSync(t))try{let r=JSON.parse(Jt.readFileSync(t,"utf-8"));i=!!(r.dependencies?.expo||r.devDependencies?.expo)}catch{i=!1}return n&&i?"expo":Jt.existsSync(mn.join(e,"ios"))||Jt.existsSync(mn.join(e,"android"))?"react-native":Zt}function Yl(e,t,n){if(t)return t;let i=!!(e.ios?.bundleIdentifier||n?.ios?.bundleIdentifier||e.deepLinks?.ios?.bundleIdentifier),r=!!(e.android?.package||n?.android?.package||n?.android?.applicationId||e.deepLinks?.android?.package);return i&&!r?"ios":r&&!i?"android":"all"}function Vi(e=process.cwd(),t){let n=t?.deepLinks?.web?.outputDir;return n||(Jt.existsSync(mn.join(e,"public"))?"public":"deep-links")}function ql(e,t){let n=e.deepLinks,i=Wn(t.baseUrl||n?.baseUrl||(t.domain?`https://${t.domain}`:"")),r=vo(t.domain)||(t.baseUrl?ji(i):n?.domain)||ji(i),o=t.profile||n?.profile||"production",s=Me(e,o)||{},l=s.ios||{},a=s.android||{},c=e.submit?.[o]?.ios||{},u=e.submit?.[o]?.android||{},d=Gi(t.scheme||n?.scheme||l.scheme||e.scheme||e.slug||e.name||"app"),f=Bt(e,o),p=Hi([...t.androidFingerprint||[],...n?.android?.sha256CertFingerprints||[],...a.sha256CertFingerprints||[],...e.android?.sha256CertFingerprints||[]]);return{baseUrl:i,domain:r,scheme:d,profile:o!=="production"?o:void 0,paths:n?.paths?.length?n.paths:["/*"],ios:{appStoreId:it(t.appStoreId,n?.ios?.appStoreId,l.ascAppId,c.ascAppId,e.ios?.ascAppId),appleTeamId:it(t.appleTeamId,n?.ios?.appleTeamId,l.appleTeamId,l.developerTeamId,c.appleTeamId,c.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,f.teamId),bundleIdentifier:it(t.iosBundleId,n?.ios?.bundleIdentifier,l.bundleIdentifier,l.bundleId,e.ios?.bundleIdentifier,e.ios?.bundleId,e.expo?.ios?.bundleIdentifier),webCredentials:!!n?.ios?.webCredentials,activityContinuation:!!n?.ios?.activityContinuation},android:{package:it(t.androidPackage,n?.android?.package,a.package,a.applicationId,e.android?.package,e.expo?.android?.package,u.packageName,u.package),sha256CertFingerprints:p},web:{smartAppBanner:n?.web?.smartAppBanner??!0,outputDir:t.output||n?.web?.outputDir||Vi(process.cwd(),e)}}}function zi(e,t={}){let n=e.deepLinks,i=t.profile||n?.profile||"production",r=t.expoHints||null,o=it(n?.baseUrl,r?.domain?`https://${r.domain}`:void 0);if(!o)return null;let s=Wn(o),l=Me(e,i)||{},a=l.ios||{},c=l.android||{},u=e.submit?.[i]?.ios||{},d=e.submit?.[i]?.android||{},f=Bt(e,i),p=it(n?.domain,r?.domain)||ji(s),g=Gi(it(n?.scheme,a.scheme,e.scheme,r?.scheme,e.slug,e.name,"app")||"app"),h=n?.paths?.length?n.paths:[...Kl];return{baseUrl:s,domain:p,scheme:g,profile:i!=="production"?i:void 0,paths:h,ios:{appStoreId:it(n?.ios?.appStoreId,a.ascAppId,u.ascAppId,e.ios?.ascAppId),appleTeamId:it(n?.ios?.appleTeamId,a.appleTeamId,a.developerTeamId,u.appleTeamId,u.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,f.teamId),bundleIdentifier:it(n?.ios?.bundleIdentifier,a.bundleIdentifier,a.bundleId,e.ios?.bundleIdentifier,e.ios?.bundleId,e.expo?.ios?.bundleIdentifier,r?.iosBundleIdentifier),webCredentials:!!n?.ios?.webCredentials,activityContinuation:!!n?.ios?.activityContinuation},android:{package:it(n?.android?.package,c.package,c.applicationId,e.android?.package,e.expo?.android?.package,d.packageName,d.package,r?.androidPackage),sha256CertFingerprints:Hi([...n?.android?.sha256CertFingerprints||[],...c.sha256CertFingerprints||[],...e.android?.sha256CertFingerprints||[]])},web:{smartAppBanner:n?.web?.smartAppBanner??!0,outputDir:n?.web?.outputDir||Vi(t.cwd||process.cwd(),e)}}}function Ki(e,t,n="production",i=process.cwd()){return t?.domain?zi({...e,deepLinks:{baseUrl:`https://${t.domain}`,domain:t.domain,scheme:t.scheme}},{profile:n,cwd:i,expoHints:t}):null}function bg(e,t,n=t.profile||"production"){let i=zi({...e,deepLinks:{baseUrl:t.baseUrl}},{profile:n}),r={baseUrl:t.baseUrl};n!=="production"&&(r.profile=n),t.domain&&t.domain!==ji(t.baseUrl)&&(r.domain=t.domain),t.scheme&&t.scheme!==i?.scheme&&(r.scheme=t.scheme),t.paths?.length&&!zl(t.paths,Kl)&&(r.paths=t.paths);let o={};t.ios?.bundleIdentifier&&t.ios.bundleIdentifier!==i?.ios?.bundleIdentifier&&(o.bundleIdentifier=t.ios.bundleIdentifier),t.ios?.appleTeamId&&t.ios.appleTeamId!==i?.ios?.appleTeamId&&(o.appleTeamId=t.ios.appleTeamId),t.ios?.appStoreId&&t.ios.appStoreId!==i?.ios?.appStoreId&&(o.appStoreId=t.ios.appStoreId),t.ios?.webCredentials&&(o.webCredentials=!0),t.ios?.activityContinuation&&(o.activityContinuation=!0),Object.keys(o).length>0&&(r.ios=o);let s={};t.android?.package&&t.android.package!==i?.android?.package&&(s.package=t.android.package);let l=t.android?.sha256CertFingerprints||[],a=i?.android?.sha256CertFingerprints||[];l.length>0&&!zl(l,a)&&(s.sha256CertFingerprints=l),Object.keys(s).length>0&&(r.android=s);let c={outputDir:t.web?.outputDir||"deep-links"};return t.web?.smartAppBanner===!1&&(c.smartAppBanner=!1),r.web=c,r}function Xl(e,t,n,i=n.profile||"production"){let r={...e,framework:t,scheme:e.scheme||n.scheme};return{...r,deepLinks:bg(r,n,i)}}function Zl(e,t=process.cwd()){let n=mn.join(t,"mach.config.json");return Jt.writeFileSync(n,`${JSON.stringify(e,null,2)}
1559
+ `),n}import $e from"fs";import Ce from"path";import{spawnSync as Ql}from"child_process";var Jn="@radhya/mach/expo-plugin",Eg=`const fs = require('fs')
1560
1560
  const path = require('path')
1561
1561
 
1562
1562
  const unique = values => Array.from(new Set(values.filter(Boolean)))
@@ -1676,24 +1676,24 @@ module.exports = function withMachDeepLinks(config) {
1676
1676
  config.android.intentFilters = intentFilters
1677
1677
  return config
1678
1678
  }
1679
- `,Yn=e=>{if(typeof e=="string"){if(e===Jn)return Jn;if(e===gn||e===gn.replace(/^\.\//,""))return gn}},$o=e=>Array.isArray(e)?$o(e[0]):Yn(e),ec=e=>!!$o(e),wg=(e,t)=>{let n=Yn(t)||t;return $o(e)===n},Ig=(e,t)=>Array.isArray(e)?[t,...e.slice(1)]:t,vg=(e,t)=>{let n=Array.isArray(e.plugins)?e.plugins:[];if(n.findIndex(o=>wg(o,t))>=0)return e.plugins=n,"already";let r=n.findIndex(ec);return r>=0?(n[r]=Ig(n[r],t),e.plugins=n,"updated"):(n.push(t),e.plugins=n,"inserted")},_o=/(['"`])((?:\.\/)?plugins\/withMachDeepLinks|@radhya\/mach\/expo-plugin)\1/g;function Wi(e){let t=Ce.join(e,"package.json");if(!_e.existsSync(t))return!1;try{let n=JSON.parse(_e.readFileSync(t,"utf-8"));return!!(n.dependencies?.["@radhya/mach"]||n.devDependencies?.["@radhya/mach"])}catch{return!1}}function _g(e){return _e.existsSync(Ce.join(e,"pnpm-lock.yaml"))?{command:"pnpm",args:["add","-D","@radhya/mach@latest"],label:"pnpm add -D @radhya/mach@latest"}:_e.existsSync(Ce.join(e,"yarn.lock"))?{command:"yarn",args:["add","-D","@radhya/mach@latest"],label:"yarn add -D @radhya/mach@latest"}:_e.existsSync(Ce.join(e,"bun.lockb"))||_e.existsSync(Ce.join(e,"bun.lock"))?{command:"bun",args:["add","-d","@radhya/mach@latest"],label:"bun add -d @radhya/mach@latest"}:{command:"npm",args:["install","--save-dev","@radhya/mach@latest"],label:"npm install --save-dev @radhya/mach@latest"}}function $g(e){let t=_g(e),n=Ql(t.command,t.args,{cwd:e,stdio:"inherit",env:{...process.env}});return n.error?{ok:!1,label:t.label,error:n.error.message}:n.status!==0?{ok:!1,label:t.label,error:`${t.command} exited with code ${n.status}`}:{ok:Wi(e),label:t.label}}function Rg(e,t){let n=_e.readFileSync(e,"utf-8"),i=Array.from(n.matchAll(_o));if(i.length>0){let f=Yn(t)||t,p=i.some(y=>Yn(y[2])===f),g=i.some(y=>Yn(y[2])!==f);if(p&&!g)return"already";let h=n.replace(_o,(y,E)=>`${E}${t}${E}`);return _e.writeFileSync(e,h),"updated"}let r=/(^[ \t]*)plugins\s*:\s*\[([\s\S]*?)\]/m.exec(n);if(!r||r.index===void 0)return"unsupported";let o=n.includes(`\r
1679
+ `,Yn=e=>{if(typeof e=="string"){if(e===Jn)return Jn;if(e===gn||e===gn.replace(/^\.\//,""))return gn}},$o=e=>Array.isArray(e)?$o(e[0]):Yn(e),ec=e=>!!$o(e),Ag=(e,t)=>{let n=Yn(t)||t;return $o(e)===n},wg=(e,t)=>Array.isArray(e)?[t,...e.slice(1)]:t,Ig=(e,t)=>{let n=Array.isArray(e.plugins)?e.plugins:[];if(n.findIndex(o=>Ag(o,t))>=0)return e.plugins=n,"already";let r=n.findIndex(ec);return r>=0?(n[r]=wg(n[r],t),e.plugins=n,"updated"):(n.push(t),e.plugins=n,"inserted")},_o=/(['"`])((?:\.\/)?plugins\/withMachDeepLinks|@radhya\/mach\/expo-plugin)\1/g;function Wi(e){let t=Ce.join(e,"package.json");if(!$e.existsSync(t))return!1;try{let n=JSON.parse($e.readFileSync(t,"utf-8"));return!!(n.dependencies?.["@radhya/mach"]||n.devDependencies?.["@radhya/mach"])}catch{return!1}}function vg(e){return $e.existsSync(Ce.join(e,"pnpm-lock.yaml"))?{command:"pnpm",args:["add","-D","@radhya/mach@latest"],label:"pnpm add -D @radhya/mach@latest"}:$e.existsSync(Ce.join(e,"yarn.lock"))?{command:"yarn",args:["add","-D","@radhya/mach@latest"],label:"yarn add -D @radhya/mach@latest"}:$e.existsSync(Ce.join(e,"bun.lockb"))||$e.existsSync(Ce.join(e,"bun.lock"))?{command:"bun",args:["add","-d","@radhya/mach@latest"],label:"bun add -d @radhya/mach@latest"}:{command:"npm",args:["install","--save-dev","@radhya/mach@latest"],label:"npm install --save-dev @radhya/mach@latest"}}function _g(e){let t=vg(e),n=Ql(t.command,t.args,{cwd:e,stdio:"inherit",env:{...process.env}});return n.error?{ok:!1,label:t.label,error:n.error.message}:n.status!==0?{ok:!1,label:t.label,error:`${t.command} exited with code ${n.status}`}:{ok:Wi(e),label:t.label}}function $g(e,t){let n=$e.readFileSync(e,"utf-8"),i=Array.from(n.matchAll(_o));if(i.length>0){let f=Yn(t)||t,p=i.some(y=>Yn(y[2])===f),g=i.some(y=>Yn(y[2])!==f);if(p&&!g)return"already";let h=n.replace(_o,(y,E)=>`${E}${t}${E}`);return $e.writeFileSync(e,h),"updated"}let r=/(^[ \t]*)plugins\s*:\s*\[([\s\S]*?)\]/m.exec(n);if(!r||r.index===void 0)return"unsupported";let o=n.includes(`\r
1680
1680
  `)?`\r
1681
1681
  `:`
1682
1682
  `,s=r[1]||"",l=r[2]||"",c=l.match(/\r?\n([ \t]+)\S/)?.[1]||`${s} `,u=l.trim().length===0?`${o}${c}'${t}',${o}${s}`:l.includes(`
1683
- `)||l.includes("\r")?`${o}${c}'${t}',${l}`:`${o}${c}'${t}',${o}${c}${l.trim()}${o}${s}`,d=`${r[0].slice(0,r[0].indexOf("[")+1)}${u}]`;return _e.writeFileSync(e,`${n.slice(0,r.index)}${d}${n.slice(r.index+r[0].length)}`),"inserted"}function tc(e,t,n={}){let i=[],r=[],o=[],s=Wi(e);if(!s&&n.installPackage){o.push("Installing @radhya/mach locally so Expo can use @radhya/mach/expo-plugin.");let d=$g(e);d.ok?(s=!0,o.push(`Installed @radhya/mach with ${d.label}.`)):o.push(`Could not install @radhya/mach with ${d.label}: ${d.error||"unknown error"}. Falling back to local plugin.`)}let l=s?Jn:gn;if(s)o.push(`Using packaged Expo plugin ${Jn}.`);else{let d=Ce.join(e,"plugins/withMachDeepLinks.js");_e.mkdirSync(Ce.dirname(d),{recursive:!0}),_e.writeFileSync(d,Ag),i.push(Ce.relative(e,d)),o.push(`Using local Expo plugin fallback ${gn}. Install @radhya/mach in this app to use ${Jn}.`)}let a=["app.config.ts","app.config.js"].find(d=>_e.existsSync(Ce.join(e,d)));if(a){let d=Ce.join(e,a),f=Rg(d,l);return f==="inserted"?i.push(a):f==="updated"?(i.push(a),o.push(`${a} updated to use ${l}.`)):f==="already"?o.push(`${a} already includes the Mach Expo plugin.`):o.push(`Add "${l}" to the plugins array in ${a}. Mach could not safely auto-edit this dynamic config.`),{written:i,skipped:r,notes:o}}let c=["app.config.json","app.json"].find(d=>_e.existsSync(Ce.join(e,d)));if(!c)return r.push("Expo config plugin insertion: no app.json or app.config.json found."),{written:i,skipped:r,notes:o};let u=Ce.join(e,c);try{let d=JSON.parse(_e.readFileSync(u,"utf-8")),f=d.expo&&typeof d.expo=="object"?d.expo:d,p=vg(f,l);_e.writeFileSync(u,`${JSON.stringify(d,null,2)}
1684
- `),i.push(c),p==="updated"&&o.push(`${c} updated to use ${l}.`)}catch(d){r.push(`Expo config plugin insertion failed: ${d.message}`)}return{written:i,skipped:r,notes:o}}function nc(e){let t=["app.config.ts","app.config.js"].find(i=>_e.existsSync(Ce.join(e,i)));if(t){let i=_e.readFileSync(Ce.join(e,t),"utf-8");return _o.test(i)?"manual":"missing"}let n=["app.config.json","app.json"].find(i=>_e.existsSync(Ce.join(e,i)));if(!n)return"missing";try{let i=JSON.parse(_e.readFileSync(Ce.join(e,n),"utf-8")),r=i.expo&&typeof i.expo=="object"?i.expo:i;return Array.isArray(r.plugins)&&r.plugins.some(ec)?"wired":"missing"}catch{return"missing"}}function Ro(e){let t=Ql("npx",["--no-install","expo","config","--type","public","--json"],{cwd:e,encoding:"utf-8",timeout:8e3,env:{...process.env,CI:"1",EXPO_NO_TELEMETRY:"1"}});if(t.error||t.status!==0||!t.stdout)return null;let n=t.stdout.trim(),i=n.indexOf("{");if(i<0)return null;try{return JSON.parse(n.slice(i))}catch{return null}}function Pg(e){if(!e||typeof e!="object")return null;let n=(Array.isArray(e.ios?.associatedDomains)?e.ios.associatedDomains:[]).map(s=>typeof s=="string"?s.match(/^applinks:(.+)$/)?.[1]:void 0).find(Boolean),r=(Array.isArray(e.android?.intentFilters)?e.android.intentFilters:[]).flatMap(s=>Array.isArray(s?.data)?s.data:[]).find(s=>s?.scheme==="https"&&typeof s.host=="string")?.host,o=n||r;return!o&&!e.scheme&&!e.ios?.bundleIdentifier&&!e.android?.package?null:{domain:o,scheme:typeof e.scheme=="string"?e.scheme:void 0,iosBundleIdentifier:typeof e.ios?.bundleIdentifier=="string"?e.ios.bundleIdentifier:void 0,androidPackage:typeof e.android?.package=="string"?e.android.package:void 0}}function Ji(e){return Pg(Ro(e))}import dt from"fs";import He from"path";var Og=e=>{dt.mkdirSync(He.dirname(e),{recursive:!0})},ic=(e,t)=>{Og(e),dt.writeFileSync(e,`${JSON.stringify(t,null,2)}
1685
- `)};var Dg=e=>{let t=e.ios?.appleTeamId,n=e.ios?.bundleIdentifier;return!t||!n?null:`${t}.${n}`};function Po(e){let t=Dg(e);if(!t)return null;let n=e.paths?.length?e.paths:["/*"],i=n.map(s=>s==="/*"?"*":s),r=n.map(s=>({"/":s,comment:s==="/*"?"Matches all URLs":`Matches ${s}`})),o={applinks:{apps:[],details:[{appIDs:[t],components:r,appID:t,paths:i}]}};return e.ios?.webCredentials&&(o.webcredentials={apps:[t]}),e.ios?.activityContinuation&&(o.activitycontinuation={apps:[t]}),o}function Cg(e){let t=e.android?.package;return t?[{relation:["delegate_permission/common.handle_all_urls"],target:{namespace:"android_app",package_name:t,sha256_cert_fingerprints:e.android?.sha256CertFingerprints||[]}}]:null}function kg(e,t){let n=t.ios?.appStoreId;if(!n||!dt.existsSync(e))return!1;let i=`<meta name="apple-itunes-app" content="app-id=${n}, app-argument=${t.baseUrl}" />`,r=dt.readFileSync(e,"utf-8");if(/name=["']apple-itunes-app["']/i.test(r)){let o=r.replace(/<meta[^>]+name=["']apple-itunes-app["'][^>]*>/i,i);return dt.writeFileSync(e,o),!0}return/<head[^>]*>/i.test(r)?(dt.writeFileSync(e,r.replace(/<head([^>]*)>/i,`<head$1>
1686
- ${i}`)),!0):!1}function rc(e,t){let n=[],i=[],r=[],o=t.web?.outputDir||"deep-links",s=He.join(e,o),l=Po(t);if(l){let d=Hi([He.join(s,".well-known/apple-app-site-association"),He.join(s,".well-known/apple-app-site-association.json"),He.join(s,"apple-app-site-association"),He.join(s,"apple-app-site-association.json")]);for(let f of d)ic(f,l),n.push(He.relative(e,f))}else i.push("Apple AASA files: missing ios.appleTeamId or ios.bundleIdentifier");let a=Cg(t);if(a){let d=He.join(s,".well-known/assetlinks.json");ic(d,a),n.push(He.relative(e,d)),t.android?.sha256CertFingerprints?.length||r.push("assetlinks.json was generated without SHA256 fingerprints. Add them before Android App Links can verify.")}else i.push("Android assetlinks.json: missing android.package");let c=He.join(e,Io);dt.existsSync(c)&&(dt.unlinkSync(c),r.push(`Removed legacy ${Io}; mach.config.json is now the source of truth.`));let u=He.join(s,"index.html");return t.web?.smartAppBanner&&kg(u,t)?n.push(He.relative(e,u)):t.web?.smartAppBanner&&t.ios?.appStoreId?r.push(`Smart App Banner skipped because ${He.relative(e,u)} was not found.`):t.web?.smartAppBanner&&r.push("Smart App Banner skipped because ios.appStoreId is not configured."),o!=="public"&&r.push(`Host ${o}/.well-known files at https://${t.domain}/.well-known/.`),{written:n,skipped:i,notes:r}}function Oo(e){if(!dt.existsSync(e))return null;try{return JSON.parse(dt.readFileSync(e,"utf-8"))}catch{return null}}import Lt from"fs";import Mt from"path";import{spawnSync as Tg}from"child_process";var G=(e,t)=>({level:e,message:t}),oc=(e,t)=>Mt.join(e,t.web?.outputDir||"deep-links");function Ng(e,t,n="fail"){let i=[],r=oc(e,t),o=Mt.join(r,".well-known/apple-app-site-association");if(!Po(t))return i.push(G("warn","Skipping AASA verification because ios.appleTeamId or ios.bundleIdentifier is missing.")),i;if(!Lt.existsSync(o))return i.push(G(n,`${Mt.relative(e,o)} is missing.`)),i;Lt.statSync(o).size>128*1024?i.push(G("fail","Apple AASA file is larger than 128 KB.")):i.push(G("pass","Apple AASA file is under 128 KB."));let a=Oo(o);if(!a)return i.push(G("fail","Apple AASA file is not valid JSON.")),i;let c=`${t.ios?.appleTeamId}.${t.ios?.bundleIdentifier}`,u=a.applinks?.details||[],d=Array.isArray(u)&&u.some(f=>f.appID===c||f.appIDs?.includes(c));return i.push(d?G("pass",`AASA contains ${c}.`):G("fail",`AASA does not contain ${c}.`)),i}function xg(e,t,n="fail"){let i=[],r=oc(e,t),o=Mt.join(r,".well-known/assetlinks.json");if(!t.android?.package)return i.push(G("warn","Skipping Android Asset Links verification because android.package is missing.")),i;if(!Lt.existsSync(o))return i.push(G(n,`${Mt.relative(e,o)} is missing.`)),i;let s=Oo(o);if(!s||!Array.isArray(s))return i.push(G("fail","assetlinks.json is not a valid JSON array.")),i;let l=s.find(c=>c?.target?.package_name===t.android?.package);i.push(l?G("pass",`assetlinks.json contains ${t.android.package}.`):G("fail",`assetlinks.json does not contain ${t.android.package}.`));let a=l?.target?.sha256_cert_fingerprints;return i.push(Array.isArray(a)&&a.length>0?G("pass","assetlinks.json has at least one SHA256 fingerprint."):G("warn","assetlinks.json has no SHA256 fingerprints.")),i}function Lg(e,t,n,i){if(t.framework!=="expo")return[];let r=[],o=Ro(e),l=(Array.isArray(o?.ios?.associatedDomains)?o.ios.associatedDomains:[]).includes(`applinks:${n.domain}`);r.push(l?G("pass",`Expo iOS config contains applinks:${n.domain}.`):G(i?"warn":"fail",`Expo iOS config does not contain applinks:${n.domain}.`));let c=(Array.isArray(o?.android?.intentFilters)?o.android.intentFilters:[]).flatMap(d=>Array.isArray(d?.data)?d.data:[]).some(d=>d?.scheme==="https"&&d.host===n.domain);if(r.push(c?G("pass",`Expo Android config contains App Link host ${n.domain}.`):G(i?"warn":"fail",`Expo Android config does not contain App Link host ${n.domain}.`)),i)return r.push(G("warn","mach.config.json is missing deepLinks; values were inferred from Expo config. Run `mach deeplink setup` to persist Mach metadata and generate hosted files.")),r;let u=nc(e);return u==="wired"?r.push(G("pass","Expo deep link plugin is wired into Expo config and derives values from mach.config.json.")):u==="manual"&&l&&c?r.push(G("pass","Expo resolved config contains the deep-link settings from the dynamic config.")):u==="manual"?r.push(G("warn","Expo deep link plugin exists, but dynamic or missing Expo config means manual plugin insertion must be confirmed.")):l&&c?r.push(G("warn","Expo plugin is not wired, but resolved Expo config already contains the deep-link settings.")):r.push(G("fail","Expo deep link plugin is missing or not wired into Expo config.")),r}function Mg(e,t,n){if(t.framework!=="react-native")return[];let i=[],r=Mt.join(e,"android/app/src/main/AndroidManifest.xml");if(Lt.existsSync(r)){let s=Lt.readFileSync(r,"utf-8");i.push(s.includes(`android:host="${n.domain}"`)?G("pass","AndroidManifest.xml contains the App Links host."):G("warn","AndroidManifest.xml does not contain the App Links host. Native patching is planned for a later phase."))}let o=Mt.join(e,"ios");if(Lt.existsSync(o)){let l=Lt.readdirSync(o,{recursive:!0}).filter(a=>typeof a=="string"&&a.endsWith(".entitlements")).some(a=>Lt.readFileSync(Mt.join(o,a),"utf-8").includes(`applinks:${n.domain}`));i.push(l?G("pass","iOS entitlements contain the associated domain."):G("warn","iOS entitlements do not contain the associated domain. Native patching is planned for a later phase."))}return i}async function Do(e,t){let n=[];try{let i=await fetch(e,{redirect:"manual"});if(i.status>=300&&i.status<400&&!t)return n.push(G("fail",`${e} redirects. Apple AASA endpoints must not redirect.`)),n;n.push(i.status===200?G("pass",`${e} returned HTTP 200.`):G("fail",`${e} returned HTTP ${i.status}.`));let r=i.headers.get("content-type")||"";!r.includes("application/json")&&!r.includes("text/json")&&n.push(G("warn",`${e} content-type is "${r||"missing"}"; application/json is recommended.`)),JSON.parse(await i.text()),n.push(G("pass",`${e} contains valid JSON.`))}catch(i){n.push(G("fail",`${e} check failed: ${i.message}`))}return n}async function Ug(e){let t=e.domain;return(await Promise.all([Do(`https://${t}/.well-known/apple-app-site-association`,!1),Do(`https://${t}/apple-app-site-association`,!1),Do(`https://${t}/.well-known/assetlinks.json`,!0)])).flat()}function Fg(e){let t=e.android?.package;if(!t)return[G("warn","Cannot print Android device verification commands because android.package is missing.")];let i=Tg("adb",["version"],{encoding:"utf-8"}).status===0?"Run":"Install Android platform-tools, then run";return[G("info",`${i}: adb shell pm set-app-links --package ${t} 0 all`),G("info",`${i}: adb shell pm verify-app-links --re-verify ${t}`),G("info",`${i}: adb shell pm get-app-links ${t}`)]}async function Co(e,t,n){let i=n.profile||t.deepLinks?.profile||"production",r=t.framework==="expo"?Ji(e):null,o=t.framework==="expo"?Ki(t,r,i,e):null,s=!t.deepLinks&&!!o,l=t.deepLinks?zi(t,{profile:i,cwd:e,expoHints:r}):o,a=[];if(!l)return[G("fail","mach.config.json is missing deepLinks and Mach could not infer existing deep-link settings. Run `mach deeplink setup`.")];a.push(l.baseUrl?G("pass","deepLinks.baseUrl is configured."):G("fail","deepLinks.baseUrl is missing.")),a.push(l.domain?G("pass","deepLinks.domain is configured."):G("fail","deepLinks.domain is missing.")),a.push(l.scheme?G("pass","deepLinks.scheme is configured."):G("fail","deepLinks.scheme is missing."));let c=s?"warn":"fail";return a.push(...Ng(e,l,c)),a.push(...xg(e,l,c)),a.push(...Lg(e,t,l,s)),a.push(...Mg(e,t,l)),n.live&&a.push(...await Ug(l)),n.androidDevice&&a.push(...Fg(l)),a}var Xn=()=>process.stdin.isTTY&&process.stdout.isTTY,ac=`${m.npmScope}/mach`,Zn=()=>{Bg("Operation cancelled."),process.exit(0)},No=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},Et=(...e)=>{for(let t of e){let n=No(t);if(n)return n}},Yt=async(e,t,n,i,r)=>{if(t)return t;if(r||!Xn()){if(i)throw new Error(`${e.replace(/:$/,"")} is required. Pass it as an option or run interactively.`);return}let o=await cc({message:e,initialValue:t||"",placeholder:n,validate(s){if(i&&s.trim().length===0)return"Required."}});return qn(o)&&Zn(),No(o)};async function Vg(e,t,n){let i=Ve(e.framework),r=Ve(t);if(r)return r;if(i)return i;let o=Jl();if(n||!Xn())return o;let s=await Hg({message:`Which framework should ${m.name} configure?`,initialValue:o,options:[{value:"expo",label:"Expo"},{value:"react-native",label:"React Native"},{value:"flutter",label:"Flutter (not supported yet)"}]});return qn(s)&&Zn(),s}var Yi=e=>e==="ios"||e==="all",ko=e=>e==="android"||e==="all";async function zg(e,t,n){if(t!=="expo"||Wi(e)||n.installPlugin===!1)return!1;if(n.installPlugin===!0||n.yes)return!0;if(!Xn())return!1;let i=await lc({message:`Install ${ac} locally so Expo can use ${ac}/expo-plugin?`,initialValue:!0});return qn(i)&&Zn(),!!i}async function Kg(e,t){let n=!!t.yes,i=t.profile||e.deepLinks?.profile||"production",r=Me(e,i)||{},o=r.ios||{},s=r.android||{},l=e.submit?.[i]?.ios||{},a=e.submit?.[i]?.android||{},c=Bt(e,i),u=await Vg(e,t.framework,n);if(u==="flutter")throw new Error("Flutter deep-link automation is planned but not supported yet.");let d=Yl(e,Wl(t.platform),r),f=e.deepLinks,g=u==="expo"&&(!Et(t.baseUrl,f?.baseUrl,t.domain?`https://${t.domain}`:void 0)||!Et(t.scheme,f?.scheme,o.scheme,e.scheme,e.slug,e.name)||Yi(d)&&!Et(t.iosBundleId,f?.ios?.bundleIdentifier,o.bundleIdentifier,o.bundleId,e.ios?.bundleIdentifier)||ko(d)&&!Et(t.androidPackage,f?.android?.package,s.package,s.applicationId,e.android?.package,a.packageName,a.package))?Ki(e,Ji(process.cwd()),i,process.cwd()):null,h=t.output||f?.web?.outputDir||Vi(process.cwd(),e),y=await Yt("Deep link base URL:",t.baseUrl||f?.baseUrl||(t.domain?`https://${t.domain}`:void 0)||g?.baseUrl,"https://app.example.com",!0,n),E=Wn(y),S=t.baseUrl?new URL(E).hostname:void 0,P=await Yt("Associated domain:",t.domain||S||f?.domain||g?.domain||new URL(E).hostname,"app.example.com",!0,n),v=Gi(await Yt("URI scheme:",Et(t.scheme,f?.scheme,o.scheme,e.scheme,g?.scheme,e.slug,e.name,"app"),"myapp",!0,n)||"app"),F=Yi(d)?await Yt("iOS bundle identifier:",Et(t.iosBundleId,f?.ios?.bundleIdentifier,o.bundleIdentifier,o.bundleId,e.ios?.bundleIdentifier,g?.ios?.bundleIdentifier),"com.example.app",!0,n):f?.ios?.bundleIdentifier||e.ios?.bundleIdentifier,H=Yi(d)?await Yt("Apple Team ID:",Et(t.appleTeamId,f?.ios?.appleTeamId,o.appleTeamId,o.developerTeamId,l.appleTeamId,l.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,c.teamId),"ABCDE12345",!0,n):f?.ios?.appleTeamId||e.ios?.appleTeamId,Y=Yi(d)?await Yt("App Store ID (optional, for Smart App Banner):",Et(t.appStoreId,f?.ios?.appStoreId,o.ascAppId,l.ascAppId,e.ios?.ascAppId),"1234567890",!1,n):f?.ios?.appStoreId,I=ko(d)?await Yt("Android package name:",Et(t.androidPackage,f?.android?.package,s.package,s.applicationId,e.android?.package,a.packageName,a.package,g?.android?.package),"com.example.app",!0,n):f?.android?.package||e.android?.package,$=f?.android?.sha256CertFingerprints||[],M=t.androidFingerprint||[],k=e.android?.sha256CertFingerprints||[],A=s.sha256CertFingerprints||[],b=[...M,...$,...A,...k].filter(Boolean);if(ko(d)&&b.length===0&&!n&&Xn()){let U=await cc({message:"Android SHA256 fingerprint (optional now, required for verified App Links):",placeholder:"AA:BB:CC:..."});qn(U)&&Zn(),No(U)&&b.push(U)}let T=ql(e,{...t,baseUrl:E,domain:P,scheme:v,output:h,appleTeamId:H,appStoreId:Y,androidFingerprint:b});return T.ios={...T.ios||{},bundleIdentifier:F,appleTeamId:H,appStoreId:Y},T.android={...T.android||{},package:I,sha256CertFingerprints:Array.from(new Set(b))},{framework:u,platform:d,deepLinks:T}}var To=(e,t)=>{t.length!==0&&jg(t.map(n=>`- ${n}`).join(`
1687
- `),e)},dc=e=>e.filter(t=>t.level==="fail").length;function uc(e){for(let t of e){let n=t.level==="pass"?rt.green("PASS"):t.level==="warn"?rt.yellow("WARN"):t.level==="info"?rt.cyan("INFO"):rt.red("FAIL");qi.info(`${n} ${t.message}`)}}async function pc(e){try{let t=process.cwd(),n=sc.join(t,m.configFileName),i=ve()||{};if(!Gg.existsSync(n)){let E=e.yes||!Xn()?!0:await lc({message:`${m.configFileName} was not found. Create one with deep link settings?`,initialValue:!0});if(qn(E)&&Zn(),!E)return}let{framework:r,deepLinks:o}=await Kg(i,e),s=e.profile||i.deepLinks?.profile||"production",l=Xl(i,r,o,s),a=Zl(l,t),c=rc(t,o),u=await zg(t,r,e),d=r==="expo"?tc(t,o,{installPackage:u}):{written:[],skipped:[],notes:r==="react-native"?["React Native native patching is planned for a later phase. Hosted files were generated now."]:[]},f=[sc.relative(t,a),...c.written,...d.written],p=[...c.skipped,...d.skipped],g=[...c.notes,...d.notes];To("Written",f),To("Skipped",p),To("Notes",g);let h=await Co(t,l,{});uc(h);let y=dc(h);if(y>0){Xi(rt.yellow(`Deep link setup finished with ${y} verification issue(s).`));return}Xi(rt.green("Deep link setup complete."))}catch(t){qi.error(rt.red(t.message||"Deep link setup failed.")),process.exit(1)}}async function fc(e){try{let t=ve();t||(qi.error(rt.red(`No ${m.configFileName} found in the current directory.`)),process.exit(1));let n=await Co(process.cwd(),t,e);uc(n);let i=dc(n);i>0&&(Xi(rt.red(`Deep link verification failed with ${i} issue(s).`)),process.exit(1)),Xi(rt.green("Deep link verification passed."))}catch(t){qi.error(rt.red(t.message||"Deep link verification failed.")),process.exit(1)}}import{log as At,spinner as mc}from"@clack/prompts";import Ut from"chalk";var Wg=e=>{let t=String(e||"").trim().toLowerCase();if(t==="ios"||t==="android")return t;throw new Error("Pass --platform ios or --platform android.")},Jg=e=>{let t=Number(e);if(!Number.isFinite(t))throw new Error("Counter value must be a non-negative integer.");let n=Math.floor(t);if(n<0)throw new Error("Counter value must be a non-negative integer.");return n},Yg=(e,t,n)=>{let i=String(n.scope||"").trim();if(i)return i;let r=n.profile||"production",o=e==="android"?n.package||n.androidPackage:n.bundleId||n.iosBundleIdentifier;if(o)return String(o).trim();let s=e==="android"?si(t,r):ai(t,r);if(s)return s;throw new Error(e==="android"?`Could not resolve Android package. Set android.package in ${m.configFileName}, or pass --package/--scope.`:`Could not resolve iOS bundle identifier. Set ios.bundleIdentifier in ${m.configFileName}, or pass --bundle-id/--scope.`)},qg=()=>{let e=ve();if(!e)throw new Error(`No ${m.configFileName} found in the current directory. Run \`${m.nameLower} init\` or \`${m.nameLower} link\` first.`);if(!e.projectId)throw new Error(`${m.configFileName} is missing projectId.`);return e},xo=e=>{At.info(Ut.bgBlue.white(" Native Version Counter ")),At.info(`${Ut.blue("Platform:")} ${e.platform}`),At.info(`${Ut.blue("Scope:")} ${e.scope}`),At.info(`${Ut.blue("Current DB value:")} ${e.current}`),At.info(`${Ut.blue("Next build will use:")} ${e.next}`),e.seeded||At.warn(Ut.yellow("This counter has not been seeded yet. Existing store apps should run `mach version seed` once before auto-version builds."))};async function Lo(e,t={}){if(!Ne()){At.error(`You are not logged in. Run \`${m.nameLower} login\` first.`);return}try{let i=qg(),r=Wg(t.platform),o=Yg(r,i,t),s=String(i.projectId);if(e==="show"){let u=mc();t.json||u.start("Fetching native version counter...");let{data:d}=await N.get(`/projects/${s}/version-counters`,{params:{platform:r,scope:o}});if(d.status!=="success")throw new Error(d.message||"Failed to fetch version counter.");t.json?console.log(JSON.stringify(d.counter,null,2)):(u.stop("Fetched native version counter."),xo(d.counter));return}let l=Jg(t.value),a=mc();t.json||a.start("Seeding native version counter...");let{data:c}=await N.post(`/projects/${s}/version-counters/seed`,{platform:r,scope:o,value:l,force:!!t.force});if(c.status!=="success")throw new Error(c.message||"Failed to seed version counter.");t.json?console.log(JSON.stringify(c.counter,null,2)):(a.stop(`Seeded ${r} counter for ${o}.`),xo(c.counter))}catch(i){let r=i?.response?.data?.message||i?.message||"Version command failed.";if(i?.response?.data?.counter&&!t.json){At.error(Ut.red(r)),xo(i.response.data.counter);return}t.json?console.log(JSON.stringify({status:"error",message:r},null,2)):At.error(Ut.red(r))}}import{log as Je,spinner as ti,text as gc,select as hc,confirm as Xg,isCancel as Qn,cancel as ei,outro as ni,note as Zg}from"@clack/prompts";import oe from"chalk";import{execSync as Qg}from"child_process";async function eh(){try{let e=Qg("xcrun devicectl list devices --json-output -",{stdio:["pipe","pipe","pipe"]}).toString();return(JSON.parse(e).result?.devices||[]).filter(i=>i.connectionProperties?.transportType==="wired"||i.connectionProperties?.transportType==="localNetwork").map(i=>({udid:i.identifier,name:i.deviceProperties?.name||"Unknown Device",model:i.hardwareProperties?.marketingName||"iPhone"}))}catch{return[]}}var yc=e=>e?.trim().toUpperCase()||void 0;function Sc(e){let t=ve(),n=t?Bt(t):{},i=yc(e.teamId),r=i||n.teamId;return{...n,teamId:r,developerTeamId:i||n.developerTeamId||r}}async function th(e){let t=e.udid,n=e.name,i=Sc(e),r=i.teamId;if(!t){let s=ti();s.start("Scanning for connected iOS devices...");let l=await eh();if(s.stop(l.length>0?`Found ${l.length} device(s)`:"No devices detected"),l.length===1)t=l[0].udid,n=n||l[0].name,Je.info(`Detected: ${oe.bold(n)} (${oe.dim(t.substring(0,12)+"...")})`);else if(l.length>1){let a=await hc({message:"Select a device to register:",options:l.map(u=>({value:u.udid,label:`${u.name} (${u.model})`,hint:u.udid.substring(0,12)+"..."}))});if(Qn(a)){ei("Cancelled");return}t=a;let c=l.find(u=>u.udid===t);n=n||c.name}else{Je.info(oe.dim("No connected devices found. Enter UDID manually."));let a=await gc({message:"Device UDID:",placeholder:"00008101-XXXXXXXXXXXX",validate(c){if(c.length<10)return"UDID seems too short"}});if(Qn(a)){ei("Cancelled");return}t=a}}if(!n){let s=await gc({message:"Device name:",placeholder:"Nitesh's iPhone",validate(l){if(l.length===0)return"Name is required"}});if(Qn(s)){ei("Cancelled");return}n=s}let o=ti();o.start("Registering device...");try{await N.post("/devices",{udid:t,name:n,platform:"ios",addedVia:"cli",teamId:r,teamName:i.teamName,developerTeamId:i.developerTeamId,appStoreConnectTeamId:i.appStoreConnectTeamId,providerId:i.providerId,providerName:i.providerName}),o.stop(oe.green("Device registered!")),Je.info(` ${oe.bold("Name:")} ${n}`),Je.info(` ${oe.bold("UDID:")} ${oe.dim(t)}`),r&&Je.info(` ${oe.bold("Team:")} ${oe.dim(r)}`)}catch(s){o.stop(oe.red("Failed to register device")),Je.error(s.response?.data?.message||s.message)}ni(oe.blue("---"))}async function nh(e={}){let t=yc(e.teamId),n=ti();n.start("Fetching registered devices...");try{let r=(await N.get("/devices",t?{params:{teamId:t}}:void 0)).data.devices||[];if(n.stop(`${r.length} device(s) registered${t?` for team ${t}`:""}`),r.length===0){Je.info(oe.dim(`No devices registered yet. Run \`${m.nameLower} device register\` to add one.`)),ni(oe.blue("---"));return}let o=r.map(s=>{let l=new Date(s.createdAt).toLocaleDateString();return` ${oe.bold(s.name.padEnd(24))} ${oe.dim(s.udid.padEnd(28))} ${oe.dim((s.teamId||"-").padEnd(12))} ${oe.cyan((s.addedVia||"-").padEnd(10))} ${oe.dim(l)}`});Zg(` ${oe.bold("Name".padEnd(24))} ${"UDID".padEnd(28)} ${"Team".padEnd(12)} ${"Via".padEnd(10)} Date
1683
+ `)||l.includes("\r")?`${o}${c}'${t}',${l}`:`${o}${c}'${t}',${o}${c}${l.trim()}${o}${s}`,d=`${r[0].slice(0,r[0].indexOf("[")+1)}${u}]`;return $e.writeFileSync(e,`${n.slice(0,r.index)}${d}${n.slice(r.index+r[0].length)}`),"inserted"}function tc(e,t,n={}){let i=[],r=[],o=[],s=Wi(e);if(!s&&n.installPackage){o.push("Installing @radhya/mach locally so Expo can use @radhya/mach/expo-plugin.");let d=_g(e);d.ok?(s=!0,o.push(`Installed @radhya/mach with ${d.label}.`)):o.push(`Could not install @radhya/mach with ${d.label}: ${d.error||"unknown error"}. Falling back to local plugin.`)}let l=s?Jn:gn;if(s)o.push(`Using packaged Expo plugin ${Jn}.`);else{let d=Ce.join(e,"plugins/withMachDeepLinks.js");$e.mkdirSync(Ce.dirname(d),{recursive:!0}),$e.writeFileSync(d,Eg),i.push(Ce.relative(e,d)),o.push(`Using local Expo plugin fallback ${gn}. Install @radhya/mach in this app to use ${Jn}.`)}let a=["app.config.ts","app.config.js"].find(d=>$e.existsSync(Ce.join(e,d)));if(a){let d=Ce.join(e,a),f=$g(d,l);return f==="inserted"?i.push(a):f==="updated"?(i.push(a),o.push(`${a} updated to use ${l}.`)):f==="already"?o.push(`${a} already includes the Mach Expo plugin.`):o.push(`Add "${l}" to the plugins array in ${a}. Mach could not safely auto-edit this dynamic config.`),{written:i,skipped:r,notes:o}}let c=["app.config.json","app.json"].find(d=>$e.existsSync(Ce.join(e,d)));if(!c)return r.push("Expo config plugin insertion: no app.json or app.config.json found."),{written:i,skipped:r,notes:o};let u=Ce.join(e,c);try{let d=JSON.parse($e.readFileSync(u,"utf-8")),f=d.expo&&typeof d.expo=="object"?d.expo:d,p=Ig(f,l);$e.writeFileSync(u,`${JSON.stringify(d,null,2)}
1684
+ `),i.push(c),p==="updated"&&o.push(`${c} updated to use ${l}.`)}catch(d){r.push(`Expo config plugin insertion failed: ${d.message}`)}return{written:i,skipped:r,notes:o}}function nc(e){let t=["app.config.ts","app.config.js"].find(i=>$e.existsSync(Ce.join(e,i)));if(t){let i=$e.readFileSync(Ce.join(e,t),"utf-8");return _o.test(i)?"manual":"missing"}let n=["app.config.json","app.json"].find(i=>$e.existsSync(Ce.join(e,i)));if(!n)return"missing";try{let i=JSON.parse($e.readFileSync(Ce.join(e,n),"utf-8")),r=i.expo&&typeof i.expo=="object"?i.expo:i;return Array.isArray(r.plugins)&&r.plugins.some(ec)?"wired":"missing"}catch{return"missing"}}function Ro(e){let t=Ql("npx",["--no-install","expo","config","--type","public","--json"],{cwd:e,encoding:"utf-8",timeout:8e3,env:{...process.env,CI:"1",EXPO_NO_TELEMETRY:"1"}});if(t.error||t.status!==0||!t.stdout)return null;let n=t.stdout.trim(),i=n.indexOf("{");if(i<0)return null;try{return JSON.parse(n.slice(i))}catch{return null}}function Rg(e){if(!e||typeof e!="object")return null;let n=(Array.isArray(e.ios?.associatedDomains)?e.ios.associatedDomains:[]).map(s=>typeof s=="string"?s.match(/^applinks:(.+)$/)?.[1]:void 0).find(Boolean),r=(Array.isArray(e.android?.intentFilters)?e.android.intentFilters:[]).flatMap(s=>Array.isArray(s?.data)?s.data:[]).find(s=>s?.scheme==="https"&&typeof s.host=="string")?.host,o=n||r;return!o&&!e.scheme&&!e.ios?.bundleIdentifier&&!e.android?.package?null:{domain:o,scheme:typeof e.scheme=="string"?e.scheme:void 0,iosBundleIdentifier:typeof e.ios?.bundleIdentifier=="string"?e.ios.bundleIdentifier:void 0,androidPackage:typeof e.android?.package=="string"?e.android.package:void 0}}function Ji(e){return Rg(Ro(e))}import dt from"fs";import He from"path";var Pg=e=>{dt.mkdirSync(He.dirname(e),{recursive:!0})},ic=(e,t)=>{Pg(e),dt.writeFileSync(e,`${JSON.stringify(t,null,2)}
1685
+ `)};var Og=e=>{let t=e.ios?.appleTeamId,n=e.ios?.bundleIdentifier;return!t||!n?null:`${t}.${n}`};function Po(e){let t=Og(e);if(!t)return null;let n=e.paths?.length?e.paths:["/*"],i=n.map(s=>s==="/*"?"*":s),r=n.map(s=>({"/":s,comment:s==="/*"?"Matches all URLs":`Matches ${s}`})),o={applinks:{apps:[],details:[{appIDs:[t],components:r,appID:t,paths:i}]}};return e.ios?.webCredentials&&(o.webcredentials={apps:[t]}),e.ios?.activityContinuation&&(o.activitycontinuation={apps:[t]}),o}function Dg(e){let t=e.android?.package;return t?[{relation:["delegate_permission/common.handle_all_urls"],target:{namespace:"android_app",package_name:t,sha256_cert_fingerprints:e.android?.sha256CertFingerprints||[]}}]:null}function Cg(e,t){let n=t.ios?.appStoreId;if(!n||!dt.existsSync(e))return!1;let i=`<meta name="apple-itunes-app" content="app-id=${n}, app-argument=${t.baseUrl}" />`,r=dt.readFileSync(e,"utf-8");if(/name=["']apple-itunes-app["']/i.test(r)){let o=r.replace(/<meta[^>]+name=["']apple-itunes-app["'][^>]*>/i,i);return dt.writeFileSync(e,o),!0}return/<head[^>]*>/i.test(r)?(dt.writeFileSync(e,r.replace(/<head([^>]*)>/i,`<head$1>
1686
+ ${i}`)),!0):!1}function rc(e,t){let n=[],i=[],r=[],o=t.web?.outputDir||"deep-links",s=He.join(e,o),l=Po(t);if(l){let d=Hi([He.join(s,".well-known/apple-app-site-association"),He.join(s,".well-known/apple-app-site-association.json"),He.join(s,"apple-app-site-association"),He.join(s,"apple-app-site-association.json")]);for(let f of d)ic(f,l),n.push(He.relative(e,f))}else i.push("Apple AASA files: missing ios.appleTeamId or ios.bundleIdentifier");let a=Dg(t);if(a){let d=He.join(s,".well-known/assetlinks.json");ic(d,a),n.push(He.relative(e,d)),t.android?.sha256CertFingerprints?.length||r.push("assetlinks.json was generated without SHA256 fingerprints. Add them before Android App Links can verify.")}else i.push("Android assetlinks.json: missing android.package");let c=He.join(e,Io);dt.existsSync(c)&&(dt.unlinkSync(c),r.push(`Removed legacy ${Io}; mach.config.json is now the source of truth.`));let u=He.join(s,"index.html");return t.web?.smartAppBanner&&Cg(u,t)?n.push(He.relative(e,u)):t.web?.smartAppBanner&&t.ios?.appStoreId?r.push(`Smart App Banner skipped because ${He.relative(e,u)} was not found.`):t.web?.smartAppBanner&&r.push("Smart App Banner skipped because ios.appStoreId is not configured."),o!=="public"&&r.push(`Host ${o}/.well-known files at https://${t.domain}/.well-known/.`),{written:n,skipped:i,notes:r}}function Oo(e){if(!dt.existsSync(e))return null;try{return JSON.parse(dt.readFileSync(e,"utf-8"))}catch{return null}}import Lt from"fs";import Mt from"path";import{spawnSync as kg}from"child_process";var G=(e,t)=>({level:e,message:t}),oc=(e,t)=>Mt.join(e,t.web?.outputDir||"deep-links");function Tg(e,t,n="fail"){let i=[],r=oc(e,t),o=Mt.join(r,".well-known/apple-app-site-association");if(!Po(t))return i.push(G("warn","Skipping AASA verification because ios.appleTeamId or ios.bundleIdentifier is missing.")),i;if(!Lt.existsSync(o))return i.push(G(n,`${Mt.relative(e,o)} is missing.`)),i;Lt.statSync(o).size>128*1024?i.push(G("fail","Apple AASA file is larger than 128 KB.")):i.push(G("pass","Apple AASA file is under 128 KB."));let a=Oo(o);if(!a)return i.push(G("fail","Apple AASA file is not valid JSON.")),i;let c=`${t.ios?.appleTeamId}.${t.ios?.bundleIdentifier}`,u=a.applinks?.details||[],d=Array.isArray(u)&&u.some(f=>f.appID===c||f.appIDs?.includes(c));return i.push(d?G("pass",`AASA contains ${c}.`):G("fail",`AASA does not contain ${c}.`)),i}function Ng(e,t,n="fail"){let i=[],r=oc(e,t),o=Mt.join(r,".well-known/assetlinks.json");if(!t.android?.package)return i.push(G("warn","Skipping Android Asset Links verification because android.package is missing.")),i;if(!Lt.existsSync(o))return i.push(G(n,`${Mt.relative(e,o)} is missing.`)),i;let s=Oo(o);if(!s||!Array.isArray(s))return i.push(G("fail","assetlinks.json is not a valid JSON array.")),i;let l=s.find(c=>c?.target?.package_name===t.android?.package);i.push(l?G("pass",`assetlinks.json contains ${t.android.package}.`):G("fail",`assetlinks.json does not contain ${t.android.package}.`));let a=l?.target?.sha256_cert_fingerprints;return i.push(Array.isArray(a)&&a.length>0?G("pass","assetlinks.json has at least one SHA256 fingerprint."):G("warn","assetlinks.json has no SHA256 fingerprints.")),i}function xg(e,t,n,i){if(t.framework!=="expo")return[];let r=[],o=Ro(e),l=(Array.isArray(o?.ios?.associatedDomains)?o.ios.associatedDomains:[]).includes(`applinks:${n.domain}`);r.push(l?G("pass",`Expo iOS config contains applinks:${n.domain}.`):G(i?"warn":"fail",`Expo iOS config does not contain applinks:${n.domain}.`));let c=(Array.isArray(o?.android?.intentFilters)?o.android.intentFilters:[]).flatMap(d=>Array.isArray(d?.data)?d.data:[]).some(d=>d?.scheme==="https"&&d.host===n.domain);if(r.push(c?G("pass",`Expo Android config contains App Link host ${n.domain}.`):G(i?"warn":"fail",`Expo Android config does not contain App Link host ${n.domain}.`)),i)return r.push(G("warn","mach.config.json is missing deepLinks; values were inferred from Expo config. Run `mach deeplink setup` to persist Mach metadata and generate hosted files.")),r;let u=nc(e);return u==="wired"?r.push(G("pass","Expo deep link plugin is wired into Expo config and derives values from mach.config.json.")):u==="manual"&&l&&c?r.push(G("pass","Expo resolved config contains the deep-link settings from the dynamic config.")):u==="manual"?r.push(G("warn","Expo deep link plugin exists, but dynamic or missing Expo config means manual plugin insertion must be confirmed.")):l&&c?r.push(G("warn","Expo plugin is not wired, but resolved Expo config already contains the deep-link settings.")):r.push(G("fail","Expo deep link plugin is missing or not wired into Expo config.")),r}function Lg(e,t,n){if(t.framework!=="react-native")return[];let i=[],r=Mt.join(e,"android/app/src/main/AndroidManifest.xml");if(Lt.existsSync(r)){let s=Lt.readFileSync(r,"utf-8");i.push(s.includes(`android:host="${n.domain}"`)?G("pass","AndroidManifest.xml contains the App Links host."):G("warn","AndroidManifest.xml does not contain the App Links host. Native patching is planned for a later phase."))}let o=Mt.join(e,"ios");if(Lt.existsSync(o)){let l=Lt.readdirSync(o,{recursive:!0}).filter(a=>typeof a=="string"&&a.endsWith(".entitlements")).some(a=>Lt.readFileSync(Mt.join(o,a),"utf-8").includes(`applinks:${n.domain}`));i.push(l?G("pass","iOS entitlements contain the associated domain."):G("warn","iOS entitlements do not contain the associated domain. Native patching is planned for a later phase."))}return i}async function Do(e,t){let n=[];try{let i=await fetch(e,{redirect:"manual"});if(i.status>=300&&i.status<400&&!t)return n.push(G("fail",`${e} redirects. Apple AASA endpoints must not redirect.`)),n;n.push(i.status===200?G("pass",`${e} returned HTTP 200.`):G("fail",`${e} returned HTTP ${i.status}.`));let r=i.headers.get("content-type")||"";!r.includes("application/json")&&!r.includes("text/json")&&n.push(G("warn",`${e} content-type is "${r||"missing"}"; application/json is recommended.`)),JSON.parse(await i.text()),n.push(G("pass",`${e} contains valid JSON.`))}catch(i){n.push(G("fail",`${e} check failed: ${i.message}`))}return n}async function Mg(e){let t=e.domain;return(await Promise.all([Do(`https://${t}/.well-known/apple-app-site-association`,!1),Do(`https://${t}/apple-app-site-association`,!1),Do(`https://${t}/.well-known/assetlinks.json`,!0)])).flat()}function Ug(e){let t=e.android?.package;if(!t)return[G("warn","Cannot print Android device verification commands because android.package is missing.")];let i=kg("adb",["version"],{encoding:"utf-8"}).status===0?"Run":"Install Android platform-tools, then run";return[G("info",`${i}: adb shell pm set-app-links --package ${t} 0 all`),G("info",`${i}: adb shell pm verify-app-links --re-verify ${t}`),G("info",`${i}: adb shell pm get-app-links ${t}`)]}async function Co(e,t,n){let i=n.profile||t.deepLinks?.profile||"production",r=t.framework==="expo"?Ji(e):null,o=t.framework==="expo"?Ki(t,r,i,e):null,s=!t.deepLinks&&!!o,l=t.deepLinks?zi(t,{profile:i,cwd:e,expoHints:r}):o,a=[];if(!l)return[G("fail","mach.config.json is missing deepLinks and Mach could not infer existing deep-link settings. Run `mach deeplink setup`.")];a.push(l.baseUrl?G("pass","deepLinks.baseUrl is configured."):G("fail","deepLinks.baseUrl is missing.")),a.push(l.domain?G("pass","deepLinks.domain is configured."):G("fail","deepLinks.domain is missing.")),a.push(l.scheme?G("pass","deepLinks.scheme is configured."):G("fail","deepLinks.scheme is missing."));let c=s?"warn":"fail";return a.push(...Tg(e,l,c)),a.push(...Ng(e,l,c)),a.push(...xg(e,t,l,s)),a.push(...Lg(e,t,l)),n.live&&a.push(...await Mg(l)),n.androidDevice&&a.push(...Ug(l)),a}var Xn=()=>process.stdin.isTTY&&process.stdout.isTTY,ac=`${m.npmScope}/mach`,Zn=()=>{Fg("Operation cancelled."),process.exit(0)},No=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},Et=(...e)=>{for(let t of e){let n=No(t);if(n)return n}},Yt=async(e,t,n,i,r)=>{if(t)return t;if(r||!Xn()){if(i)throw new Error(`${e.replace(/:$/,"")} is required. Pass it as an option or run interactively.`);return}let o=await cc({message:e,initialValue:t||"",placeholder:n,validate(s){if(i&&s.trim().length===0)return"Required."}});return qn(o)&&Zn(),No(o)};async function Gg(e,t,n){let i=Ve(e.framework),r=Ve(t);if(r)return r;if(i)return i;let o=Jl();if(n||!Xn())return o;let s=await jg({message:`Which framework should ${m.name} configure?`,initialValue:o,options:[{value:"expo",label:"Expo"},{value:"react-native",label:"React Native"},{value:"flutter",label:"Flutter (not supported yet)"}]});return qn(s)&&Zn(),s}var Yi=e=>e==="ios"||e==="all",ko=e=>e==="android"||e==="all";async function Vg(e,t,n){if(t!=="expo"||Wi(e)||n.installPlugin===!1)return!1;if(n.installPlugin===!0||n.yes)return!0;if(!Xn())return!1;let i=await lc({message:`Install ${ac} locally so Expo can use ${ac}/expo-plugin?`,initialValue:!0});return qn(i)&&Zn(),!!i}async function zg(e,t){let n=!!t.yes,i=t.profile||e.deepLinks?.profile||"production",r=Me(e,i)||{},o=r.ios||{},s=r.android||{},l=e.submit?.[i]?.ios||{},a=e.submit?.[i]?.android||{},c=Bt(e,i),u=await Gg(e,t.framework,n);if(u==="flutter")throw new Error("Flutter deep-link automation is planned but not supported yet.");let d=Yl(e,Wl(t.platform),r),f=e.deepLinks,g=u==="expo"&&(!Et(t.baseUrl,f?.baseUrl,t.domain?`https://${t.domain}`:void 0)||!Et(t.scheme,f?.scheme,o.scheme,e.scheme,e.slug,e.name)||Yi(d)&&!Et(t.iosBundleId,f?.ios?.bundleIdentifier,o.bundleIdentifier,o.bundleId,e.ios?.bundleIdentifier)||ko(d)&&!Et(t.androidPackage,f?.android?.package,s.package,s.applicationId,e.android?.package,a.packageName,a.package))?Ki(e,Ji(process.cwd()),i,process.cwd()):null,h=t.output||f?.web?.outputDir||Vi(process.cwd(),e),y=await Yt("Deep link base URL:",t.baseUrl||f?.baseUrl||(t.domain?`https://${t.domain}`:void 0)||g?.baseUrl,"https://app.example.com",!0,n),E=Wn(y),S=t.baseUrl?new URL(E).hostname:void 0,P=await Yt("Associated domain:",t.domain||S||f?.domain||g?.domain||new URL(E).hostname,"app.example.com",!0,n),v=Gi(await Yt("URI scheme:",Et(t.scheme,f?.scheme,o.scheme,e.scheme,g?.scheme,e.slug,e.name,"app"),"myapp",!0,n)||"app"),F=Yi(d)?await Yt("iOS bundle identifier:",Et(t.iosBundleId,f?.ios?.bundleIdentifier,o.bundleIdentifier,o.bundleId,e.ios?.bundleIdentifier,g?.ios?.bundleIdentifier),"com.example.app",!0,n):f?.ios?.bundleIdentifier||e.ios?.bundleIdentifier,H=Yi(d)?await Yt("Apple Team ID:",Et(t.appleTeamId,f?.ios?.appleTeamId,o.appleTeamId,o.developerTeamId,l.appleTeamId,l.developerTeamId,e.ios?.appleTeamId,e.ios?.developerTeamId,c.teamId),"ABCDE12345",!0,n):f?.ios?.appleTeamId||e.ios?.appleTeamId,Y=Yi(d)?await Yt("App Store ID (optional, for Smart App Banner):",Et(t.appStoreId,f?.ios?.appStoreId,o.ascAppId,l.ascAppId,e.ios?.ascAppId),"1234567890",!1,n):f?.ios?.appStoreId,I=ko(d)?await Yt("Android package name:",Et(t.androidPackage,f?.android?.package,s.package,s.applicationId,e.android?.package,a.packageName,a.package,g?.android?.package),"com.example.app",!0,n):f?.android?.package||e.android?.package,$=f?.android?.sha256CertFingerprints||[],M=t.androidFingerprint||[],k=e.android?.sha256CertFingerprints||[],A=s.sha256CertFingerprints||[],b=[...M,...$,...A,...k].filter(Boolean);if(ko(d)&&b.length===0&&!n&&Xn()){let U=await cc({message:"Android SHA256 fingerprint (optional now, required for verified App Links):",placeholder:"AA:BB:CC:..."});qn(U)&&Zn(),No(U)&&b.push(U)}let T=ql(e,{...t,baseUrl:E,domain:P,scheme:v,output:h,appleTeamId:H,appStoreId:Y,androidFingerprint:b});return T.ios={...T.ios||{},bundleIdentifier:F,appleTeamId:H,appStoreId:Y},T.android={...T.android||{},package:I,sha256CertFingerprints:Array.from(new Set(b))},{framework:u,platform:d,deepLinks:T}}var To=(e,t)=>{t.length!==0&&Bg(t.map(n=>`- ${n}`).join(`
1687
+ `),e)},dc=e=>e.filter(t=>t.level==="fail").length;function uc(e){for(let t of e){let n=t.level==="pass"?rt.green("PASS"):t.level==="warn"?rt.yellow("WARN"):t.level==="info"?rt.cyan("INFO"):rt.red("FAIL");qi.info(`${n} ${t.message}`)}}async function pc(e){try{let t=process.cwd(),n=sc.join(t,m.configFileName),i=ve()||{};if(!Hg.existsSync(n)){let E=e.yes||!Xn()?!0:await lc({message:`${m.configFileName} was not found. Create one with deep link settings?`,initialValue:!0});if(qn(E)&&Zn(),!E)return}let{framework:r,deepLinks:o}=await zg(i,e),s=e.profile||i.deepLinks?.profile||"production",l=Xl(i,r,o,s),a=Zl(l,t),c=rc(t,o),u=await Vg(t,r,e),d=r==="expo"?tc(t,o,{installPackage:u}):{written:[],skipped:[],notes:r==="react-native"?["React Native native patching is planned for a later phase. Hosted files were generated now."]:[]},f=[sc.relative(t,a),...c.written,...d.written],p=[...c.skipped,...d.skipped],g=[...c.notes,...d.notes];To("Written",f),To("Skipped",p),To("Notes",g);let h=await Co(t,l,{});uc(h);let y=dc(h);if(y>0){Xi(rt.yellow(`Deep link setup finished with ${y} verification issue(s).`));return}Xi(rt.green("Deep link setup complete."))}catch(t){qi.error(rt.red(t.message||"Deep link setup failed.")),process.exit(1)}}async function fc(e){try{let t=ve();t||(qi.error(rt.red(`No ${m.configFileName} found in the current directory.`)),process.exit(1));let n=await Co(process.cwd(),t,e);uc(n);let i=dc(n);i>0&&(Xi(rt.red(`Deep link verification failed with ${i} issue(s).`)),process.exit(1)),Xi(rt.green("Deep link verification passed."))}catch(t){qi.error(rt.red(t.message||"Deep link verification failed.")),process.exit(1)}}import{log as At,spinner as mc}from"@clack/prompts";import Ut from"chalk";var Kg=e=>{let t=String(e||"").trim().toLowerCase();if(t==="ios"||t==="android")return t;throw new Error("Pass --platform ios or --platform android.")},Wg=e=>{let t=Number(e);if(!Number.isFinite(t))throw new Error("Counter value must be a non-negative integer.");let n=Math.floor(t);if(n<0)throw new Error("Counter value must be a non-negative integer.");return n},Jg=(e,t,n)=>{let i=String(n.scope||"").trim();if(i)return i;let r=n.profile||"production",o=e==="android"?n.package||n.androidPackage:n.bundleId||n.iosBundleIdentifier;if(o)return String(o).trim();let s=e==="android"?si(t,r):ai(t,r);if(s)return s;throw new Error(e==="android"?`Could not resolve Android package. Set android.package in ${m.configFileName}, or pass --package/--scope.`:`Could not resolve iOS bundle identifier. Set ios.bundleIdentifier in ${m.configFileName}, or pass --bundle-id/--scope.`)},Yg=()=>{let e=ve();if(!e)throw new Error(`No ${m.configFileName} found in the current directory. Run \`${m.nameLower} init\` or \`${m.nameLower} link\` first.`);if(!e.projectId)throw new Error(`${m.configFileName} is missing projectId.`);return e},xo=e=>{At.info(Ut.bgBlue.white(" Native Version Counter ")),At.info(`${Ut.blue("Platform:")} ${e.platform}`),At.info(`${Ut.blue("Scope:")} ${e.scope}`),At.info(`${Ut.blue("Current DB value:")} ${e.current}`),At.info(`${Ut.blue("Next build will use:")} ${e.next}`),e.seeded||At.warn(Ut.yellow("This counter has not been seeded yet. Existing store apps should run `mach version seed` once before auto-version builds."))};async function Lo(e,t={}){if(!Ne()){At.error(`You are not logged in. Run \`${m.nameLower} login\` first.`);return}try{let i=Yg(),r=Kg(t.platform),o=Jg(r,i,t),s=String(i.projectId);if(e==="show"){let u=mc();t.json||u.start("Fetching native version counter...");let{data:d}=await N.get(`/projects/${s}/version-counters`,{params:{platform:r,scope:o}});if(d.status!=="success")throw new Error(d.message||"Failed to fetch version counter.");t.json?console.log(JSON.stringify(d.counter,null,2)):(u.stop("Fetched native version counter."),xo(d.counter));return}let l=Wg(t.value),a=mc();t.json||a.start("Seeding native version counter...");let{data:c}=await N.post(`/projects/${s}/version-counters/seed`,{platform:r,scope:o,value:l,force:!!t.force});if(c.status!=="success")throw new Error(c.message||"Failed to seed version counter.");t.json?console.log(JSON.stringify(c.counter,null,2)):(a.stop(`Seeded ${r} counter for ${o}.`),xo(c.counter))}catch(i){let r=i?.response?.data?.message||i?.message||"Version command failed.";if(i?.response?.data?.counter&&!t.json){At.error(Ut.red(r)),xo(i.response.data.counter);return}t.json?console.log(JSON.stringify({status:"error",message:r},null,2)):At.error(Ut.red(r))}}import{log as Je,spinner as ti,text as gc,select as hc,confirm as qg,isCancel as Qn,cancel as ei,outro as ni,note as Xg}from"@clack/prompts";import oe from"chalk";import{execSync as Zg}from"child_process";async function Qg(){try{let e=Zg("xcrun devicectl list devices --json-output -",{stdio:["pipe","pipe","pipe"]}).toString();return(JSON.parse(e).result?.devices||[]).filter(i=>i.connectionProperties?.transportType==="wired"||i.connectionProperties?.transportType==="localNetwork").map(i=>({udid:i.identifier,name:i.deviceProperties?.name||"Unknown Device",model:i.hardwareProperties?.marketingName||"iPhone"}))}catch{return[]}}var yc=e=>e?.trim().toUpperCase()||void 0;function Sc(e){let t=ve(),n=t?Bt(t):{},i=yc(e.teamId),r=i||n.teamId;return{...n,teamId:r,developerTeamId:i||n.developerTeamId||r}}async function eh(e){let t=e.udid,n=e.name,i=Sc(e),r=i.teamId;if(!t){let s=ti();s.start("Scanning for connected iOS devices...");let l=await Qg();if(s.stop(l.length>0?`Found ${l.length} device(s)`:"No devices detected"),l.length===1)t=l[0].udid,n=n||l[0].name,Je.info(`Detected: ${oe.bold(n)} (${oe.dim(t.substring(0,12)+"...")})`);else if(l.length>1){let a=await hc({message:"Select a device to register:",options:l.map(u=>({value:u.udid,label:`${u.name} (${u.model})`,hint:u.udid.substring(0,12)+"..."}))});if(Qn(a)){ei("Cancelled");return}t=a;let c=l.find(u=>u.udid===t);n=n||c.name}else{Je.info(oe.dim("No connected devices found. Enter UDID manually."));let a=await gc({message:"Device UDID:",placeholder:"00008101-XXXXXXXXXXXX",validate(c){if(c.length<10)return"UDID seems too short"}});if(Qn(a)){ei("Cancelled");return}t=a}}if(!n){let s=await gc({message:"Device name:",placeholder:"Nitesh's iPhone",validate(l){if(l.length===0)return"Name is required"}});if(Qn(s)){ei("Cancelled");return}n=s}let o=ti();o.start("Registering device...");try{await N.post("/devices",{udid:t,name:n,platform:"ios",addedVia:"cli",teamId:r,teamName:i.teamName,developerTeamId:i.developerTeamId,appStoreConnectTeamId:i.appStoreConnectTeamId,providerId:i.providerId,providerName:i.providerName}),o.stop(oe.green("Device registered!")),Je.info(` ${oe.bold("Name:")} ${n}`),Je.info(` ${oe.bold("UDID:")} ${oe.dim(t)}`),r&&Je.info(` ${oe.bold("Team:")} ${oe.dim(r)}`)}catch(s){o.stop(oe.red("Failed to register device")),Je.error(s.response?.data?.message||s.message)}ni(oe.blue("---"))}async function th(e={}){let t=yc(e.teamId),n=ti();n.start("Fetching registered devices...");try{let r=(await N.get("/devices",t?{params:{teamId:t}}:void 0)).data.devices||[];if(n.stop(`${r.length} device(s) registered${t?` for team ${t}`:""}`),r.length===0){Je.info(oe.dim(`No devices registered yet. Run \`${m.nameLower} device register\` to add one.`)),ni(oe.blue("---"));return}let o=r.map(s=>{let l=new Date(s.createdAt).toLocaleDateString();return` ${oe.bold(s.name.padEnd(24))} ${oe.dim(s.udid.padEnd(28))} ${oe.dim((s.teamId||"-").padEnd(12))} ${oe.cyan((s.addedVia||"-").padEnd(10))} ${oe.dim(l)}`});Xg(` ${oe.bold("Name".padEnd(24))} ${"UDID".padEnd(28)} ${"Team".padEnd(12)} ${"Via".padEnd(10)} Date
1688
1688
  `+o.join(`
1689
- `),"Registered Devices")}catch(i){n.stop(oe.red("Failed to fetch devices")),Je.error(i.response?.data?.message||i.message)}ni(oe.blue("---"))}async function ih(e){let t=e.udid,n=Sc(e).teamId;if(!t){let o=ti();o.start("Fetching registered devices...");try{let l=(await N.get("/devices",n?{params:{teamId:n}}:void 0)).data.devices||[];if(o.stop(`${l.length} device(s) found`),l.length===0){Je.info(oe.dim("No devices to remove.")),ni(oe.blue("---"));return}let a=await hc({message:"Select device to remove:",options:l.map(u=>({value:u.deviceKey||u.udid,label:u.name,hint:`${u.udid.substring(0,12)}...${u.teamId?` \xB7 ${u.teamId}`:""}`}))});if(Qn(a)){ei("Cancelled");return}let c=l.find(u=>(u.deviceKey||u.udid)===a);t=c?.udid||a,n=c?.teamId||n}catch(s){o.stop(oe.red("Failed to fetch devices")),Je.error(s.response?.data?.message||s.message);return}}let i=await Xg({message:`Remove device ${oe.dim(t.substring(0,12)+"...")}?`});if(Qn(i)||!i){ei("Cancelled");return}let r=ti();r.start("Removing device...");try{await N.delete(`/devices/${encodeURIComponent(t)}`,n?{params:{teamId:n}}:void 0),r.stop(oe.green("Device removed"))}catch(o){r.stop(oe.red("Failed to remove device")),Je.error(o.response?.data?.message||o.message)}ni(oe.blue("---"))}async function Zi(e,t){switch(e){case"register":await th(t);break;case"list":await nh(t);break;case"remove":await ih(t);break;default:Je.error(`Unknown action: ${e}`)}}import{log as qe,spinner as Wc}from"@clack/prompts";import st from"chalk";import lr from"path";import oi from"fs";import Mh from"os";import Uh from"axios";var Ye=(o=>(o.CRITICAL="critical",o.HIGH="high",o.MEDIUM="medium",o.LOW="low",o.INFO="info",o))(Ye||{}),ot=(a=>(a.PERMISSIONS="permissions",a.SECRETS="secrets",a.DEPENDENCIES="dependencies",a.STORAGE="storage",a.NETWORK="network",a.DEBUG="debug",a.MANIFEST="manifest",a.ATS="ats",a))(ot||{});import{spinner as Mc,log as ri}from"@clack/prompts";import Ge from"chalk";import{execSync as Rh}from"child_process";import Uc from"fs";import Fc from"path";import Ft from"fs";import ii from"path";import{execSync as rh}from"child_process";var bc={"android.permission.READ_PHONE_STATE":{severity:"high",description:"Can read phone number, IMEI, carrier info"},"android.permission.CALL_PHONE":{severity:"high",description:"Can make calls without user interaction"},"android.permission.SEND_SMS":{severity:"high",description:"Can send SMS without user interaction"},"android.permission.READ_SMS":{severity:"high",description:"Can read user SMS messages"},"android.permission.READ_CONTACTS":{severity:"medium",description:"Can read user contacts"},"android.permission.WRITE_CONTACTS":{severity:"medium",description:"Can modify user contacts"},"android.permission.READ_CALL_LOG":{severity:"high",description:"Can read call history"},"android.permission.CAMERA":{severity:"medium",description:"Camera access"},"android.permission.RECORD_AUDIO":{severity:"medium",description:"Microphone access"},"android.permission.ACCESS_FINE_LOCATION":{severity:"medium",description:"Precise GPS location"},"android.permission.ACCESS_COARSE_LOCATION":{severity:"medium",description:"Approximate location"},"android.permission.ACCESS_BACKGROUND_LOCATION":{severity:"high",description:"Background location tracking"},"android.permission.READ_EXTERNAL_STORAGE":{severity:"medium",description:"Read files on device"},"android.permission.WRITE_EXTERNAL_STORAGE":{severity:"medium",description:"Write files on device"},"android.permission.READ_MEDIA_IMAGES":{severity:"low",description:"Read photos"},"android.permission.READ_MEDIA_VIDEO":{severity:"low",description:"Read videos"},"android.permission.SYSTEM_ALERT_WINDOW":{severity:"high",description:"Draw overlays on other apps"},"android.permission.REQUEST_INSTALL_PACKAGES":{severity:"high",description:"Can install APKs"}},oh={NSCameraUsageDescription:{severity:"medium",description:"Camera access declared"},NSMicrophoneUsageDescription:{severity:"medium",description:"Microphone access declared"},NSLocationWhenInUseUsageDescription:{severity:"medium",description:"Location (in-use) declared"},NSLocationAlwaysUsageDescription:{severity:"high",description:"Background location declared"},NSContactsUsageDescription:{severity:"medium",description:"Contacts access declared"},NSCalendarsUsageDescription:{severity:"low",description:"Calendar access declared"},NSPhotoLibraryUsageDescription:{severity:"low",description:"Photo library access declared"}};function sh(e){try{let n=rh("npx expo config --type introspect --json 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:3e4});return JSON.parse(n)}catch{}let t=ii.join(e,"app.json");if(Ft.existsSync(t))try{let n=JSON.parse(Ft.readFileSync(t,"utf-8"));return n.expo||n}catch{}return null}function ah(e){let t=[],n=ii.join(e,"node_modules");if(!Ft.existsSync(n))return t;let i=/<uses-permission\s+android:name="([^"]+)"/g;function r(o,s){let l=ii.join(o,"android","src","main","AndroidManifest.xml");if(!Ft.existsSync(l))return;let a=Ft.readFileSync(l,"utf-8"),c;for(;(c=i.exec(a))!==null;)t.push({pkg:s,permission:c[1]});i.lastIndex=0}for(let o of Ft.readdirSync(n)){if(o.startsWith("."))continue;let s=ii.join(n,o);if(o.startsWith("@")){if(!Ft.statSync(s).isDirectory())continue;for(let l of Ft.readdirSync(s))r(ii.join(s,l),`${o}/${l}`)}else r(s,o)}return t}async function Ec(e,t){let n=[],i=1,r=sh(e),o=r?.android?.permissions||[],s=r?.android?.blockedPermissions||[];for(let c of o){let u=bc[c];u&&n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`${c.split(".").pop()} declared`,description:u.description,severity:u.severity,category:"permissions",file:"app.config.ts",recommendation:"Remove from permissions if not needed, or add justification."})}let l=ah(e);for(let{pkg:c,permission:u}of l){let d=bc[u];d&&(s.includes(u)||o.includes(u)||n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`${u.split(".").pop()} injected by ${c}`,description:`${d.description}. Auto-injected via ${c} AndroidManifest.xml`,severity:d.severity,category:"permissions",file:`node_modules/${c}/android/src/main/AndroidManifest.xml`,recommendation:`Add "${u}" to blockedPermissions if not needed.`,autoFixable:!0}))}let a=r?.ios?.infoPlist||{};for(let[c,u]of Object.entries(oh))c in a&&n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`iOS: ${c} declared`,description:u.description,severity:u.severity,category:"permissions",file:"app.config.ts",recommendation:`Remove ${c} from infoPlist if the app does not use this capability.`});return{category:"permissions",label:"Permission Audit",findings:n,durationMs:0}}import Mo from"fs";import Qi from"path";var lh=[{id:"SEC-001",pattern:/(['"`])(?:sk|pk)_(?:live|test)_[a-zA-Z0-9]{20,}\1/g,severity:"critical",label:"Stripe API Key"},{id:"SEC-002",pattern:/(['"`])AIza[0-9A-Za-z\-_]{35}\1/g,severity:"high",label:"Google API Key"},{id:"SEC-003",pattern:/(['"`])AKIA[0-9A-Z]{16}\1/g,severity:"critical",label:"AWS Access Key ID"},{id:"SEC-004",pattern:/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g,severity:"critical",label:"Private key in source"},{id:"SEC-005",pattern:/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/g,severity:"high",label:"GitHub Token"},{id:"SEC-006",pattern:/xox[bpors]-[a-zA-Z0-9-]{10,}/g,severity:"high",label:"Slack Token"},{id:"SEC-007",pattern:/(?:password|secret|apikey|api_key)\s*[:=]\s*['"`][^'"`\n]{8,}['"`]/gi,severity:"high",label:"Hardcoded credential"}],ch=new Set([".ts",".tsx",".js",".jsx"]),dh=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function Ac(e){let t=[],n=Mo.readdirSync(e,{withFileTypes:!0});for(let i of n){if(dh.has(i.name))continue;let r=Qi.join(e,i.name);i.isDirectory()?t.push(...Ac(r)):ch.has(Qi.extname(i.name))&&t.push(r)}return t}function uh(e){let t=e.trim();return t.startsWith("//")||t.startsWith("*")||t.startsWith("/*")}function ph(e){return/process\.env\./i.test(e)||/import\.meta\.env/i.test(e)}async function wc(e,t){let n=[],i=Qi.join(e,"src"),r=Mo.existsSync(i)?i:e,o=Ac(r);for(let s of o){let a=Mo.readFileSync(s,"utf-8").split(`
1690
- `),c=Qi.relative(e,s);for(let u=0;u<a.length;u++){let d=a[u];if(!uh(d)&&!ph(d))for(let{id:f,pattern:p,severity:g,label:h}of lh)p.lastIndex=0,p.test(d)&&n.push({id:f,title:h,description:`Potential ${h.toLowerCase()} found in source code`,severity:g,category:"secrets",file:c,line:u+1,recommendation:`Move to environment variables or a secrets manager. Use \`${m.nameLower} env set\` for managed secrets.`})}}return{category:"secrets",label:"Secrets Detection",findings:n,durationMs:0}}import{execSync as fh}from"child_process";var mh={critical:"critical",high:"high",moderate:"medium",low:"low",info:"info"};async function Ic(e,t){let n=[],i=1,r;try{r=fh("npm audit --json 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:6e4})}catch(l){if(r=l.stdout||"",!r)return{category:"dependencies",label:"Dependency CVEs",findings:[],durationMs:0,error:"Failed to run npm audit. Ensure npm is available and dependencies are installed."}}let o;try{o=JSON.parse(r)}catch{return{category:"dependencies",label:"Dependency CVEs",findings:[],durationMs:0,error:"Failed to parse npm audit output."}}let s=o.vulnerabilities||{};for(let[l,a]of Object.entries(s)){let c=mh[a.severity]||"info",u=Array.isArray(a.via)?a.via.map(d=>typeof d=="string"?d:d.title||d.name).join(", "):String(a.via);n.push({id:`DEP-${String(i++).padStart(3,"0")}`,title:`${l} \u2014 ${a.severity}`,description:`Via: ${u}. Range: ${a.range||"unknown"}`,severity:c,category:"dependencies",recommendation:a.fixAvailable?`Run \`npm audit fix\` or update ${l} to a patched version.`:"No fix available yet. Monitor for updates or evaluate alternatives.",autoFixable:!!a.fixAvailable})}return{category:"dependencies",label:"Dependency CVEs",findings:n,durationMs:0}}import er from"fs";import tr from"path";var gh=/(?:token|password|credential|secret|apiKey|api_key|session|auth|jwt|refresh_token|access_token|private_key|pin|otp)/i,vc=/AsyncStorage\s*\.\s*(?:setItem|mergeItem)\s*\(\s*['"`]([^'"`]+)['"`]/g,hh=/from\s+['"]@react-native-async-storage\/async-storage['"]|from\s+['"]react-native['"].*AsyncStorage/,yh=new Set([".ts",".tsx",".js",".jsx"]),Sh=new Set(["node_modules",".expo",".git","android","ios","dist","build"]);function _c(e){let t=[];if(!er.existsSync(e))return t;let n=er.readdirSync(e,{withFileTypes:!0});for(let i of n){if(Sh.has(i.name))continue;let r=tr.join(e,i.name);i.isDirectory()?t.push(..._c(r)):yh.has(tr.extname(i.name))&&t.push(r)}return t}async function $c(e,t){let n=[],i=1,r=tr.join(e,"src"),o=er.existsSync(r)?r:e,s=_c(o);for(let l of s){let a=er.readFileSync(l,"utf-8");if(!hh.test(a))continue;let c=a.split(`
1691
- `),u=tr.relative(e,l);for(let d=0;d<c.length;d++){let f=c[d];vc.lastIndex=0;let p;for(;(p=vc.exec(f))!==null;){let g=p[1];gh.test(g)&&n.push({id:`STOR-${String(i++).padStart(3,"0")}`,title:`AsyncStorage used for "${g}"`,description:`Sensitive data key "${g}" stored in AsyncStorage (unencrypted).`,severity:"high",category:"storage",file:u,line:d+1,recommendation:"Use expo-secure-store (SecureStore) for sensitive data instead of AsyncStorage."})}}}return{category:"storage",label:"Insecure Storage",findings:n,durationMs:0}}import nr from"fs";import ir from"path";var Rc=/['"`](http:\/\/(?!localhost|127\.0\.0\.1|10\.|192\.168\.|0\.0\.0\.0|172\.(?:1[6-9]|2\d|3[01])\.)[^'"`\s]+)['"`]/g,bh=/\/api\/|\/v[0-9]+\/|\/graphql|\/rest\//i,Eh=new Set([".ts",".tsx",".js",".jsx"]),Ah=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function Pc(e){let t=[];if(!nr.existsSync(e))return t;let n=nr.readdirSync(e,{withFileTypes:!0});for(let i of n){if(Ah.has(i.name))continue;let r=ir.join(e,i.name);i.isDirectory()?t.push(...Pc(r)):Eh.has(ir.extname(i.name))&&t.push(r)}return t}async function Oc(e,t){let n=[],i=1,r=ir.join(e,"src"),o=nr.existsSync(r)?r:e,s=Pc(o);for(let l of s){let c=nr.readFileSync(l,"utf-8").split(`
1692
- `),u=ir.relative(e,l);for(let d=0;d<c.length;d++){let f=c[d],p=f.trim();if(p.startsWith("//")||p.startsWith("*"))continue;Rc.lastIndex=0;let g;for(;(g=Rc.exec(f))!==null;){let h=g[1],y=bh.test(h);n.push({id:`NET-${String(i++).padStart(3,"0")}`,title:`Insecure HTTP URL${y?" (API endpoint)":""}`,description:`Plain HTTP URL found: ${h}`,severity:y?"high":"medium",category:"network",file:u,line:d+1,recommendation:"Use HTTPS instead of HTTP for all network requests."})}}}return{category:"network",label:"HTTP URLs",findings:n,durationMs:0}}import rr from"fs";import or from"path";var Dc=/(?:debug|debugMode|devMode|enableDebug)\s*[:=]\s*true/gi,Cc=/console\.(?:log|warn|info|debug)\s*\([^)]*(?:token|password|secret|credential|key|auth)/gi,wh=new Set([".ts",".tsx",".js",".jsx"]),Ih=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function kc(e){let t=[];if(!rr.existsSync(e))return t;let n=rr.readdirSync(e,{withFileTypes:!0});for(let i of n){if(Ih.has(i.name))continue;let r=or.join(e,i.name);i.isDirectory()?t.push(...kc(r)):wh.has(or.extname(i.name))&&t.push(r)}return t}async function Tc(e,t){let n=[],i=1,r=or.join(e,"src"),o=rr.existsSync(r)?r:e,s=kc(o);for(let l of s){let c=rr.readFileSync(l,"utf-8").split(`
1689
+ `),"Registered Devices")}catch(i){n.stop(oe.red("Failed to fetch devices")),Je.error(i.response?.data?.message||i.message)}ni(oe.blue("---"))}async function nh(e){let t=e.udid,n=Sc(e).teamId;if(!t){let o=ti();o.start("Fetching registered devices...");try{let l=(await N.get("/devices",n?{params:{teamId:n}}:void 0)).data.devices||[];if(o.stop(`${l.length} device(s) found`),l.length===0){Je.info(oe.dim("No devices to remove.")),ni(oe.blue("---"));return}let a=await hc({message:"Select device to remove:",options:l.map(u=>({value:u.deviceKey||u.udid,label:u.name,hint:`${u.udid.substring(0,12)}...${u.teamId?` \xB7 ${u.teamId}`:""}`}))});if(Qn(a)){ei("Cancelled");return}let c=l.find(u=>(u.deviceKey||u.udid)===a);t=c?.udid||a,n=c?.teamId||n}catch(s){o.stop(oe.red("Failed to fetch devices")),Je.error(s.response?.data?.message||s.message);return}}let i=await qg({message:`Remove device ${oe.dim(t.substring(0,12)+"...")}?`});if(Qn(i)||!i){ei("Cancelled");return}let r=ti();r.start("Removing device...");try{await N.delete(`/devices/${encodeURIComponent(t)}`,n?{params:{teamId:n}}:void 0),r.stop(oe.green("Device removed"))}catch(o){r.stop(oe.red("Failed to remove device")),Je.error(o.response?.data?.message||o.message)}ni(oe.blue("---"))}async function Zi(e,t){switch(e){case"register":await eh(t);break;case"list":await th(t);break;case"remove":await nh(t);break;default:Je.error(`Unknown action: ${e}`)}}import{log as qe,spinner as Wc}from"@clack/prompts";import st from"chalk";import lr from"path";import oi from"fs";import Lh from"os";import Mh from"axios";var Ye=(o=>(o.CRITICAL="critical",o.HIGH="high",o.MEDIUM="medium",o.LOW="low",o.INFO="info",o))(Ye||{}),ot=(a=>(a.PERMISSIONS="permissions",a.SECRETS="secrets",a.DEPENDENCIES="dependencies",a.STORAGE="storage",a.NETWORK="network",a.DEBUG="debug",a.MANIFEST="manifest",a.ATS="ats",a))(ot||{});import{spinner as Mc,log as ri}from"@clack/prompts";import Ge from"chalk";import{execSync as $h}from"child_process";import Uc from"fs";import Fc from"path";import Ft from"fs";import ii from"path";import{execSync as ih}from"child_process";var bc={"android.permission.READ_PHONE_STATE":{severity:"high",description:"Can read phone number, IMEI, carrier info"},"android.permission.CALL_PHONE":{severity:"high",description:"Can make calls without user interaction"},"android.permission.SEND_SMS":{severity:"high",description:"Can send SMS without user interaction"},"android.permission.READ_SMS":{severity:"high",description:"Can read user SMS messages"},"android.permission.READ_CONTACTS":{severity:"medium",description:"Can read user contacts"},"android.permission.WRITE_CONTACTS":{severity:"medium",description:"Can modify user contacts"},"android.permission.READ_CALL_LOG":{severity:"high",description:"Can read call history"},"android.permission.CAMERA":{severity:"medium",description:"Camera access"},"android.permission.RECORD_AUDIO":{severity:"medium",description:"Microphone access"},"android.permission.ACCESS_FINE_LOCATION":{severity:"medium",description:"Precise GPS location"},"android.permission.ACCESS_COARSE_LOCATION":{severity:"medium",description:"Approximate location"},"android.permission.ACCESS_BACKGROUND_LOCATION":{severity:"high",description:"Background location tracking"},"android.permission.READ_EXTERNAL_STORAGE":{severity:"medium",description:"Read files on device"},"android.permission.WRITE_EXTERNAL_STORAGE":{severity:"medium",description:"Write files on device"},"android.permission.READ_MEDIA_IMAGES":{severity:"low",description:"Read photos"},"android.permission.READ_MEDIA_VIDEO":{severity:"low",description:"Read videos"},"android.permission.SYSTEM_ALERT_WINDOW":{severity:"high",description:"Draw overlays on other apps"},"android.permission.REQUEST_INSTALL_PACKAGES":{severity:"high",description:"Can install APKs"}},rh={NSCameraUsageDescription:{severity:"medium",description:"Camera access declared"},NSMicrophoneUsageDescription:{severity:"medium",description:"Microphone access declared"},NSLocationWhenInUseUsageDescription:{severity:"medium",description:"Location (in-use) declared"},NSLocationAlwaysUsageDescription:{severity:"high",description:"Background location declared"},NSContactsUsageDescription:{severity:"medium",description:"Contacts access declared"},NSCalendarsUsageDescription:{severity:"low",description:"Calendar access declared"},NSPhotoLibraryUsageDescription:{severity:"low",description:"Photo library access declared"}};function oh(e){try{let n=ih("npx expo config --type introspect --json 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:3e4});return JSON.parse(n)}catch{}let t=ii.join(e,"app.json");if(Ft.existsSync(t))try{let n=JSON.parse(Ft.readFileSync(t,"utf-8"));return n.expo||n}catch{}return null}function sh(e){let t=[],n=ii.join(e,"node_modules");if(!Ft.existsSync(n))return t;let i=/<uses-permission\s+android:name="([^"]+)"/g;function r(o,s){let l=ii.join(o,"android","src","main","AndroidManifest.xml");if(!Ft.existsSync(l))return;let a=Ft.readFileSync(l,"utf-8"),c;for(;(c=i.exec(a))!==null;)t.push({pkg:s,permission:c[1]});i.lastIndex=0}for(let o of Ft.readdirSync(n)){if(o.startsWith("."))continue;let s=ii.join(n,o);if(o.startsWith("@")){if(!Ft.statSync(s).isDirectory())continue;for(let l of Ft.readdirSync(s))r(ii.join(s,l),`${o}/${l}`)}else r(s,o)}return t}async function Ec(e,t){let n=[],i=1,r=oh(e),o=r?.android?.permissions||[],s=r?.android?.blockedPermissions||[];for(let c of o){let u=bc[c];u&&n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`${c.split(".").pop()} declared`,description:u.description,severity:u.severity,category:"permissions",file:"app.config.ts",recommendation:"Remove from permissions if not needed, or add justification."})}let l=sh(e);for(let{pkg:c,permission:u}of l){let d=bc[u];d&&(s.includes(u)||o.includes(u)||n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`${u.split(".").pop()} injected by ${c}`,description:`${d.description}. Auto-injected via ${c} AndroidManifest.xml`,severity:d.severity,category:"permissions",file:`node_modules/${c}/android/src/main/AndroidManifest.xml`,recommendation:`Add "${u}" to blockedPermissions if not needed.`,autoFixable:!0}))}let a=r?.ios?.infoPlist||{};for(let[c,u]of Object.entries(rh))c in a&&n.push({id:`PERM-${String(i++).padStart(3,"0")}`,title:`iOS: ${c} declared`,description:u.description,severity:u.severity,category:"permissions",file:"app.config.ts",recommendation:`Remove ${c} from infoPlist if the app does not use this capability.`});return{category:"permissions",label:"Permission Audit",findings:n,durationMs:0}}import Mo from"fs";import Qi from"path";var ah=[{id:"SEC-001",pattern:/(['"`])(?:sk|pk)_(?:live|test)_[a-zA-Z0-9]{20,}\1/g,severity:"critical",label:"Stripe API Key"},{id:"SEC-002",pattern:/(['"`])AIza[0-9A-Za-z\-_]{35}\1/g,severity:"high",label:"Google API Key"},{id:"SEC-003",pattern:/(['"`])AKIA[0-9A-Z]{16}\1/g,severity:"critical",label:"AWS Access Key ID"},{id:"SEC-004",pattern:/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g,severity:"critical",label:"Private key in source"},{id:"SEC-005",pattern:/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/g,severity:"high",label:"GitHub Token"},{id:"SEC-006",pattern:/xox[bpors]-[a-zA-Z0-9-]{10,}/g,severity:"high",label:"Slack Token"},{id:"SEC-007",pattern:/(?:password|secret|apikey|api_key)\s*[:=]\s*['"`][^'"`\n]{8,}['"`]/gi,severity:"high",label:"Hardcoded credential"}],lh=new Set([".ts",".tsx",".js",".jsx"]),ch=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function Ac(e){let t=[],n=Mo.readdirSync(e,{withFileTypes:!0});for(let i of n){if(ch.has(i.name))continue;let r=Qi.join(e,i.name);i.isDirectory()?t.push(...Ac(r)):lh.has(Qi.extname(i.name))&&t.push(r)}return t}function dh(e){let t=e.trim();return t.startsWith("//")||t.startsWith("*")||t.startsWith("/*")}function uh(e){return/process\.env\./i.test(e)||/import\.meta\.env/i.test(e)}async function wc(e,t){let n=[],i=Qi.join(e,"src"),r=Mo.existsSync(i)?i:e,o=Ac(r);for(let s of o){let a=Mo.readFileSync(s,"utf-8").split(`
1690
+ `),c=Qi.relative(e,s);for(let u=0;u<a.length;u++){let d=a[u];if(!dh(d)&&!uh(d))for(let{id:f,pattern:p,severity:g,label:h}of ah)p.lastIndex=0,p.test(d)&&n.push({id:f,title:h,description:`Potential ${h.toLowerCase()} found in source code`,severity:g,category:"secrets",file:c,line:u+1,recommendation:`Move to environment variables or a secrets manager. Use \`${m.nameLower} env set\` for managed secrets.`})}}return{category:"secrets",label:"Secrets Detection",findings:n,durationMs:0}}import{execSync as ph}from"child_process";var fh={critical:"critical",high:"high",moderate:"medium",low:"low",info:"info"};async function Ic(e,t){let n=[],i=1,r;try{r=ph("npm audit --json 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:6e4})}catch(l){if(r=l.stdout||"",!r)return{category:"dependencies",label:"Dependency CVEs",findings:[],durationMs:0,error:"Failed to run npm audit. Ensure npm is available and dependencies are installed."}}let o;try{o=JSON.parse(r)}catch{return{category:"dependencies",label:"Dependency CVEs",findings:[],durationMs:0,error:"Failed to parse npm audit output."}}let s=o.vulnerabilities||{};for(let[l,a]of Object.entries(s)){let c=fh[a.severity]||"info",u=Array.isArray(a.via)?a.via.map(d=>typeof d=="string"?d:d.title||d.name).join(", "):String(a.via);n.push({id:`DEP-${String(i++).padStart(3,"0")}`,title:`${l} \u2014 ${a.severity}`,description:`Via: ${u}. Range: ${a.range||"unknown"}`,severity:c,category:"dependencies",recommendation:a.fixAvailable?`Run \`npm audit fix\` or update ${l} to a patched version.`:"No fix available yet. Monitor for updates or evaluate alternatives.",autoFixable:!!a.fixAvailable})}return{category:"dependencies",label:"Dependency CVEs",findings:n,durationMs:0}}import er from"fs";import tr from"path";var mh=/(?:token|password|credential|secret|apiKey|api_key|session|auth|jwt|refresh_token|access_token|private_key|pin|otp)/i,vc=/AsyncStorage\s*\.\s*(?:setItem|mergeItem)\s*\(\s*['"`]([^'"`]+)['"`]/g,gh=/from\s+['"]@react-native-async-storage\/async-storage['"]|from\s+['"]react-native['"].*AsyncStorage/,hh=new Set([".ts",".tsx",".js",".jsx"]),yh=new Set(["node_modules",".expo",".git","android","ios","dist","build"]);function _c(e){let t=[];if(!er.existsSync(e))return t;let n=er.readdirSync(e,{withFileTypes:!0});for(let i of n){if(yh.has(i.name))continue;let r=tr.join(e,i.name);i.isDirectory()?t.push(..._c(r)):hh.has(tr.extname(i.name))&&t.push(r)}return t}async function $c(e,t){let n=[],i=1,r=tr.join(e,"src"),o=er.existsSync(r)?r:e,s=_c(o);for(let l of s){let a=er.readFileSync(l,"utf-8");if(!gh.test(a))continue;let c=a.split(`
1691
+ `),u=tr.relative(e,l);for(let d=0;d<c.length;d++){let f=c[d];vc.lastIndex=0;let p;for(;(p=vc.exec(f))!==null;){let g=p[1];mh.test(g)&&n.push({id:`STOR-${String(i++).padStart(3,"0")}`,title:`AsyncStorage used for "${g}"`,description:`Sensitive data key "${g}" stored in AsyncStorage (unencrypted).`,severity:"high",category:"storage",file:u,line:d+1,recommendation:"Use expo-secure-store (SecureStore) for sensitive data instead of AsyncStorage."})}}}return{category:"storage",label:"Insecure Storage",findings:n,durationMs:0}}import nr from"fs";import ir from"path";var Rc=/['"`](http:\/\/(?!localhost|127\.0\.0\.1|10\.|192\.168\.|0\.0\.0\.0|172\.(?:1[6-9]|2\d|3[01])\.)[^'"`\s]+)['"`]/g,Sh=/\/api\/|\/v[0-9]+\/|\/graphql|\/rest\//i,bh=new Set([".ts",".tsx",".js",".jsx"]),Eh=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function Pc(e){let t=[];if(!nr.existsSync(e))return t;let n=nr.readdirSync(e,{withFileTypes:!0});for(let i of n){if(Eh.has(i.name))continue;let r=ir.join(e,i.name);i.isDirectory()?t.push(...Pc(r)):bh.has(ir.extname(i.name))&&t.push(r)}return t}async function Oc(e,t){let n=[],i=1,r=ir.join(e,"src"),o=nr.existsSync(r)?r:e,s=Pc(o);for(let l of s){let c=nr.readFileSync(l,"utf-8").split(`
1692
+ `),u=ir.relative(e,l);for(let d=0;d<c.length;d++){let f=c[d],p=f.trim();if(p.startsWith("//")||p.startsWith("*"))continue;Rc.lastIndex=0;let g;for(;(g=Rc.exec(f))!==null;){let h=g[1],y=Sh.test(h);n.push({id:`NET-${String(i++).padStart(3,"0")}`,title:`Insecure HTTP URL${y?" (API endpoint)":""}`,description:`Plain HTTP URL found: ${h}`,severity:y?"high":"medium",category:"network",file:u,line:d+1,recommendation:"Use HTTPS instead of HTTP for all network requests."})}}}return{category:"network",label:"HTTP URLs",findings:n,durationMs:0}}import rr from"fs";import or from"path";var Dc=/(?:debug|debugMode|devMode|enableDebug)\s*[:=]\s*true/gi,Cc=/console\.(?:log|warn|info|debug)\s*\([^)]*(?:token|password|secret|credential|key|auth)/gi,Ah=new Set([".ts",".tsx",".js",".jsx"]),wh=new Set(["node_modules",".expo",".git","android","ios","dist","build","__tests__","__mocks__"]);function kc(e){let t=[];if(!rr.existsSync(e))return t;let n=rr.readdirSync(e,{withFileTypes:!0});for(let i of n){if(wh.has(i.name))continue;let r=or.join(e,i.name);i.isDirectory()?t.push(...kc(r)):Ah.has(or.extname(i.name))&&t.push(r)}return t}async function Tc(e,t){let n=[],i=1,r=or.join(e,"src"),o=rr.existsSync(r)?r:e,s=kc(o);for(let l of s){let c=rr.readFileSync(l,"utf-8").split(`
1693
1693
  `),u=or.relative(e,l);for(let d=0;d<c.length;d++){let f=c[d],p=f.trim();if(!(p.startsWith("//")||p.startsWith("*"))&&(Dc.lastIndex=0,Dc.test(f)&&n.push({id:`DBG-${String(i++).padStart(3,"0")}`,title:"Hardcoded debug flag",description:"Debug/dev mode enabled in source code",severity:"medium",category:"debug",file:u,line:d+1,recommendation:"Use __DEV__ or environment variables instead of hardcoded debug flags."}),Cc.lastIndex=0,Cc.test(f))){let g=c.slice(Math.max(0,d-3),d).join(`
1694
- `);/__DEV__/.test(g)||n.push({id:`DBG-${String(i++).padStart(3,"0")}`,title:"Sensitive data in console log",description:"Console log may expose sensitive data (token, password, etc.) in production",severity:"medium",category:"debug",file:u,line:d+1,recommendation:"Wrap in __DEV__ check or remove sensitive data from console output."})}}}return{category:"debug",label:"Debug/Dev Flags",findings:n,durationMs:0}}import Nc from"fs";import Uo from"path";var vh=new Set(["androidx.work.impl.background.systemjob.SystemJobService","androidx.work.impl.diagnostics.DiagnosticsReceiver","com.google.firebase.iid.FirebaseInstanceIdReceiver","com.google.android.gms.auth.api.signin.RevocationBoundService","androidx.profileinstaller.ProfileInstallReceiver","com.google.firebase.messaging.FirebaseMessagingService","com.google.android.gms.measurement.AppMeasurementService","com.google.android.gms.measurement.AppMeasurementJobService"]);function _h(e){let t=[Uo.join(e,"android","app","src","main","AndroidManifest.xml"),Uo.join(e,"android","AndroidManifest.xml")];for(let n of t)if(Nc.existsSync(n))return n;return null}async function xc(e,t){let n=[],i=1,r=_h(e);if(!r)return{category:"manifest",label:"Android Manifest",findings:[],durationMs:0,error:"No AndroidManifest.xml found. Run with --prebuild or run `expo prebuild` first."};let o=Nc.readFileSync(r,"utf-8"),s=Uo.relative(e,r),l=/<(activity|service|receiver|provider)\s+([^>]*?)(?:\/>|>)/gs,a;for(;(a=l.exec(o))!==null;){let c=a[1],u=a[2],f=u.match(/android:name="([^"]+)"/)?.[1]||"unknown";if(vh.has(f))continue;let p=/android:exported="true"/.test(u),g=/android:permission="/.test(u);p&&!g&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:`Exported ${c}: ${f.split(".").pop()}`,description:`${f} is exported=true without android:permission protection`,severity:"medium",category:"manifest",file:s,recommendation:'Set android:exported="false" or add an android:permission attribute.'})}return/android:allowBackup="true"/.test(o)&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:"allowBackup is enabled",description:'android:allowBackup="true" allows data extraction via ADB backup',severity:"medium",category:"manifest",file:s,recommendation:'Set android:allowBackup="false" via expo-build-properties plugin.',autoFixable:!0}),/android:debuggable="true"/.test(o)&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:"App is debuggable",description:'android:debuggable="true" allows attaching a debugger in production',severity:"critical",category:"manifest",file:s,recommendation:"Ensure debuggable is false for production builds. This is usually set automatically for release builds."}),{category:"manifest",label:"Android Manifest",findings:n,durationMs:0}}import sr from"fs";import Fo from"path";function $h(e){let t=Fo.join(e,"ios");if(!sr.existsSync(t))return null;let n=sr.readdirSync(t,{withFileTypes:!0});for(let i of n){if(!i.isDirectory()||i.name==="Pods"||i.name.startsWith("."))continue;let r=Fo.join(t,i.name,"Info.plist");if(sr.existsSync(r))return r}return null}async function Lc(e,t){let n=[],i=1,r=$h(e);if(!r)return{category:"ats",label:"iOS ATS Config",findings:[],durationMs:0,error:"No Info.plist found. Run with --prebuild or run `expo prebuild` first."};let o=sr.readFileSync(r,"utf-8"),s=Fo.relative(e,r),l=o.match(/<key>NSAppTransportSecurity<\/key>\s*<dict>([\s\S]*?)<\/dict>/);if(l){let a=l[1];/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/.test(a)&&n.push({id:`ATS-${String(i++).padStart(3,"0")}`,title:"ATS disabled \u2014 NSAllowsArbitraryLoads",description:"App Transport Security is completely disabled. All HTTP connections are allowed.",severity:"critical",category:"ats",file:s,recommendation:"Remove NSAllowsArbitraryLoads or set to false. Use domain-specific exceptions only if absolutely necessary."});let c=a.match(/<key>NSExceptionDomains<\/key>\s*<dict>([\s\S]*?)<\/dict>/);if(c){let u=c[1].match(/<key>([^<]+)<\/key>/g);if(u)for(let d of u){let f=d.replace(/<\/?key>/g,"");f!=="NSExceptionDomains"&&n.push({id:`ATS-${String(i++).padStart(3,"0")}`,title:`ATS exception: ${f}`,description:`ATS exception configured for ${f}`,severity:"low",category:"ats",file:s,recommendation:`Verify that the ATS exception for ${f} is necessary. Use HTTPS where possible.`})}}}return{category:"ats",label:"iOS ATS Config",findings:n,durationMs:0}}var Ph=[{category:"permissions",label:"Permission Audit",fn:Ec,requiresPrebuild:!1},{category:"secrets",label:"Secrets Detection",fn:wc,requiresPrebuild:!1},{category:"dependencies",label:"Dependency CVEs",fn:Ic,requiresPrebuild:!1},{category:"storage",label:"Insecure Storage",fn:$c,requiresPrebuild:!1},{category:"network",label:"HTTP URLs",fn:Oc,requiresPrebuild:!1},{category:"debug",label:"Debug/Dev Flags",fn:Tc,requiresPrebuild:!1},{category:"manifest",label:"Android Manifest",fn:xc,requiresPrebuild:!0},{category:"ats",label:"iOS ATS Config",fn:Lc,requiresPrebuild:!0}];async function Bc(e,t){let n=Ph;t.category&&(n=n.filter(s=>s.category===t.category),n.length===0&&(ri.error(Ge.red(`Unknown category: ${t.category}`)),ri.info(Ge.dim(`Available: ${Object.values(ot).join(", ")}`)),process.exit(1)));let i=n.filter(s=>!s.requiresPrebuild),r=n.filter(s=>s.requiresPrebuild),o=[];for(let s of i){let l=Mc();l.start(Ge.dim(`Running ${s.label}...`));try{let a=Date.now(),c=await s.fn(e,t);c.durationMs=Date.now()-a,o.push(c);let u=c.findings.length;l.stop(u>0?Ge.yellow(`${s.label} \u2014 ${u} finding(s)`):Ge.green(`${s.label} \u2014 clean`))}catch(a){l.stop(Ge.red(`${s.label} \u2014 error`)),o.push({category:s.category,label:s.label,findings:[],durationMs:0,error:a.message})}}if(r.length>0){let s=Fc.join(e,"android"),l=Fc.join(e,"ios"),a=Uc.existsSync(s)||Uc.existsSync(l);if(!a&&t.prebuild){t.json||ri.info(Ge.dim(`
1695
- Running expo prebuild for deeper analysis...`));try{Rh("npx expo prebuild --no-install",{cwd:e,stdio:"pipe"})}catch{return ri.warn(Ge.yellow("Prebuild failed \u2014 skipping manifest/plist checks.")),o}}else if(!a)return t.json||ri.info(Ge.dim(`
1696
- Skipping manifest/plist checks \u2014 no android/ios dirs. Use --prebuild to generate them.`)),o;for(let c of r){let u=Mc();u.start(Ge.dim(`Running ${c.label}...`));try{let d=Date.now(),f=await c.fn(e,t);f.durationMs=Date.now()-d,o.push(f);let p=f.findings.length;u.stop(p>0?Ge.yellow(`${c.label} \u2014 ${p} finding(s)`):Ge.green(`${c.label} \u2014 clean`))}catch(d){u.stop(Ge.red(`${c.label} \u2014 error`)),o.push({category:c.category,label:c.label,findings:[],durationMs:0,error:d.message})}}}return o}import $e from"chalk";import{log as we}from"@clack/prompts";var hn={critical:$e.bgRed.white.bold,high:$e.red.bold,medium:$e.yellow.bold,low:$e.blue,info:$e.dim},ar={critical:0,high:1,medium:2,low:3,info:4};function Oh(e,t){if(!t)return!0;let n=ar[t];return n===void 0?!0:ar[e]<=n}function jc(e,t){let n=0,i={critical:0,high:0,medium:0,low:0,info:0};for(let s of e){let l=s.findings.filter(c=>Oh(c.severity,t));if(l.length===0&&!s.error)continue;if(we.info(""),we.info($e.bold(` ${s.label}`)+$e.dim(` ${l.length} finding(s)`)),we.info($e.dim(" "+"\u2500".repeat(60))),s.error){we.error($e.red(` Error: ${s.error}`));continue}let a=[...l].sort((c,u)=>ar[c.severity]-ar[u.severity]);for(let c of a){let u=hn[c.severity](` ${c.severity.toUpperCase()} `);we.info(` ${u} ${$e.bold(c.id)} ${c.title}`),we.info($e.dim(` ${c.description}`)),c.file&&we.info($e.dim(` File: ${c.file}${c.line?`:${c.line}`:""}`)),we.info($e.cyan(` Fix: ${c.recommendation}`)),we.info(""),i[c.severity]++,n++}}we.info(""),we.info($e.bold(" Summary")),we.info($e.dim(" "+"\u2500".repeat(60))),we.info(` Total: ${$e.bold(String(n))} finding(s)`),we.info(` ${hn.critical(` ${i.critical} Critical `)} ${hn.high(` ${i.high} High `)} ${hn.medium(` ${i.medium} Medium `)} ${hn.low(` ${i.low} Low `)} ${hn.info(` ${i.info} Info `)}`);let r=i.critical>0,o=i.high>0;r||o?(we.info(""),we.error($e.red.bold(` FAIL \u2014 ${i.critical} critical, ${i.high} high issue(s) found`))):n>0?(we.info(""),we.warn($e.yellow(" PASS (with warnings) \u2014 no critical or high issues"))):(we.info(""),we.success($e.green.bold(" PASS \u2014 no issues found")))}function Hc(e,t){let n=e.flatMap(r=>r.findings),i={};for(let r of Object.values(Ye))i[r]=n.filter(o=>o.severity===r).length;return{version:"1.0.0",timestamp:new Date().toISOString(),project:t.name||"unknown",projectVersion:t.version||"0.0.0",summary:{total:n.length,...i},passed:i.critical===0&&i.high===0,checks:e.map(r=>({category:r.category,label:r.label,findingCount:r.findings.length,durationMs:r.durationMs,error:r.error||null})),findings:n}}import{log as Ie,spinner as Dh}from"@clack/prompts";import Se from"chalk";import wt from"fs";import Ch from"os";import yn from"path";import{execSync as kh}from"child_process";var Gc=16384,Th=1,Nh=Buffer.from([127,69,76,70]);async function Vc(e){Ie.info(Se.bold.underline("16KB Page Alignment Check")),console.log("");let t=yn.resolve(e);if(!wt.existsSync(t))return Ie.error(Se.red(`APK file not found: ${t}`)),!1;if(!t.endsWith(".apk"))return Ie.error(Se.red("The provided file is not an APK file.")),!1;let n=Dh();n.start("Extracting and analyzing native libraries...");let i=wt.mkdtempSync(yn.join(Ch.tmpdir(),"mach-16kb-"));try{try{kh(`unzip -o -q "${t}" "lib/arm64-v8a/*.so" "lib/x86_64/*.so" -d "${i}" 2>/dev/null`,{encoding:"utf-8",maxBuffer:50*1024*1024})}catch{}let r=zc(yn.join(i,"lib"));if(r.length===0)return n.stop("Analysis complete."),Ie.info(Se.yellow("No arm64-v8a or x86_64 native libraries found in the APK.")),Ie.info(Se.dim("16KB alignment check only applies to arm64-v8a and x86_64 architectures.")),!0;let o=[];for(let a of r){let c=yn.relative(i,a),u=Lh(c),d=yn.basename(a),f=xh(a);f!==null&&o.push({name:d,filePath:c,arch:u,align:f,compatible:f>=Gc})}if(n.stop("Analysis complete."),o.length===0)return Ie.info(Se.yellow("No valid ELF shared libraries found.")),!0;let s=o.filter(a=>!a.compatible),l=o.filter(a=>a.compatible);if(Ie.info(Se.dim(`Checked ${o.length} native librar${o.length===1?"y":"ies"}
1694
+ `);/__DEV__/.test(g)||n.push({id:`DBG-${String(i++).padStart(3,"0")}`,title:"Sensitive data in console log",description:"Console log may expose sensitive data (token, password, etc.) in production",severity:"medium",category:"debug",file:u,line:d+1,recommendation:"Wrap in __DEV__ check or remove sensitive data from console output."})}}}return{category:"debug",label:"Debug/Dev Flags",findings:n,durationMs:0}}import Nc from"fs";import Uo from"path";var Ih=new Set(["androidx.work.impl.background.systemjob.SystemJobService","androidx.work.impl.diagnostics.DiagnosticsReceiver","com.google.firebase.iid.FirebaseInstanceIdReceiver","com.google.android.gms.auth.api.signin.RevocationBoundService","androidx.profileinstaller.ProfileInstallReceiver","com.google.firebase.messaging.FirebaseMessagingService","com.google.android.gms.measurement.AppMeasurementService","com.google.android.gms.measurement.AppMeasurementJobService"]);function vh(e){let t=[Uo.join(e,"android","app","src","main","AndroidManifest.xml"),Uo.join(e,"android","AndroidManifest.xml")];for(let n of t)if(Nc.existsSync(n))return n;return null}async function xc(e,t){let n=[],i=1,r=vh(e);if(!r)return{category:"manifest",label:"Android Manifest",findings:[],durationMs:0,error:"No AndroidManifest.xml found. Run with --prebuild or run `expo prebuild` first."};let o=Nc.readFileSync(r,"utf-8"),s=Uo.relative(e,r),l=/<(activity|service|receiver|provider)\s+([^>]*?)(?:\/>|>)/gs,a;for(;(a=l.exec(o))!==null;){let c=a[1],u=a[2],f=u.match(/android:name="([^"]+)"/)?.[1]||"unknown";if(Ih.has(f))continue;let p=/android:exported="true"/.test(u),g=/android:permission="/.test(u);p&&!g&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:`Exported ${c}: ${f.split(".").pop()}`,description:`${f} is exported=true without android:permission protection`,severity:"medium",category:"manifest",file:s,recommendation:'Set android:exported="false" or add an android:permission attribute.'})}return/android:allowBackup="true"/.test(o)&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:"allowBackup is enabled",description:'android:allowBackup="true" allows data extraction via ADB backup',severity:"medium",category:"manifest",file:s,recommendation:'Set android:allowBackup="false" via expo-build-properties plugin.',autoFixable:!0}),/android:debuggable="true"/.test(o)&&n.push({id:`MAN-${String(i++).padStart(3,"0")}`,title:"App is debuggable",description:'android:debuggable="true" allows attaching a debugger in production',severity:"critical",category:"manifest",file:s,recommendation:"Ensure debuggable is false for production builds. This is usually set automatically for release builds."}),{category:"manifest",label:"Android Manifest",findings:n,durationMs:0}}import sr from"fs";import Fo from"path";function _h(e){let t=Fo.join(e,"ios");if(!sr.existsSync(t))return null;let n=sr.readdirSync(t,{withFileTypes:!0});for(let i of n){if(!i.isDirectory()||i.name==="Pods"||i.name.startsWith("."))continue;let r=Fo.join(t,i.name,"Info.plist");if(sr.existsSync(r))return r}return null}async function Lc(e,t){let n=[],i=1,r=_h(e);if(!r)return{category:"ats",label:"iOS ATS Config",findings:[],durationMs:0,error:"No Info.plist found. Run with --prebuild or run `expo prebuild` first."};let o=sr.readFileSync(r,"utf-8"),s=Fo.relative(e,r),l=o.match(/<key>NSAppTransportSecurity<\/key>\s*<dict>([\s\S]*?)<\/dict>/);if(l){let a=l[1];/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/.test(a)&&n.push({id:`ATS-${String(i++).padStart(3,"0")}`,title:"ATS disabled \u2014 NSAllowsArbitraryLoads",description:"App Transport Security is completely disabled. All HTTP connections are allowed.",severity:"critical",category:"ats",file:s,recommendation:"Remove NSAllowsArbitraryLoads or set to false. Use domain-specific exceptions only if absolutely necessary."});let c=a.match(/<key>NSExceptionDomains<\/key>\s*<dict>([\s\S]*?)<\/dict>/);if(c){let u=c[1].match(/<key>([^<]+)<\/key>/g);if(u)for(let d of u){let f=d.replace(/<\/?key>/g,"");f!=="NSExceptionDomains"&&n.push({id:`ATS-${String(i++).padStart(3,"0")}`,title:`ATS exception: ${f}`,description:`ATS exception configured for ${f}`,severity:"low",category:"ats",file:s,recommendation:`Verify that the ATS exception for ${f} is necessary. Use HTTPS where possible.`})}}}return{category:"ats",label:"iOS ATS Config",findings:n,durationMs:0}}var Rh=[{category:"permissions",label:"Permission Audit",fn:Ec,requiresPrebuild:!1},{category:"secrets",label:"Secrets Detection",fn:wc,requiresPrebuild:!1},{category:"dependencies",label:"Dependency CVEs",fn:Ic,requiresPrebuild:!1},{category:"storage",label:"Insecure Storage",fn:$c,requiresPrebuild:!1},{category:"network",label:"HTTP URLs",fn:Oc,requiresPrebuild:!1},{category:"debug",label:"Debug/Dev Flags",fn:Tc,requiresPrebuild:!1},{category:"manifest",label:"Android Manifest",fn:xc,requiresPrebuild:!0},{category:"ats",label:"iOS ATS Config",fn:Lc,requiresPrebuild:!0}];async function Bc(e,t){let n=Rh;t.category&&(n=n.filter(s=>s.category===t.category),n.length===0&&(ri.error(Ge.red(`Unknown category: ${t.category}`)),ri.info(Ge.dim(`Available: ${Object.values(ot).join(", ")}`)),process.exit(1)));let i=n.filter(s=>!s.requiresPrebuild),r=n.filter(s=>s.requiresPrebuild),o=[];for(let s of i){let l=Mc();l.start(Ge.dim(`Running ${s.label}...`));try{let a=Date.now(),c=await s.fn(e,t);c.durationMs=Date.now()-a,o.push(c);let u=c.findings.length;l.stop(u>0?Ge.yellow(`${s.label} \u2014 ${u} finding(s)`):Ge.green(`${s.label} \u2014 clean`))}catch(a){l.stop(Ge.red(`${s.label} \u2014 error`)),o.push({category:s.category,label:s.label,findings:[],durationMs:0,error:a.message})}}if(r.length>0){let s=Fc.join(e,"android"),l=Fc.join(e,"ios"),a=Uc.existsSync(s)||Uc.existsSync(l);if(!a&&t.prebuild){t.json||ri.info(Ge.dim(`
1695
+ Running expo prebuild for deeper analysis...`));try{$h("npx expo prebuild --no-install",{cwd:e,stdio:"pipe"})}catch{return ri.warn(Ge.yellow("Prebuild failed \u2014 skipping manifest/plist checks.")),o}}else if(!a)return t.json||ri.info(Ge.dim(`
1696
+ Skipping manifest/plist checks \u2014 no android/ios dirs. Use --prebuild to generate them.`)),o;for(let c of r){let u=Mc();u.start(Ge.dim(`Running ${c.label}...`));try{let d=Date.now(),f=await c.fn(e,t);f.durationMs=Date.now()-d,o.push(f);let p=f.findings.length;u.stop(p>0?Ge.yellow(`${c.label} \u2014 ${p} finding(s)`):Ge.green(`${c.label} \u2014 clean`))}catch(d){u.stop(Ge.red(`${c.label} \u2014 error`)),o.push({category:c.category,label:c.label,findings:[],durationMs:0,error:d.message})}}}return o}import Re from"chalk";import{log as we}from"@clack/prompts";var hn={critical:Re.bgRed.white.bold,high:Re.red.bold,medium:Re.yellow.bold,low:Re.blue,info:Re.dim},ar={critical:0,high:1,medium:2,low:3,info:4};function Ph(e,t){if(!t)return!0;let n=ar[t];return n===void 0?!0:ar[e]<=n}function jc(e,t){let n=0,i={critical:0,high:0,medium:0,low:0,info:0};for(let s of e){let l=s.findings.filter(c=>Ph(c.severity,t));if(l.length===0&&!s.error)continue;if(we.info(""),we.info(Re.bold(` ${s.label}`)+Re.dim(` ${l.length} finding(s)`)),we.info(Re.dim(" "+"\u2500".repeat(60))),s.error){we.error(Re.red(` Error: ${s.error}`));continue}let a=[...l].sort((c,u)=>ar[c.severity]-ar[u.severity]);for(let c of a){let u=hn[c.severity](` ${c.severity.toUpperCase()} `);we.info(` ${u} ${Re.bold(c.id)} ${c.title}`),we.info(Re.dim(` ${c.description}`)),c.file&&we.info(Re.dim(` File: ${c.file}${c.line?`:${c.line}`:""}`)),we.info(Re.cyan(` Fix: ${c.recommendation}`)),we.info(""),i[c.severity]++,n++}}we.info(""),we.info(Re.bold(" Summary")),we.info(Re.dim(" "+"\u2500".repeat(60))),we.info(` Total: ${Re.bold(String(n))} finding(s)`),we.info(` ${hn.critical(` ${i.critical} Critical `)} ${hn.high(` ${i.high} High `)} ${hn.medium(` ${i.medium} Medium `)} ${hn.low(` ${i.low} Low `)} ${hn.info(` ${i.info} Info `)}`);let r=i.critical>0,o=i.high>0;r||o?(we.info(""),we.error(Re.red.bold(` FAIL \u2014 ${i.critical} critical, ${i.high} high issue(s) found`))):n>0?(we.info(""),we.warn(Re.yellow(" PASS (with warnings) \u2014 no critical or high issues"))):(we.info(""),we.success(Re.green.bold(" PASS \u2014 no issues found")))}function Hc(e,t){let n=e.flatMap(r=>r.findings),i={};for(let r of Object.values(Ye))i[r]=n.filter(o=>o.severity===r).length;return{version:"1.0.0",timestamp:new Date().toISOString(),project:t.name||"unknown",projectVersion:t.version||"0.0.0",summary:{total:n.length,...i},passed:i.critical===0&&i.high===0,checks:e.map(r=>({category:r.category,label:r.label,findingCount:r.findings.length,durationMs:r.durationMs,error:r.error||null})),findings:n}}import{log as Ie,spinner as Oh}from"@clack/prompts";import Se from"chalk";import wt from"fs";import Dh from"os";import yn from"path";import{execSync as Ch}from"child_process";var Gc=16384,kh=1,Th=Buffer.from([127,69,76,70]);async function Vc(e){Ie.info(Se.bold.underline("16KB Page Alignment Check")),console.log("");let t=yn.resolve(e);if(!wt.existsSync(t))return Ie.error(Se.red(`APK file not found: ${t}`)),!1;if(!t.endsWith(".apk"))return Ie.error(Se.red("The provided file is not an APK file.")),!1;let n=Oh();n.start("Extracting and analyzing native libraries...");let i=wt.mkdtempSync(yn.join(Dh.tmpdir(),"mach-16kb-"));try{try{Ch(`unzip -o -q "${t}" "lib/arm64-v8a/*.so" "lib/x86_64/*.so" -d "${i}" 2>/dev/null`,{encoding:"utf-8",maxBuffer:50*1024*1024})}catch{}let r=zc(yn.join(i,"lib"));if(r.length===0)return n.stop("Analysis complete."),Ie.info(Se.yellow("No arm64-v8a or x86_64 native libraries found in the APK.")),Ie.info(Se.dim("16KB alignment check only applies to arm64-v8a and x86_64 architectures.")),!0;let o=[];for(let a of r){let c=yn.relative(i,a),u=xh(c),d=yn.basename(a),f=Nh(a);f!==null&&o.push({name:d,filePath:c,arch:u,align:f,compatible:f>=Gc})}if(n.stop("Analysis complete."),o.length===0)return Ie.info(Se.yellow("No valid ELF shared libraries found.")),!0;let s=o.filter(a=>!a.compatible),l=o.filter(a=>a.compatible);if(Ie.info(Se.dim(`Checked ${o.length} native librar${o.length===1?"y":"ies"}
1697
1697
  `)),s.length>0){Ie.info(Se.red.bold(`Incompatible libraries:
1698
- `));for(let a of s)Ie.error(Se.red(`${Se.bold(a.name)} ${Se.dim(`(${a.arch})`)}`)),Ie.info(Se.dim(` PT_LOAD alignment: 2**${Math.log2(a.align)} (${a.align} bytes, needs 2**14 = ${Gc})`))}return l.length>0&&(console.log(""),Ie.success(Se.green(`${l.length} librar${l.length===1?"y":"ies"} correctly aligned (2**14 or higher).`))),console.log(""),s.length===0?(Ie.success(Se.green.bold("ELF Verification Successful. APK is 16KB compatible.")),!0):(Ie.error(Se.red.bold(`Found ${s.length} unaligned lib${s.length===1?"":"s"} (arm64-v8a/x86_64 libs need to be aligned).`)),console.log(""),Ie.info(Se.yellow("To fix this:")),Ie.info(Se.dim(" 1. Rebuild with NDK r28+ which defaults to 16KB page alignment")),Ie.info(Se.dim(" 2. Or set -DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON in CMake")),Ie.info(Se.dim(" 3. Update third-party libraries that ship prebuilt .so files")),Ie.info(Se.dim(" 4. After rebuilding: zipalign -p -f -v 16 input.apk output.apk")),console.log(""),Ie.info(Se.dim("Reference: https://developer.android.com/guide/practices/page-sizes")),!1)}catch(r){return n.stop("Analysis failed."),Ie.error(Se.red(`Failed to analyze APK: ${r.message}`)),!1}finally{try{wt.rmSync(i,{recursive:!0,force:!0})}catch{}}}function xh(e){let t=wt.openSync(e,"r");try{let n=Buffer.alloc(64);if(wt.readSync(t,n,0,64,0),!n.subarray(0,4).equals(Nh))return null;let i=n[4]===2,r=n[5]===1,o=(f,p)=>r?f.readUInt16LE(p):f.readUInt16BE(p),s=(f,p)=>r?f.readUInt32LE(p):f.readUInt32BE(p),l=(f,p)=>Number(r?f.readBigUInt64LE(p):f.readBigUInt64BE(p)),a,c,u;i?(a=l(n,32),c=o(n,54),u=o(n,56)):(a=s(n,28),c=o(n,42),u=o(n,44));let d=null;for(let f=0;f<u;f++){let p=Buffer.alloc(c);if(wt.readSync(t,p,0,c,a+f*c),s(p,0)!==Th)continue;let h;i?h=l(p,48):h=s(p,28),h>0&&(d===null||h<d)&&(d=h)}return d??0}catch{return null}finally{wt.closeSync(t)}}function zc(e){let t=[];if(!wt.existsSync(e))return t;let n=wt.readdirSync(e,{withFileTypes:!0});for(let i of n){let r=yn.join(e,i.name);i.isDirectory()?t.push(...zc(r)):i.name.endsWith(".so")&&t.push(r)}return t}function Lh(e){return e.includes("arm64-v8a")?"arm64-v8a":e.includes("x86_64")?"x86_64":"unknown"}var Jc=async e=>{if(Ne()||(qe.error(st.red(`You are not logged in. Run \`${m.nameLower} login\` first.`)),process.exit(1)),e.apk){await Fh(e);return}let n=process.cwd(),i=lr.join(n,"package.json");oi.existsSync(i)||(qe.error(st.red("No package.json found. Run this command from a project root.")),process.exit(1));let r=JSON.parse(oi.readFileSync(i,"utf-8")),o=!!(r.dependencies?.expo||r.devDependencies?.expo);e.json||(qe.info(st.dim(`Project: ${r.name||"unknown"} v${r.version||"0.0.0"}`)),qe.info(st.dim(`Type: ${o?"Expo":"Bare React Native"}`)),qe.info(""));let s={json:e.json,fix:e.fix,category:e.category,severity:e.severity,prebuild:e.prebuild},l=await Bc(n,s);e.json?console.log(JSON.stringify(Hc(l,r),null,2)):jc(l,e.severity);let a=l.some(u=>u.findings.some(d=>d.severity==="critical")),c=l.some(u=>u.findings.some(d=>d.severity==="high"));(a||c)&&process.exit(1)},Kc=["16kb"];async function Fh(e){let t=e.category?.toLowerCase();t&&!Kc.includes(t)&&(qe.error(st.red(`Unknown APK check category: "${t}"`)),qe.info(st.dim(`Available APK checks: ${Kc.join(", ")}`)),process.exit(1));let n=await Bh(e.apk);n||process.exit(1);let i=!t,r=!1;(i||t==="16kb")&&(await Vc(n)||(r=!0)),r&&process.exit(1)}async function Bh(e){if(e.startsWith("build:")){let n=e.slice(6);return await jh(n)}if(e.startsWith("http://")||e.startsWith("https://"))return await Yc(e);let t=lr.resolve(e);return oi.existsSync(t)?t:(qe.error(st.red(`APK file not found: ${t}`)),null)}async function jh(e){let t=Wc();t.start(`Fetching APK from build ${e}...`);try{let n=await N.get(`/build/${e}`);if(n.data.status!=="success")throw new Error(n.data.message);let i=n.data.build;if(!i)throw new Error("Build not found");let r=i.artifacts?.find(o=>o.type?o.type==="apk":o.name?.endsWith(".apk")||o.url?.includes(".apk"));return!r||!r.url?(t.stop("No APK artifact found"),qe.error(st.red("This build does not have an APK artifact.")),qe.info(st.dim("Make sure the build produced an APK (not just an AAB).")),null):(t.stop(`Found build: ${i.buildId||e}`),await Yc(r.url))}catch(n){return t.stop("Failed to fetch build"),qe.error(st.red(n.message)),null}}async function Yc(e){let t=Wc();t.start("Downloading APK...");try{let n=oi.mkdtempSync(lr.join(Mh.tmpdir(),`${m.nameLower}-audit-`)),i=lr.join(n,"app.apk"),r=await Uh.get(e,{responseType:"arraybuffer"});oi.writeFileSync(i,r.data);let o=(r.data.length/(1024*1024)).toFixed(1);return t.stop(`Downloaded APK (${o} MB)`),i}catch(n){return t.stop("Download failed"),qe.error(st.red(`Failed to download APK: ${n.message}`)),null}}var Vh=console.log;console.log=()=>{};Gh();console.log=Vh;de(ae.bgCyan.black(m.cliTag("Production Build Orchestrator")));var Bo=(e,t=[])=>t.concat(e),J=new Hh;J.name(m.nameLower).description(`${m.name} CLI: Powerful Cloud Infrastructure Tools`).version(Tt).option("-y, --yes","Automatically answer yes to all prompts",!1).option("--quiet","Suppress non-essential output",!1);J.command("agent").description("Manage local device discovery agent for the Install Hub (port 7070)").argument("[action]","start, run, install, status, restart, stop, or uninstall","start").action(async e=>{await dl(e)});J.command("host").description("Setup AWS S3 static hosting and CloudFront").action(async()=>{de(ae.bgCyan.black(m.cliTag("Hosting"))),await Zo()});J.command("sitemap").description("Generate sitemap.xml from Expo Router app structure").option("-o, --output <path>","Output path for sitemap.xml","./public/sitemap.xml").action(async e=>{de(ae.bgCyan.black(m.cliTag("Sitemap Generator"))),await ns(e)});J.command("build").description("Run a build on a cloud provider (AWS Spot Instances)").argument("[platform]","Target platform (android, ios)").option("--platform <os>","Target platform alias (android, ios) [Optional]").option("--repo <url>","Git Repository URL").option("--branch <name>","Git Branch").option("--ami <id>","AMI ID").option("--bucket <name>","S3 Bucket for artifacts").option("--region <region>","AWS Region").option("--aws-profile <name>","AWS Credentials Profile").option("-p, --build-profile <name>","Build Profile (e.g. production, staging)").option("--profile <name>","Build Profile (alias for --build-profile)").option("--dry-run","Generate User Data script without launching").option("--keep-instance","Do not terminate the instance after build").option("--subnet <id>","VPC Subnet ID to launch the instance in").option("--security-group <id>","VPC Security Group ID to use").option("--ssh-host <host>","Remote host for iOS build (SSH)").option("--ssh-user <user>","SSH username for remote host").option("--ssh-key <path>","Path to private SSH key for remote host").option("--ios-scheme <scheme>","iOS Scheme to build").option("--ios-team-id <id>","Apple Development Team ID").option("--ios-configuration <config>","Xcode configuration (Release/Debug)").option("--ios-export-method <method>","Export method (ad-hoc, app-store, development, enterprise)").option("--ios-api-key-id <id>","App Store Connect API Key ID").option("--ios-api-key-issuer <id>","App Store Connect API Key Issuer ID").option("--ios-api-key-path <path>","Path to App Store Connect API Key (.p8) file").option("--simulator","Build for iOS Simulator (no signing required)").addOption(new Xe("--build-id <id>","Internal build ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--fail-only","Internal: mark an existing build failed").hideHelp()).addOption(new Xe("--error <message>","Internal failure message").hideHelp()).option("--local","Run build on local machine").option("--auto-version","Assign the next Android versionCode or iOS buildNumber from Mach").option("--auto-submit","Submit this build after it completes successfully").option("--submit-track <track>","Google Play track to use with --auto-submit").option("--submit-release-status <status>","Google Play release status to use with --auto-submit").option("--submit-rollout <fraction>","Google Play staged rollout fraction to use with --auto-submit").option("--submit-changes-not-sent-for-review","Commit Google Play changes without automatically sending them for review when using --auto-submit").option("--submit-credential-id <id>","Service credential ID to use with --auto-submit").option("--submit-json-output <path>","Write auto-submit metadata JSON to a file for CI/CD").option("--verbose","Enable verbose logging").option("--json-output <path>","Write build metadata JSON to a file for CI/CD").option("--maestro","Queue a managed Maestro test run after a successful build").option("--maestro-flows <path>","Maestro flow file or directory (default: .maestro)").option("--maestro-device <device>","iOS simulator model or identifier (default: iPhone 17 Pro; managed Android uses Pixel 6)").option("--maestro-retries <n>","Retries for the Maestro run","1").option("--maestro-record","Enable screen recording where supported").option("--maestro-use-build-source","Use Maestro flows from the build source snapshot instead of uploading local flows").option("--maestro-env <name>","Forward an environment variable to the managed Maestro runner; repeat or comma-separate names",Bo,[]).option("-y, --yes","Automatically answer yes to all prompts",!1).option("--quiet","Suppress non-essential output",!1).action(async(e,t)=>{let n=J.opts();t.yes=t.yes||n.yes,t.quiet=t.quiet||n.quiet,t.platform=e||t.platform,t.buildProfile=t.profile||t.buildProfile,t.quiet||de(ae.bgCyan.black(m.cliTag("Cloud Build"))),await vi(t)});J.command("maestro").description("Run Maestro tests locally or with the managed Mach runner").option("--platform <os>","Target platform (android/ios)").option("-p, --profile <name>","Build profile to resolve when using --latest","production").option("--latest","Use the latest successful build for the platform/profile").option("--build-id <id>","Use a specific Mach build ID").option("--build-url <url>","Use a Mach build URL").option("--artifact-url <url>","Use a direct APK or iOS simulator app artifact URL").option("--local","Run Maestro on a connected local emulator/device instead of the managed runner").option("--maestro-flows <path>","Maestro flow file or directory (default: .maestro)").option("--maestro-device <device>","iOS simulator model or identifier (default: iPhone 17 Pro; managed Android uses Pixel 6)").option("--maestro-retries <n>","Retries for the Maestro run","1").option("--maestro-record","Enable screen recording where supported").option("--maestro-use-build-source","Use Maestro flows from the build source snapshot instead of uploading local flows").option("--maestro-env <name>","Forward an environment variable to the managed Maestro runner; repeat or comma-separate names",Bo,[]).option("--wait","Wait for managed Maestro completion and exit non-zero on failure").option("--wait-timeout <minutes>","Maximum minutes to wait for managed Maestro completion","60").option("--wait-interval <seconds>","Polling interval while waiting for managed Maestro completion","15").option("--json-output <path>","Write test run metadata JSON to a file for CI/CD").addOption(new Xe("--local-runner","Internal: execute a managed Maestro run on this machine").hideHelp()).addOption(new Xe("--test-run-id <id>","Internal test run ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--runner-token <token>","Internal runner token").hideHelp()).action(async e=>{de(ae.bgCyan.black(m.cliTag("Maestro Testing"))),await Ws(e)});var Sn=e=>e.option("--platform <os>","Target platform: ios, android, or all","all").option("--channel <name>","OTA channel (for example: production, staging)").option("--branch <name>","Alias for --channel").option("--runtime-version <version>","Runtime version override").option("--message <text>","Release note/message for this OTA update").option("--framework <name>","Framework override: expo or react-native").option("--engine <name>","OTA engine override (currently expo-updates)").option("--url <url>","Mach OTA update URL override").option("--install-plugin","Install @radhya/mach locally for the Expo config plugin").option("--skip-install","Do not install local Expo plugin dependency").option("--update-id <id>","OTA update id for rollback/promote").option("--json","Print JSON output"),bn=J.command("ota").description("Setup, publish, verify, and manage Mach OTA updates");Sn(bn.command("setup").description("Configure OTA for this project")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Setup"))),await Nt("setup",{...e,yes:e.yes||t.yes})});Sn(bn.command("publish").description("Publish an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Publish"))),await Nt("publish",{...e,yes:e.yes||t.yes})});Sn(bn.command("verify").description("Verify OTA configuration")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Verify"))),await Nt("verify",{...e,yes:e.yes||t.yes})});Sn(bn.command("list").description("List OTA updates for the linked project")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Updates"))),await Nt("list",{...e,yes:e.yes||t.yes})});Sn(bn.command("rollback").description("Roll back an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Rollback"))),await Nt("rollback",{...e,yes:e.yes||t.yes})});Sn(bn.command("promote").description("Promote an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Promote"))),await Nt("promote",{...e,yes:e.yes||t.yes})});J.command("submit").description("Automated Store Submission (App Store / Google Play)").option("--platform <os>","Target platform (ios/android)").option("-p, --profile <name>","Build/submission profile to submit from (e.g. production, preprod)").option("--track <track>","Google Play Track (internal, alpha, beta, production)").option("--release-status <status>","Google Play release status (completed, draft, halted, inProgress)").option("--rollout <fraction>","Google Play staged rollout fraction for inProgress releases (example: 0.1)").option("--changes-not-sent-for-review","Commit Google Play changes without automatically sending them for review").option("--ssh-host <host>","Remote macOS host for iOS submission").option("--ssh-user <user>","SSH username for remote iOS submission").option("--ssh-key <path>","Path to private SSH key for remote iOS submission").option("--credential-id <id>","Service credential ID to use when multiple submission credentials exist").option("--local","Run submission on this machine (internal runner mode for iOS)").addOption(new Xe("--submit-id <id>","Internal submission ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--package-name <name>","Internal Android package name").hideHelp()).addOption(new Xe("--bundle-id <id>","Internal iOS bundle identifier").hideHelp()).option("--json-output <path>","Write submission metadata JSON to a file for CI/CD").option("--latest","Use the latest successful build for submission").option("--build-id <id>","Use a specific build ID for submission").action(async e=>{let t=J.opts();e.quiet=e.quiet||t.quiet,e.yes=e.yes||t.yes,de(ae.bgCyan.black(m.cliTag("Store Submission"))),await Si(e)});J.command("login").description(`Login to ${m.name} Dashboard`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Login"))),await ml()});J.command("logout").description(`Logout from ${m.name} on this machine`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Logout"))),await gl()});J.command("link").description(`Link local directory to a ${m.name} Project`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Link Project"))),await yl()});J.command("me").description("Display currently logged in user details").action(async()=>{await jl()});J.command("init").description(`Create a new ${m.name} Project`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Init Project"))),await Sl()});J.command("env").description("Manage Environment Variables").argument("<action>","Action to perform: set, list, pull, download, or export").argument("[args...]","Key=Value pairs for set action").option("-e, --environment <name>","Environment to pull/download","development").option("-o, --out <file>","Output file for pull/download",".env.local").option("--overwrite","Overwrite output file if it already exists").action(async(e,t,n)=>{de(ae.bgCyan.black(m.cliTag("Env Manager"))),await El(e,t,n)});J.command("start").description("Start the development server (Metro)").option("-p, --build-profile <name>","Build profile (e.g. development, staging)","development").option("--tunnel","Use ngrok tunnel").option("--lan","Use LAN IP").option("--localhost","Use localhost").option("-c, --clear","Clear Metro cache").option("--port <number>","Port to listen on").option("--ios","Start on iOS").option("--android","Start on Android").option("--web","Start on Web").action(async e=>{de(ae.bgCyan.black(m.cliTag("Dev Server"))),await Gl(e)});J.command("config").description("Show resolved configuration for a profile").option("-p, --profile <name>","Build profile to resolve","development").option("--json","Output as JSON").action(async e=>{await Vl(e)});var Xc=J.command("version").description("Show or seed DB-owned native version counters"),Zc=e=>e.requiredOption("--platform <os>","Target platform: ios or android").option("-p, --profile <name>","Build profile used to resolve package/bundle scope","production").option("--scope <value>","Counter scope override, usually package name or bundle identifier").option("--package <name>","Android package override").option("--bundle-id <id>","iOS bundle identifier override").option("--json","Print JSON output");Zc(Xc.command("show").description("Show the current native version counter")).action(async e=>{de(ae.bgCyan.black(m.cliTag("Version Counter"))),await Lo("show",e)});Zc(Xc.command("seed").description("Seed the native version counter for an existing app").argument("[value]","Latest already-used value; the next build uses value + 1").option("--value <number>","Latest already-used value; the next build uses value + 1").option("--force","Allow lowering an existing counter")).action(async(e,t)=>{de(ae.bgCyan.black(m.cliTag("Version Counter"))),await Lo("seed",{...t,value:t.value??e})});var Qc=J.command("deeplink").description("Configure and verify app links, universal links, and URI schemes");Qc.command("setup").description("Generate deep link config, hosted association files, and Expo plugin support").option("--domain <domain>","Associated domain, e.g. app.example.com").option("--base-url <url>","Base URL, e.g. https://app.example.com").option("-p, --profile <name>","Profile used for build/submit overrides").option("--platform <platform>","ios, android, or all").option("--framework <framework>","expo or react-native").option("--scheme <scheme>","URI scheme, e.g. myapp").option("--ios-bundle-id <id>","iOS bundle identifier").option("--android-package <name>","Android package name").option("--apple-team-id <id>","Apple Developer Team ID").option("--app-store-id <id>","App Store app ID for Smart App Banner").option("--android-fingerprint <sha256>","Android SHA256 signing certificate fingerprint; repeatable",Bo,[]).option("--output <dir>","Directory for hosted files, e.g. public or deep-links").option("--install-plugin","Install @radhya/mach locally so Expo can use the packaged config plugin").option("--no-install-plugin","Do not install @radhya/mach for Expo projects; use the local plugin fallback if the package is missing").option("-y, --yes","Automatically answer yes to prompts",!1).action(async e=>{let t=J.opts();e.yes=e.yes||t.yes,de(ae.bgCyan.black(m.cliTag("Deep Links"))),await pc(e)});Qc.command("verify").description("Verify local and optionally live deep link configuration").option("-p, --profile <name>","Profile used for build/submit overrides").option("--live","Check hosted files over HTTPS").option("--android-device","Print Android device verification commands").action(async e=>{de(ae.bgCyan.black(m.cliTag("Deep Link Verify"))),await fc(e)});J.command("credentials").description("Unified, interactive credential management (iOS/Android)").option("-p, --profile <name>","Build profile (production, staging, development)").option("--force","Force recreate certificates (nuke existing on Apple Portal)",!1).action(async e=>{let{credentialsMainCommand:t}=await import("./credentials-STB7QAPB.js");await t(e)});J.command("credentials:setup").description("Automated credential provisioning (iOS/Android) [Legacy]").requiredOption("--platform <os>","Platform: ios or android").option("--profile <name>","Build profile (production, staging, development)").option("--bundle-id <id>","Bundle ID / Package Name (optional, auto-detected)").option("--team-id <id>","Apple Team ID (iOS only)").option("--key-alias <alias>","Android keystore alias (optional, auto-generated)").option("--aws-profile <name>","AWS Credentials Profile (for S3 storage)").option("--force","Force recreate certificates (nuke existing on Apple Portal)",!1).action(async e=>{de(ae.bgCyan.black(m.cliTag("Credentials Setup"))),await An(e),qc(ae.green("Done!"))});J.command("credentials:service").description("Upload Service Credentials (ASC API Key / Google Service JSON) [Legacy]").requiredOption("--platform <os>","Platform: ios or android").option("--file <path>","Path to credential file (.p8 or .json)").option("--issuer-id <id>","ASC API Key Issuer ID (iOS only)").option("--key-id <id>","ASC API Key ID (iOS only)").option("--bundle-id <id>","Bundle identifier / Android package name to attach this service credential to").option("--package-name <name>","Android package name to attach this service credential to").option("-p, --profile <name>","Build profile to resolve package/bundle identifier from config").action(async e=>{de(ae.bgCyan.black(m.cliTag("Service Credentials"))),await qo(e),qc(ae.green("Done!"))});var jo=J.command("device").description("Manage registered iOS test devices");jo.command("register").description("Register a connected iOS device or enter UDID manually").option("--udid <udid>","Device UDID (skip auto-detection)").option("--name <name>","Device name").option("--team-id <id>","Apple Developer Team ID for this device").action(async e=>{await Zi("register",e)});jo.command("list").description("List all registered devices").option("--team-id <id>","Filter by Apple Developer Team ID").action(async e=>{await Zi("list",e)});jo.command("remove").description("Remove a registered device").option("--udid <udid>","Device UDID to remove").option("--team-id <id>","Apple Developer Team ID for this device").action(async e=>{await Zi("remove",e)});J.command("audit").description("Run automated security audit on the project or APK").option("--apk <path>","Path to APK file (runs APK diagnostics like 16KB alignment)").option("--json","Output results as JSON (for CI/CD)").option("--fix","Auto-apply fixes where possible").option("--category <name>","Run only a specific check category (e.g. 16kb, permissions, secrets)").option("--severity <level>","Minimum severity to display (critical, high, medium, low, info)").option("--prebuild","Run expo prebuild for deeper manifest/plist analysis").action(async e=>{de(ae.bgCyan.black(m.cliTag(e.apk?"APK Audit":"Security Audit"))),await Jc(e)});var zh=new Set(["login","logout","agent"]);J.hook("preAction",(e,t)=>{let n=t.name(),i=t.opts?.()||{};n==="maestro"&&(i.local||i.localRunner)||t.parent?.name()!=="deeplink"&&(zh.has(n)||Xt())});J.on("command:*",()=>{console.error(`Invalid command: %s
1698
+ `));for(let a of s)Ie.error(Se.red(`${Se.bold(a.name)} ${Se.dim(`(${a.arch})`)}`)),Ie.info(Se.dim(` PT_LOAD alignment: 2**${Math.log2(a.align)} (${a.align} bytes, needs 2**14 = ${Gc})`))}return l.length>0&&(console.log(""),Ie.success(Se.green(`${l.length} librar${l.length===1?"y":"ies"} correctly aligned (2**14 or higher).`))),console.log(""),s.length===0?(Ie.success(Se.green.bold("ELF Verification Successful. APK is 16KB compatible.")),!0):(Ie.error(Se.red.bold(`Found ${s.length} unaligned lib${s.length===1?"":"s"} (arm64-v8a/x86_64 libs need to be aligned).`)),console.log(""),Ie.info(Se.yellow("To fix this:")),Ie.info(Se.dim(" 1. Rebuild with NDK r28+ which defaults to 16KB page alignment")),Ie.info(Se.dim(" 2. Or set -DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON in CMake")),Ie.info(Se.dim(" 3. Update third-party libraries that ship prebuilt .so files")),Ie.info(Se.dim(" 4. After rebuilding: zipalign -p -f -v 16 input.apk output.apk")),console.log(""),Ie.info(Se.dim("Reference: https://developer.android.com/guide/practices/page-sizes")),!1)}catch(r){return n.stop("Analysis failed."),Ie.error(Se.red(`Failed to analyze APK: ${r.message}`)),!1}finally{try{wt.rmSync(i,{recursive:!0,force:!0})}catch{}}}function Nh(e){let t=wt.openSync(e,"r");try{let n=Buffer.alloc(64);if(wt.readSync(t,n,0,64,0),!n.subarray(0,4).equals(Th))return null;let i=n[4]===2,r=n[5]===1,o=(f,p)=>r?f.readUInt16LE(p):f.readUInt16BE(p),s=(f,p)=>r?f.readUInt32LE(p):f.readUInt32BE(p),l=(f,p)=>Number(r?f.readBigUInt64LE(p):f.readBigUInt64BE(p)),a,c,u;i?(a=l(n,32),c=o(n,54),u=o(n,56)):(a=s(n,28),c=o(n,42),u=o(n,44));let d=null;for(let f=0;f<u;f++){let p=Buffer.alloc(c);if(wt.readSync(t,p,0,c,a+f*c),s(p,0)!==kh)continue;let h;i?h=l(p,48):h=s(p,28),h>0&&(d===null||h<d)&&(d=h)}return d??0}catch{return null}finally{wt.closeSync(t)}}function zc(e){let t=[];if(!wt.existsSync(e))return t;let n=wt.readdirSync(e,{withFileTypes:!0});for(let i of n){let r=yn.join(e,i.name);i.isDirectory()?t.push(...zc(r)):i.name.endsWith(".so")&&t.push(r)}return t}function xh(e){return e.includes("arm64-v8a")?"arm64-v8a":e.includes("x86_64")?"x86_64":"unknown"}var Jc=async e=>{if(Ne()||(qe.error(st.red(`You are not logged in. Run \`${m.nameLower} login\` first.`)),process.exit(1)),e.apk){await Uh(e);return}let n=process.cwd(),i=lr.join(n,"package.json");oi.existsSync(i)||(qe.error(st.red("No package.json found. Run this command from a project root.")),process.exit(1));let r=JSON.parse(oi.readFileSync(i,"utf-8")),o=!!(r.dependencies?.expo||r.devDependencies?.expo);e.json||(qe.info(st.dim(`Project: ${r.name||"unknown"} v${r.version||"0.0.0"}`)),qe.info(st.dim(`Type: ${o?"Expo":"Bare React Native"}`)),qe.info(""));let s={json:e.json,fix:e.fix,category:e.category,severity:e.severity,prebuild:e.prebuild},l=await Bc(n,s);e.json?console.log(JSON.stringify(Hc(l,r),null,2)):jc(l,e.severity);let a=l.some(u=>u.findings.some(d=>d.severity==="critical")),c=l.some(u=>u.findings.some(d=>d.severity==="high"));(a||c)&&process.exit(1)},Kc=["16kb"];async function Uh(e){let t=e.category?.toLowerCase();t&&!Kc.includes(t)&&(qe.error(st.red(`Unknown APK check category: "${t}"`)),qe.info(st.dim(`Available APK checks: ${Kc.join(", ")}`)),process.exit(1));let n=await Fh(e.apk);n||process.exit(1);let i=!t,r=!1;(i||t==="16kb")&&(await Vc(n)||(r=!0)),r&&process.exit(1)}async function Fh(e){if(e.startsWith("build:")){let n=e.slice(6);return await Bh(n)}if(e.startsWith("http://")||e.startsWith("https://"))return await Yc(e);let t=lr.resolve(e);return oi.existsSync(t)?t:(qe.error(st.red(`APK file not found: ${t}`)),null)}async function Bh(e){let t=Wc();t.start(`Fetching APK from build ${e}...`);try{let n=await N.get(`/build/${e}`);if(n.data.status!=="success")throw new Error(n.data.message);let i=n.data.build;if(!i)throw new Error("Build not found");let r=i.artifacts?.find(o=>o.type?o.type==="apk":o.name?.endsWith(".apk")||o.url?.includes(".apk"));return!r||!r.url?(t.stop("No APK artifact found"),qe.error(st.red("This build does not have an APK artifact.")),qe.info(st.dim("Make sure the build produced an APK (not just an AAB).")),null):(t.stop(`Found build: ${i.buildId||e}`),await Yc(r.url))}catch(n){return t.stop("Failed to fetch build"),qe.error(st.red(n.message)),null}}async function Yc(e){let t=Wc();t.start("Downloading APK...");try{let n=oi.mkdtempSync(lr.join(Lh.tmpdir(),`${m.nameLower}-audit-`)),i=lr.join(n,"app.apk"),r=await Mh.get(e,{responseType:"arraybuffer"});oi.writeFileSync(i,r.data);let o=(r.data.length/(1024*1024)).toFixed(1);return t.stop(`Downloaded APK (${o} MB)`),i}catch(n){return t.stop("Download failed"),qe.error(st.red(`Failed to download APK: ${n.message}`)),null}}var Gh=console.log;console.log=()=>{};Hh();console.log=Gh;de(ae.bgCyan.black(m.cliTag("Production Build Orchestrator")));var Bo=(e,t=[])=>t.concat(e),J=new jh;J.name(m.nameLower).description(`${m.name} CLI: Powerful Cloud Infrastructure Tools`).version(Tt).option("-y, --yes","Automatically answer yes to all prompts",!1).option("--quiet","Suppress non-essential output",!1);J.command("agent").description("Manage local device discovery agent for the Install Hub (port 7070)").argument("[action]","start, run, install, status, restart, stop, or uninstall","start").action(async e=>{await dl(e)});J.command("host").description("Setup AWS S3 static hosting and CloudFront").action(async()=>{de(ae.bgCyan.black(m.cliTag("Hosting"))),await Zo()});J.command("sitemap").description("Generate sitemap.xml from Expo Router app structure").option("-o, --output <path>","Output path for sitemap.xml","./public/sitemap.xml").action(async e=>{de(ae.bgCyan.black(m.cliTag("Sitemap Generator"))),await ns(e)});J.command("build").description("Run a build on a cloud provider (AWS Spot Instances)").argument("[platform]","Target platform (android, ios)").option("--platform <os>","Target platform alias (android, ios) [Optional]").option("--repo <url>","Git Repository URL").option("--branch <name>","Git Branch").option("--ami <id>","AMI ID").option("--bucket <name>","S3 Bucket for artifacts").option("--region <region>","AWS Region").option("--aws-profile <name>","AWS Credentials Profile").option("-p, --build-profile <name>","Build Profile (e.g. production, staging)").option("--profile <name>","Build Profile (alias for --build-profile)").option("--dry-run","Generate User Data script without launching").option("--keep-instance","Do not terminate the instance after build").option("--subnet <id>","VPC Subnet ID to launch the instance in").option("--security-group <id>","VPC Security Group ID to use").option("--ssh-host <host>","Remote host for iOS build (SSH)").option("--ssh-user <user>","SSH username for remote host").option("--ssh-key <path>","Path to private SSH key for remote host").option("--ios-scheme <scheme>","iOS Scheme to build").option("--ios-team-id <id>","Apple Development Team ID").option("--ios-configuration <config>","Xcode configuration (Release/Debug)").option("--ios-export-method <method>","Export method (ad-hoc, app-store, development, enterprise)").option("--ios-api-key-id <id>","App Store Connect API Key ID").option("--ios-api-key-issuer <id>","App Store Connect API Key Issuer ID").option("--ios-api-key-path <path>","Path to App Store Connect API Key (.p8) file").option("--simulator","Build for iOS Simulator (no signing required)").addOption(new Xe("--build-id <id>","Internal build ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--fail-only","Internal: mark an existing build failed").hideHelp()).addOption(new Xe("--error <message>","Internal failure message").hideHelp()).option("--local","Run build on local machine").option("--auto-version","Assign the next Android versionCode or iOS buildNumber from Mach").option("--auto-submit","Submit this build after it completes successfully").option("--submit-track <track>","Google Play track to use with --auto-submit").option("--submit-release-status <status>","Google Play release status to use with --auto-submit").option("--submit-rollout <fraction>","Google Play staged rollout fraction to use with --auto-submit").option("--submit-changes-not-sent-for-review","Commit Google Play changes without automatically sending them for review when using --auto-submit").option("--submit-credential-id <id>","Service credential ID to use with --auto-submit").option("--submit-json-output <path>","Write auto-submit metadata JSON to a file for CI/CD").option("--verbose","Enable verbose logging").option("--json-output <path>","Write build metadata JSON to a file for CI/CD").option("--maestro","Queue a managed Maestro test run after a successful build").option("--maestro-flows <path>","Maestro flow file or directory (default: .maestro)").option("--maestro-device <device>","iOS simulator model or identifier (default: iPhone 17 Pro; managed Android uses Pixel 6)").option("--maestro-retries <n>","Retries for the Maestro run","1").option("--maestro-record","Enable screen recording where supported").option("--maestro-use-build-source","Use Maestro flows from the build source snapshot instead of uploading local flows").option("--maestro-env <name>","Forward an environment variable to the managed Maestro runner; repeat or comma-separate names",Bo,[]).option("-y, --yes","Automatically answer yes to all prompts",!1).option("--quiet","Suppress non-essential output",!1).action(async(e,t)=>{let n=J.opts();t.yes=t.yes||n.yes,t.quiet=t.quiet||n.quiet,t.platform=e||t.platform,t.buildProfile=t.profile||t.buildProfile,t.quiet||de(ae.bgCyan.black(m.cliTag("Cloud Build"))),await vi(t)});J.command("maestro").description("Run Maestro tests locally or with the managed Mach runner").option("--platform <os>","Target platform (android/ios)").option("-p, --profile <name>","Build profile to resolve when using --latest","production").option("--latest","Use the latest successful build for the platform/profile").option("--build-id <id>","Use a specific Mach build ID").option("--build-url <url>","Use a Mach build URL").option("--artifact-url <url>","Use a direct APK or iOS simulator app artifact URL").option("--local","Run Maestro on a connected local emulator/device instead of the managed runner").option("--maestro-flows <path>","Maestro flow file or directory (default: .maestro)").option("--maestro-device <device>","iOS simulator model or identifier (default: iPhone 17 Pro; managed Android uses Pixel 6)").option("--maestro-retries <n>","Retries for the Maestro run","1").option("--maestro-record","Enable screen recording where supported").option("--maestro-use-build-source","Use Maestro flows from the build source snapshot instead of uploading local flows").option("--maestro-env <name>","Forward an environment variable to the managed Maestro runner; repeat or comma-separate names",Bo,[]).option("--wait","Wait for managed Maestro completion and exit non-zero on failure").option("--wait-timeout <minutes>","Maximum minutes to wait for managed Maestro completion","60").option("--wait-interval <seconds>","Polling interval while waiting for managed Maestro completion","15").option("--json-output <path>","Write test run metadata JSON to a file for CI/CD").addOption(new Xe("--local-runner","Internal: execute a managed Maestro run on this machine").hideHelp()).addOption(new Xe("--test-run-id <id>","Internal test run ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--runner-token <token>","Internal runner token").hideHelp()).action(async e=>{de(ae.bgCyan.black(m.cliTag("Maestro Testing"))),await Ws(e)});var Sn=e=>e.option("--platform <os>","Target platform: ios, android, or all","all").option("--channel <name>","OTA channel (for example: production, staging)").option("--branch <name>","Alias for --channel").option("--runtime-version <version>","Runtime version override").option("--message <text>","Release note/message for this OTA update").option("--framework <name>","Framework override: expo or react-native").option("--engine <name>","OTA engine override (currently expo-updates)").option("--url <url>","Mach OTA update URL override").option("--install-plugin","Install @radhya/mach locally for the Expo config plugin").option("--skip-install","Do not install local Expo plugin dependency").option("--update-id <id>","OTA update id for rollback/promote").option("--json","Print JSON output"),bn=J.command("ota").description("Setup, publish, verify, and manage Mach OTA updates");Sn(bn.command("setup").description("Configure OTA for this project")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Setup"))),await Nt("setup",{...e,yes:e.yes||t.yes})});Sn(bn.command("publish").description("Publish an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Publish"))),await Nt("publish",{...e,yes:e.yes||t.yes})});Sn(bn.command("verify").description("Verify OTA configuration")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Verify"))),await Nt("verify",{...e,yes:e.yes||t.yes})});Sn(bn.command("list").description("List OTA updates for the linked project")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Updates"))),await Nt("list",{...e,yes:e.yes||t.yes})});Sn(bn.command("rollback").description("Roll back an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Rollback"))),await Nt("rollback",{...e,yes:e.yes||t.yes})});Sn(bn.command("promote").description("Promote an OTA update")).action(async e=>{let t=J.opts();de(ae.bgCyan.black(m.cliTag("OTA Promote"))),await Nt("promote",{...e,yes:e.yes||t.yes})});J.command("submit").description("Automated Store Submission (App Store / Google Play)").option("--platform <os>","Target platform (ios/android)").option("-p, --profile <name>","Build/submission profile to submit from (e.g. production, preprod)").option("--track <track>","Google Play Track (internal, alpha, beta, production)").option("--release-status <status>","Google Play release status (completed, draft, halted, inProgress)").option("--rollout <fraction>","Google Play staged rollout fraction for inProgress releases (example: 0.1)").option("--changes-not-sent-for-review","Commit Google Play changes without automatically sending them for review").option("--ssh-host <host>","Remote macOS host for iOS submission").option("--ssh-user <user>","SSH username for remote iOS submission").option("--ssh-key <path>","Path to private SSH key for remote iOS submission").option("--credential-id <id>","Service credential ID to use when multiple submission credentials exist").option("--local","Run submission on this machine (internal runner mode for iOS)").addOption(new Xe("--submit-id <id>","Internal submission ID").hideHelp()).addOption(new Xe("--project-id <id>","Internal project ID").hideHelp()).addOption(new Xe("--package-name <name>","Internal Android package name").hideHelp()).addOption(new Xe("--bundle-id <id>","Internal iOS bundle identifier").hideHelp()).option("--json-output <path>","Write submission metadata JSON to a file for CI/CD").option("--latest","Use the latest successful build for submission").option("--build-id <id>","Use a specific build ID for submission").action(async e=>{let t=J.opts();e.quiet=e.quiet||t.quiet,e.yes=e.yes||t.yes,de(ae.bgCyan.black(m.cliTag("Store Submission"))),await Si(e)});J.command("login").description(`Login to ${m.name} Dashboard`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Login"))),await ml()});J.command("logout").description(`Logout from ${m.name} on this machine`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Logout"))),await gl()});J.command("link").description(`Link local directory to a ${m.name} Project`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Link Project"))),await yl()});J.command("me").description("Display currently logged in user details").action(async()=>{await jl()});J.command("init").description(`Create a new ${m.name} Project`).action(async()=>{de(ae.bgCyan.black(m.cliTag("Init Project"))),await Sl()});J.command("env").description("Manage Environment Variables").argument("<action>","Action to perform: set, list, pull, download, or export").argument("[args...]","Key=Value pairs for set action").option("-e, --environment <name>","Environment to pull/download","development").option("-o, --out <file>","Output file for pull/download",".env.local").option("--overwrite","Overwrite output file if it already exists").action(async(e,t,n)=>{de(ae.bgCyan.black(m.cliTag("Env Manager"))),await El(e,t,n)});J.command("start").description("Start the development server (Metro)").option("-p, --build-profile <name>","Build profile (e.g. development, staging)","development").option("--tunnel","Use ngrok tunnel").option("--lan","Use LAN IP").option("--localhost","Use localhost").option("-c, --clear","Clear Metro cache").option("--port <number>","Port to listen on").option("--ios","Start on iOS").option("--android","Start on Android").option("--web","Start on Web").action(async e=>{de(ae.bgCyan.black(m.cliTag("Dev Server"))),await Gl(e)});J.command("config").description("Show resolved configuration for a profile").option("-p, --profile <name>","Build profile to resolve","development").option("--json","Output as JSON").action(async e=>{await Vl(e)});var Xc=J.command("version").description("Show or seed DB-owned native version counters"),Zc=e=>e.requiredOption("--platform <os>","Target platform: ios or android").option("-p, --profile <name>","Build profile used to resolve package/bundle scope","production").option("--scope <value>","Counter scope override, usually package name or bundle identifier").option("--package <name>","Android package override").option("--bundle-id <id>","iOS bundle identifier override").option("--json","Print JSON output");Zc(Xc.command("show").description("Show the current native version counter")).action(async e=>{de(ae.bgCyan.black(m.cliTag("Version Counter"))),await Lo("show",e)});Zc(Xc.command("seed").description("Seed the native version counter for an existing app").argument("[value]","Latest already-used value; the next build uses value + 1").option("--value <number>","Latest already-used value; the next build uses value + 1").option("--force","Allow lowering an existing counter")).action(async(e,t)=>{de(ae.bgCyan.black(m.cliTag("Version Counter"))),await Lo("seed",{...t,value:t.value??e})});var Qc=J.command("deeplink").description("Configure and verify app links, universal links, and URI schemes");Qc.command("setup").description("Generate deep link config, hosted association files, and Expo plugin support").option("--domain <domain>","Associated domain, e.g. app.example.com").option("--base-url <url>","Base URL, e.g. https://app.example.com").option("-p, --profile <name>","Profile used for build/submit overrides").option("--platform <platform>","ios, android, or all").option("--framework <framework>","expo or react-native").option("--scheme <scheme>","URI scheme, e.g. myapp").option("--ios-bundle-id <id>","iOS bundle identifier").option("--android-package <name>","Android package name").option("--apple-team-id <id>","Apple Developer Team ID").option("--app-store-id <id>","App Store app ID for Smart App Banner").option("--android-fingerprint <sha256>","Android SHA256 signing certificate fingerprint; repeatable",Bo,[]).option("--output <dir>","Directory for hosted files, e.g. public or deep-links").option("--install-plugin","Install @radhya/mach locally so Expo can use the packaged config plugin").option("--no-install-plugin","Do not install @radhya/mach for Expo projects; use the local plugin fallback if the package is missing").option("-y, --yes","Automatically answer yes to prompts",!1).action(async e=>{let t=J.opts();e.yes=e.yes||t.yes,de(ae.bgCyan.black(m.cliTag("Deep Links"))),await pc(e)});Qc.command("verify").description("Verify local and optionally live deep link configuration").option("-p, --profile <name>","Profile used for build/submit overrides").option("--live","Check hosted files over HTTPS").option("--android-device","Print Android device verification commands").action(async e=>{de(ae.bgCyan.black(m.cliTag("Deep Link Verify"))),await fc(e)});J.command("credentials").description("Unified, interactive credential management (iOS/Android)").option("-p, --profile <name>","Build profile (production, staging, development)").option("--force","Force recreate certificates (nuke existing on Apple Portal)",!1).action(async e=>{let{credentialsMainCommand:t}=await import("./credentials-SOUQOL2C.js");await t(e)});J.command("credentials:setup").description("Automated credential provisioning (iOS/Android) [Legacy]").requiredOption("--platform <os>","Platform: ios or android").option("--profile <name>","Build profile (production, staging, development)").option("--bundle-id <id>","Bundle ID / Package Name (optional, auto-detected)").option("--team-id <id>","Apple Team ID (iOS only)").option("--key-alias <alias>","Android keystore alias (optional, auto-generated)").option("--aws-profile <name>","AWS Credentials Profile (for S3 storage)").option("--force","Force recreate certificates (nuke existing on Apple Portal)",!1).action(async e=>{de(ae.bgCyan.black(m.cliTag("Credentials Setup"))),await An(e),qc(ae.green("Done!"))});J.command("credentials:service").description("Upload Service Credentials (ASC API Key / Google Service JSON) [Legacy]").requiredOption("--platform <os>","Platform: ios or android").option("--file <path>","Path to credential file (.p8 or .json)").option("--issuer-id <id>","ASC API Key Issuer ID (iOS only)").option("--key-id <id>","ASC API Key ID (iOS only)").option("--bundle-id <id>","Bundle identifier / Android package name to attach this service credential to").option("--package-name <name>","Android package name to attach this service credential to").option("-p, --profile <name>","Build profile to resolve package/bundle identifier from config").action(async e=>{de(ae.bgCyan.black(m.cliTag("Service Credentials"))),await qo(e),qc(ae.green("Done!"))});var jo=J.command("device").description("Manage registered iOS test devices");jo.command("register").description("Register a connected iOS device or enter UDID manually").option("--udid <udid>","Device UDID (skip auto-detection)").option("--name <name>","Device name").option("--team-id <id>","Apple Developer Team ID for this device").action(async e=>{await Zi("register",e)});jo.command("list").description("List all registered devices").option("--team-id <id>","Filter by Apple Developer Team ID").action(async e=>{await Zi("list",e)});jo.command("remove").description("Remove a registered device").option("--udid <udid>","Device UDID to remove").option("--team-id <id>","Apple Developer Team ID for this device").action(async e=>{await Zi("remove",e)});J.command("audit").description("Run automated security audit on the project or APK").option("--apk <path>","Path to APK file (runs APK diagnostics like 16KB alignment)").option("--json","Output results as JSON (for CI/CD)").option("--fix","Auto-apply fixes where possible").option("--category <name>","Run only a specific check category (e.g. 16kb, permissions, secrets)").option("--severity <level>","Minimum severity to display (critical, high, medium, low, info)").option("--prebuild","Run expo prebuild for deeper manifest/plist analysis").action(async e=>{de(ae.bgCyan.black(m.cliTag(e.apk?"APK Audit":"Security Audit"))),await Jc(e)});var Vh=new Set(["login","logout","agent"]);J.hook("preAction",(e,t)=>{let n=t.name(),i=t.opts?.()||{};n==="maestro"&&(i.local||i.localRunner)||t.parent?.name()!=="deeplink"&&(Vh.has(n)||Xt())});J.on("command:*",()=>{console.error(`Invalid command: %s
1699
1699
  See --help for a list of available commands.`,J.args.join(" ")),process.exit(1)});process.argv.slice(2).length||J.help();J.parse(process.argv);