@iwas/mcp 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,25 @@
1
- "use strict";var Ce=Object.create;var N=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Ie=Object.getPrototypeOf,xe=Object.prototype.hasOwnProperty;var Me=(i,e)=>()=>{try{return e||i((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}},Le=(i,e)=>{for(var t in e)N(i,t,{get:e[t],enumerable:!0})},se=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of _e(e))!xe.call(i,n)&&n!==t&&N(i,n,{get:()=>e[n],enumerable:!(r=be(e,n))||r.enumerable});return i};var I=(i,e,t)=>(t=i!=null?Ce(Ie(i)):{},se(e||!i||!i.__esModule?N(t,"default",{value:i,enumerable:!0}):t,i)),Ae=i=>se(N({},"__esModule",{value:!0}),i);var he=Me(g=>{"use strict";Object.defineProperty(g,"__esModule",{value:!0});g.MCP_OAUTH_SCOPES=g.OFFLINE_ACCESS_SCOPE=g.MCP_ELIGIBLE_PERMISSION_SET=g.MCP_ELIGIBLE_PERMISSIONS=g.SENSITIVE_MCP_RESOURCES=g.PERMISSION_SET=g.ALL_PERMISSIONS=g.CATALOG_DEFINITIONS=void 0;g.isPermissionString=ce;g.parsePermissionString=le;g.isSensitiveMcpResource=de;g.isMcpEligiblePermission=ue;g.partitionMcpScopes=Fe;g.CATALOG_DEFINITIONS={organization:{tier:"T",actions:["update","delete"],description:"Organization tenant profile and settings",models:["Organization","OrganizationSetting","OrganizationSubscription"],mcpEligible:!0},member:{tier:"T",actions:["create","update","delete"],description:"Organization member management",models:["Member"],mcpEligible:!0},invitation:{tier:"T",actions:["create","cancel"],description:"Organization membership invitations",models:["Invitation"],mcpEligible:!0},location:{tier:"T",actions:["read","write","delete"],description:"Physical hotspot location management",models:["Location"],mcpEligible:!0},router:{tier:"T",actions:["read","write","delete","control"],description:"Gateway router inventory, provisioning and control",models:["Router","ZtpToken","CoaQueue","IpamAllocationCursor"],mcpEligible:!0},package:{tier:"S",actions:["read","write","delete","publish"],description:"WiFi access package catalog (tenant + shared)",models:["Package","VoucherPackage"],mcpEligible:!0},voucher:{tier:"S",actions:["read","write","publish","issue","revoke"],description:"Prepaid voucher codes and issuance (tenant + shared)",models:["Voucher","VoucherIssuance","VoucherUser"],mcpEligible:!0},session:{tier:"T",actions:["read","control"],description:"Active and historical WiFi guest sessions",models:["WifiSession","RadiusAccounting","DeviceToken"],mcpEligible:!0},wallet:{tier:"T",actions:["read","topup","charge","refund","freeze"],description:"Prepaid wallet balances, topups and ledger transactions (SENSITIVE - MCP EXCLUDED)",models:["Wallet","WalletTransaction","TopupRequest"],hasOwnQualifier:!0,mcpEligible:!1},payment:{tier:"T",actions:["read","approve","refund"],description:"Inbound payment transactions and gateway integrations (SENSITIVE - MCP EXCLUDED)",models:["PaymentTransaction","PaymentGatewayIntegration"],hasOwnQualifier:!0,mcpEligible:!1},subscription:{tier:"T",actions:["read","manage"],description:"SaaS organization plan subscription and entitlements",models:["OrganizationSubscription"],mcpEligible:!0},partner:{tier:"T",actions:["read","manage","payout:approve"],description:"Affiliate/partner programs and payout requests",models:["Partner","PartnerPayoutRequest"],mcpEligible:!0},campaign:{tier:"T",actions:["read","write"],description:"Captive portal ad campaigns and lead capture",models:["AdCampaign","LeadCapture"],mcpEligible:!0},notification:{tier:"T",actions:["read","send","template:write"],description:"Multi-channel notifications, templates and logs",models:["NotificationItem","NotificationTemplate","DeliveryLog"],hasOwnQualifier:!0,mcpEligible:!0},store:{tier:"P",actions:["read","write"],description:"Public hardware/affiliate store catalog",models:["StoreProduct","AffiliateClick"],mcpEligible:!0},blog:{tier:"P",actions:["read","write","publish"],description:"Company-wide editorial articles and CDN media",models:["BlogPost","Media"],hasOwnQualifier:!0,mcpEligible:!0},analytics:{tier:"D",actions:["read"],description:"Aggregated usage, session and revenue metrics",models:["WifiSession","Package","PaymentTransaction"],mcpEligible:!0},diagnostics:{tier:"P",actions:["read","write"],description:"System error events, MCP tool execution spans, flow events, issue triage and audit logs",models:["ErrorEvent","McpToolSpan","AuditLog","SystemSetting","FlowEvent","IssueState"],mcpEligible:!0}};g.ALL_PERMISSIONS=["organization:update","organization:delete","member:create","member:update","member:delete","invitation:create","invitation:cancel","location:read","location:write","location:delete","router:read","router:write","router:delete","router:control","package:read","package:write","package:delete","package:publish","voucher:read","voucher:write","voucher:publish","voucher:issue","voucher:revoke","session:read","session:control","wallet:read","wallet:topup","wallet:charge","wallet:refund","wallet:freeze","payment:read","payment:approve","payment:refund","subscription:read","subscription:manage","partner:read","partner:manage","partner:payout:approve","campaign:read","campaign:write","notification:read","notification:send","notification:template:write","store:read","store:write","blog:read","blog:write","blog:publish","analytics:read","diagnostics:read","diagnostics:write"];g.PERMISSION_SET=new Set(g.ALL_PERMISSIONS);function ce(i){return typeof i=="string"&&g.PERMISSION_SET.has(i)}function le(i){if(!ce(i))return null;let e=i.indexOf(":"),t=i.slice(0,e),r=i.slice(e+1);return{resource:t,action:r}}g.SENSITIVE_MCP_RESOURCES=["payment","wallet"];function de(i){return i==="payment"||i==="wallet"}function ue(i){let e=le(i);return e?!de(e.resource):!1}g.MCP_ELIGIBLE_PERMISSIONS=g.ALL_PERMISSIONS.filter(ue);g.MCP_ELIGIBLE_PERMISSION_SET=new Set(g.MCP_ELIGIBLE_PERMISSIONS);g.OFFLINE_ACCESS_SCOPE="offline_access";g.MCP_OAUTH_SCOPES=[...g.MCP_ELIGIBLE_PERMISSIONS,g.OFFLINE_ACCESS_SCOPE];function Fe(i){return{permissionScopes:i.filter(e=>e!==g.OFFLINE_ACCESS_SCOPE),offlineAccessRequested:i.includes(g.OFFLINE_ACCESS_SCOPE)}}});var Be={};Le(Be,{CredentialVault:()=>P,DEFAULT_CLI_CLIENT_ID:()=>$,DEFAULT_KEYCHAIN_SERVICE:()=>j,DEFAULT_READ_ONLY_SCOPES:()=>ge,DEFAULT_SCOPES:()=>me,DEFAULT_SERVER_URL:()=>pe,DEVICE_CODE_GRANT_TYPE:()=>ye,DefaultKeychainRunner:()=>z,HttpError:()=>T,KeychainService:()=>A,ReauthenticationRequiredError:()=>_,TokenRefreshManager:()=>ee,VaultLock:()=>L,VaultLockError:()=>J,VaultLockTimeoutError:()=>B,decodeJwtPayload:()=>q,defaultIsProcessAlive:()=>Ee,executeLogin:()=>De,executeLogout:()=>fe,executeStatus:()=>Ne,isCredentialExpired:()=>M,isLocalhost:()=>Z,isValidStoredCredentials:()=>U,normalizeProfileKey:()=>x,openBrowser:()=>ve,pollDeviceToken:()=>Te,redactCredentials:()=>Re,requestDeviceCode:()=>ke,resolveAuthServerUrl:()=>O,resolveMcpResourceUrl:()=>Se,sanitizeSecretString:()=>E,sendRequest:()=>C,validateScopes:()=>we,withVaultLock:()=>Ve});module.exports=Ae(Be);function U(i){if(!i||typeof i!="object"||Array.isArray(i))return!1;let e=i;return!(typeof e.serverUrl!="string"||typeof e.clientId!="string"||typeof e.accessToken!="string"||typeof e.tokenType!="string"||typeof e.expiresAt!="number"||!Number.isFinite(e.expiresAt)||!("organizationId"in e)||e.organizationId!==null&&typeof e.organizationId!="string"||e.refreshToken!==void 0&&typeof e.refreshToken!="string"||e.scope!==void 0&&typeof e.scope!="string"||e.resource!==void 0&&typeof e.resource!="string"||e.updatedAt!==void 0&&(typeof e.updatedAt!="number"||!Number.isFinite(e.updatedAt)))}function x(i,e){let t=(e||"").trim(),r=(i||"").trim();/^https?:\/\//i.test(r)||(r=`https://${r}`);try{return`${new URL(r).origin.toLowerCase()}::${t}`}catch{return`${r.replace(/\/+$/,"").toLowerCase()}::${t}`}}function Re(i){let e={...i};return"accessToken"in e&&typeof e.accessToken=="string"&&(e.accessToken="[REDACTED]"),"refreshToken"in e&&typeof e.refreshToken=="string"&&(e.refreshToken="[REDACTED]"),e}function E(i,e){let t=i;for(let r of e)r&&typeof r=="string"&&r.length>=4&&(t=t.split(r).join("[REDACTED]"));return t}function M(i,e=60){if(!i.expiresAt)return!1;let t=i.expiresAt>1e11?i.expiresAt:i.expiresAt*1e3;return Date.now()+e*1e3>=t}var oe=require("child_process");var z=class{defaultTimeoutMs;constructor(e=5e3){this.defaultTimeoutMs=e}spawn(e,t,r){return new Promise(n=>{let s;try{s=(0,oe.spawn)(e,t,{shell:!1,stdio:["pipe","pipe","pipe"]})}catch(a){let u=a instanceof Error?a.message:String(a);return n({stdout:"",stderr:u,exitCode:-1})}let o="",c="",l=!1,d=r?.timeoutMs??this.defaultTimeoutMs,m=setTimeout(()=>{if(!l){l=!0;try{s.kill("SIGKILL")}catch{}n({stdout:o,stderr:`Keychain operation timed out after ${d}ms`,exitCode:-1})}},d);if(s.stdout?.on("data",a=>{o+=a.toString()}),s.stderr?.on("data",a=>{c+=a.toString()}),s.on("error",a=>{c+=a.message}),s.on("close",a=>{l||(l=!0,clearTimeout(m),n({stdout:o,stderr:c,exitCode:a??-1}))}),r?.input!==void 0&&s.stdin)try{s.stdin.write(r.input),s.stdin.end()}catch{}})}},j="iwas-cli",A=class{service;runner;platform;constructor(e){this.service=e?.serviceName||j,this.runner=e?.runner||new z,this.platform=e?.platform||process.platform}availableCache=null;getPlatform(){return this.platform}async isAvailable(e=!1){if(!e&&this.availableCache===!0)return!0;if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["help"]),r=t.exitCode===0||t.stdout.includes("security")||t.stderr.includes("security");return r&&(this.availableCache=!0),r}if(this.platform==="linux"){let r=(await this.runner.spawn("which",["secret-tool"])).exitCode===0;return r&&(this.availableCache=!0),r}return!1}clearAvailabilityCache(){this.availableCache=null}async getSecret(e){if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["find-generic-password","-s",this.service,"-a",e,"-w"]);if(t.exitCode===0){let r=t.stdout.trim();if(r.length>0&&r.length%2===0&&/^[0-9a-fA-F]+$/.test(r)&&(r.startsWith("7b")||r.startsWith("7B")||r.startsWith("5b")||r.startsWith("5B")))try{let n=Buffer.from(r,"hex").toString("utf8");if(!n.includes("\uFFFD"))return n}catch{}return r}if(t.exitCode===44||t.stderr.includes("could not be found")||t.stderr.includes("The specified item could not be found"))return null;throw new Error(`Keychain read failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}if(this.platform==="linux"){let t=await this.runner.spawn("secret-tool",["lookup","service",this.service,"account",e]);if(t.exitCode===0)return t.stdout.length>0?t.stdout.trim():null;let r=t.stderr.trim();if(t.exitCode===1&&(r===""||r.toLowerCase().includes("not found")||r.toLowerCase().includes("no such secret")))return null;throw new Error(`Secret-tool read failed: ${r||`exit code ${t.exitCode}`}`)}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}async setSecret(e,t,r){let n=r||`${this.service} (${e})`,s=[t];try{let o=JSON.parse(t);if(typeof o=="object"&&o!==null)for(let c of Object.values(o))typeof c=="string"&&c.length>=4&&s.push(c)}catch{}if(this.platform==="darwin"){let o=await this.runner.spawn("/usr/bin/security",["add-generic-password","-U","-s",this.service,"-a",e,"-l",n,"-w",t]);if(o.exitCode!==0){let c=E(o.stderr.trim(),s);throw new Error(`Keychain write failed: ${c||`exit code ${o.exitCode}`}`)}return}if(this.platform==="linux"){let o=await this.runner.spawn("secret-tool",["store",`--label=${n}`,"service",this.service,"account",e],{input:t});if(o.exitCode!==0){let c=E(o.stderr.trim(),s);throw new Error(`Secret-tool write failed: ${c||`exit code ${o.exitCode}`}`)}return}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}async deleteSecret(e){if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["delete-generic-password","-s",this.service,"-a",e]);if(t.exitCode===0||t.exitCode===44||t.stderr.includes("could not be found"))return;throw new Error(`Keychain delete failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}if(this.platform==="linux"){let t=await this.runner.spawn("secret-tool",["clear","service",this.service,"account",e]);if(t.exitCode===0||t.exitCode===1)return;throw new Error(`Secret-tool clear failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}};var w=I(require("fs")),R=I(require("path")),ae=I(require("os"));var V="__profiles__",P=class{vaultType;configDir;credentialsFilePath;keychain;constructor(e){let t=process.env.IWAS_MCP_VAULT;if(t&&!["auto","keychain","file"].includes(t))throw new Error(`Invalid IWAS_MCP_VAULT value: "${t}". Supported values are "auto", "keychain", or "file".`);let r=t;this.vaultType=e?.vaultType||r||"auto";let n=e?.configDir||process.env.XDG_CONFIG_HOME||R.default.join(ae.default.homedir(),".config");this.configDir=e?.configDir?e.configDir:R.default.join(n,"iwas"),this.credentialsFilePath=R.default.join(this.configDir,"credentials.json"),this.keychain=e?.keychainService||new A({serviceName:e?.serviceName||j,runner:e?.keychainRunner})}getConfigDir(){return this.configDir}getCredentialsFilePath(){return this.credentialsFilePath}async getEffectiveBackend(){if(this.vaultType==="file")return"file";let e=await this.keychain.isAvailable().catch(()=>!1),t=this.keychain.getPlatform();if(this.vaultType==="keychain"){if(!e)throw new Error(`Keychain vault requested (IWAS_MCP_VAULT="keychain") but OS keychain is unavailable on platform: ${t}`);return"keychain"}return e?"keychain":"file"}async load(e){if(await this.getEffectiveBackend()==="keychain"){let r=null;try{let c=await this.keychain.getSecret(e);if(c)try{let l=JSON.parse(c);if(U(l))r=l;else if(this.vaultType==="keychain")throw new Error(`Corrupt credentials stored in keychain for profile: ${e}`)}catch(l){if(this.vaultType==="keychain"){let d=l instanceof Error?l.message:String(l);throw new Error(`Corrupt credentials JSON in keychain for profile ${e}: ${d}`)}}}catch(c){if(this.vaultType==="keychain")throw c}if(this.vaultType==="keychain")return r;let n=this.loadFromFile(e);if(!r){if(n)try{await this.keychain.setSecret(e,JSON.stringify(n),`IWAS MCP Profile (${e})`),await this.addProfileToKeychainIndex(e),this.deleteFromFile(e)}catch{}return n}if(!n)return r;let s=r.updatedAt??r.expiresAt??0;if((n.updatedAt??n.expiresAt??0)>s){try{await this.keychain.setSecret(e,JSON.stringify(n),`IWAS MCP Profile (${e})`),await this.addProfileToKeychainIndex(e),this.deleteFromFile(e)}catch{}return n}else return this.deleteFromFile(e),r}return this.loadFromFile(e)}async save(e,t){let r=await this.getEffectiveBackend(),n={...t,updatedAt:t.updatedAt??Date.now()};if(r==="keychain"){let s=!1;try{let o=JSON.stringify(n);await this.keychain.setSecret(e,o,`IWAS MCP Profile (${e})`),s=!0;try{await this.addProfileToKeychainIndex(e)}catch(c){let l=c instanceof Error?c.message:String(c),d=E(l,[n.accessToken,n.refreshToken]);throw new Error(`Keychain index update failed: ${d}`)}this.deleteFromFile(e);return}catch(o){if(this.vaultType==="keychain"||s){let c=o instanceof Error?o.message:String(o),l=E(c,[n.accessToken,n.refreshToken]);throw o instanceof Error&&o.message.startsWith("Keychain")?o:new Error(`Keychain save failed: ${l}`)}}}this.saveToFile(e,n)}async delete(e){let t=null,r=!1;if(this.vaultType==="file"){this.deleteFromFile(e);return}try{if(r=await this.keychain.isAvailable().catch(()=>!1),this.vaultType==="keychain"&&!r)throw new Error(`Keychain vault requested (IWAS_MCP_VAULT="keychain") but OS keychain is unavailable on platform: ${this.keychain.getPlatform()}`);r&&(await this.keychain.deleteSecret(e),await this.removeProfileFromKeychainIndex(e))}catch(n){t=n}if(this.deleteFromFile(e),t&&(this.vaultType==="keychain"||this.vaultType==="auto"&&r))throw t}async listProfiles(){if(this.vaultType==="file")return this.listProfilesFromFile();if(this.vaultType==="keychain")return this.readKeychainIndex();let e=new Set;try{if(await this.keychain.isAvailable()){let r=await this.readKeychainIndex();for(let n of r)e.add(n)}}catch{}for(let t of this.listProfilesFromFile())e.add(t);return Array.from(e)}async readKeychainIndex(){let e=await this.keychain.getSecret(V);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(r=>typeof r=="string"):[]}catch(t){let r=t instanceof Error?t.message:String(t);throw new Error(`Corrupt keychain profile index at ${V}: ${r}`)}}async addProfileToKeychainIndex(e){let t=await this.readKeychainIndex();t.includes(e)||(t.push(e),await this.keychain.setSecret(V,JSON.stringify(t),"IWAS MCP Profiles Index"))}async removeProfileFromKeychainIndex(e){let t=await this.readKeychainIndex();if(t.includes(e)){let r=t.filter(n=>n!==e);await this.keychain.setSecret(V,JSON.stringify(r),"IWAS MCP Profiles Index")}}ensureConfigDirectory(){w.default.existsSync(this.configDir)||w.default.mkdirSync(this.configDir,{recursive:!0,mode:448});try{w.default.chmodSync(this.configDir,448)}catch{}}readAllFromFile(){if(!w.default.existsSync(this.credentialsFilePath))return{};if(process.platform!=="win32")try{w.default.chmodSync(this.credentialsFilePath,384)}catch{}let e=w.default.readFileSync(this.credentialsFilePath,"utf8").trim();if(!e)return{};try{let t=JSON.parse(e);if(typeof t!="object"||t===null||Array.isArray(t))throw new Error("Root credentials object must be a JSON record");for(let[r,n]of Object.entries(t))if(!U(n))throw new Error(`Profile "${r}" contains invalid or missing credential fields`);return t}catch(t){let r=t instanceof Error?t.message:String(t);throw new Error(`Corrupt credentials file at ${this.credentialsFilePath}: ${r}. Will not overwrite.`)}}writeAllToFile(e){this.ensureConfigDirectory();let t=JSON.stringify(e,null,2)+`
2
- `,r=R.default.join(this.configDir,`.credentials.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`),n=null;try{n=w.default.openSync(r,"w",384),w.default.writeSync(n,t,void 0,"utf8"),w.default.fsyncSync(n),w.default.closeSync(n),n=null;try{w.default.chmodSync(r,384)}catch{}w.default.renameSync(r,this.credentialsFilePath);try{w.default.chmodSync(this.credentialsFilePath,384)}catch{}if(process.platform!=="win32")try{let s=w.default.openSync(this.configDir,"r");w.default.fsyncSync(s),w.default.closeSync(s)}catch{}}catch(s){if(n!==null)try{w.default.closeSync(n)}catch{}if(w.default.existsSync(r))try{w.default.unlinkSync(r)}catch{}throw s}}loadFromFile(e){return this.readAllFromFile()[e]??null}saveToFile(e,t){let r=this.readAllFromFile();r[e]=t,this.writeAllToFile(r)}deleteFromFile(e){if(!w.default.existsSync(this.credentialsFilePath))return;let t=this.readAllFromFile();e in t&&(delete t[e],this.writeAllToFile(t))}listProfilesFromFile(){if(!w.default.existsSync(this.credentialsFilePath))return[];let e=this.readAllFromFile();return Object.keys(e)}};var T=class extends Error{status;data;constructor(e,t,r){super(e),this.name="HttpError",this.status=t,this.data=r}},$e=15e3,Oe=2;async function C(i,e){let t=e?.method||"GET",r=e?.timeoutMs??$e,n=e?.retries??Oe,s={Accept:"application/json",...e?.headers||{}},o;e?.body&&(typeof e.body=="string"?o=e.body:e.body instanceof URLSearchParams?(o=e.body.toString(),s["Content-Type"]||(s["Content-Type"]="application/x-www-form-urlencoded")):(o=JSON.stringify(e.body),s["Content-Type"]||(s["Content-Type"]="application/json")));let c=0,l=null;for(;c<=n;){c++;let d=new AbortController,m=setTimeout(()=>d.abort(),r);try{let u=await(e?.fetchFn||fetch)(i,{method:t,headers:s,body:o,signal:d.signal});clearTimeout(m);let p=u.headers.get("content-type")||"",f;if(p.includes("application/json"))try{f=await u.json()}catch{f=null}else f=await u.text();if(u.status>=500&&u.status<=504&&c<=n){let y=Math.min(1e3*Math.pow(2,c-1)+Math.random()*200,5e3);await new Promise(S=>setTimeout(S,y));continue}return{status:u.status,ok:u.ok,headers:u.headers,data:f}}catch(a){clearTimeout(m);let p=a instanceof Error&&a.name==="AbortError"?`Request timed out after ${r}ms`:a instanceof Error?a.message:String(a);if(l=new Error(`Network request failed for ${t} ${i}: ${p}`),c<=n){let f=Math.min(500*Math.pow(2,c-1)+Math.random()*200,3e3);await new Promise(y=>setTimeout(y,f));continue}}}throw l||new Error(`Network request failed after ${c} attempts`)}var K=require("child_process"),b=I(he());async function fe(i){let e=i?.logger||(f=>process.stdout.write(`${f}
1
+ "use strict";var be=Object.create;var N=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var Ie=Object.getOwnPropertyNames;var xe=Object.getPrototypeOf,Me=Object.prototype.hasOwnProperty;var Le=(i,e)=>()=>{try{return e||i((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}},Ae=(i,e)=>{for(var t in e)N(i,t,{get:e[t],enumerable:!0})},se=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Ie(e))!Me.call(i,n)&&n!==t&&N(i,n,{get:()=>e[n],enumerable:!(r=_e(e,n))||r.enumerable});return i};var I=(i,e,t)=>(t=i!=null?be(xe(i)):{},se(e||!i||!i.__esModule?N(t,"default",{value:i,enumerable:!0}):t,i)),$e=i=>se(N({},"__esModule",{value:!0}),i);var he=Le(g=>{"use strict";Object.defineProperty(g,"__esModule",{value:!0});g.MCP_OAUTH_SCOPES=g.OFFLINE_ACCESS_SCOPE=g.MCP_ELIGIBLE_PERMISSION_SET=g.MCP_ELIGIBLE_PERMISSIONS=g.SENSITIVE_MCP_RESOURCES=g.PERMISSION_SET=g.ALL_PERMISSIONS=g.CATALOG_DEFINITIONS=void 0;g.isPermissionString=ce;g.parsePermissionString=le;g.isSensitiveMcpResource=ue;g.isMcpEligiblePermission=de;g.partitionMcpScopes=De;g.CATALOG_DEFINITIONS={organization:{tier:"T",actions:["update","delete"],description:"Organization tenant profile and settings",models:["Organization","OrganizationSetting","OrganizationSubscription"],mcpEligible:!0},member:{tier:"T",actions:["create","update","delete"],description:"Organization member management",models:["Member"],mcpEligible:!0},invitation:{tier:"T",actions:["create","cancel"],description:"Organization membership invitations",models:["Invitation"],mcpEligible:!0},location:{tier:"T",actions:["read","write","delete"],description:"Physical hotspot location management",models:["Location"],mcpEligible:!0},router:{tier:"T",actions:["read","write","delete","control"],description:"Gateway router inventory, provisioning and control",models:["Router","ZtpToken","CoaQueue","IpamAllocationCursor"],mcpEligible:!0},package:{tier:"S",actions:["read","write","delete","publish"],description:"WiFi access package catalog (tenant + shared)",models:["Package","VoucherPackage"],mcpEligible:!0},voucher:{tier:"S",actions:["read","write","publish","issue","revoke"],description:"Prepaid voucher codes and issuance (tenant + shared)",models:["Voucher","VoucherIssuance","VoucherUser"],mcpEligible:!0},session:{tier:"T",actions:["read","control"],description:"Active and historical WiFi guest sessions",models:["WifiSession","RadiusAccounting","DeviceToken"],mcpEligible:!0},wallet:{tier:"T",actions:["read","topup","charge","refund","freeze"],description:"Prepaid wallet balances, topups and ledger transactions (SENSITIVE - MCP EXCLUDED)",models:["Wallet","WalletTransaction","TopupRequest"],hasOwnQualifier:!0,mcpEligible:!1},payment:{tier:"T",actions:["read","approve","refund"],description:"Inbound payment transactions and gateway integrations (SENSITIVE - MCP EXCLUDED)",models:["PaymentTransaction","PaymentGatewayIntegration"],hasOwnQualifier:!0,mcpEligible:!1},subscription:{tier:"T",actions:["read","manage"],description:"SaaS organization plan subscription and entitlements",models:["OrganizationSubscription"],mcpEligible:!0},partner:{tier:"T",actions:["read","manage","payout:approve"],description:"Affiliate/partner programs and payout requests",models:["Partner","PartnerPayoutRequest"],mcpEligible:!0},campaign:{tier:"T",actions:["read","write"],description:"Captive portal ad campaigns and lead capture",models:["AdCampaign","LeadCapture"],mcpEligible:!0},notification:{tier:"T",actions:["read","send","template:write"],description:"Multi-channel notifications, templates and logs",models:["NotificationItem","NotificationTemplate","DeliveryLog"],hasOwnQualifier:!0,mcpEligible:!0},store:{tier:"P",actions:["read","write"],description:"Public hardware/affiliate store catalog",models:["StoreProduct","AffiliateClick"],mcpEligible:!0},blog:{tier:"P",actions:["read","write","publish"],description:"Company-wide editorial articles and CDN media",models:["BlogPost","Media"],hasOwnQualifier:!0,mcpEligible:!0},analytics:{tier:"D",actions:["read"],description:"Aggregated usage, session and revenue metrics",models:["WifiSession","Package","PaymentTransaction"],mcpEligible:!0},diagnostics:{tier:"P",actions:["read","write"],description:"System error events, MCP tool execution spans, flow events, issue triage and audit logs",models:["ErrorEvent","McpToolSpan","AuditLog","SystemSetting","FlowEvent","IssueState"],mcpEligible:!0}};g.ALL_PERMISSIONS=["organization:update","organization:delete","member:create","member:update","member:delete","invitation:create","invitation:cancel","location:read","location:write","location:delete","router:read","router:write","router:delete","router:control","package:read","package:write","package:delete","package:publish","voucher:read","voucher:write","voucher:publish","voucher:issue","voucher:revoke","session:read","session:control","wallet:read","wallet:topup","wallet:charge","wallet:refund","wallet:freeze","payment:read","payment:approve","payment:refund","subscription:read","subscription:manage","partner:read","partner:manage","partner:payout:approve","campaign:read","campaign:write","notification:read","notification:send","notification:template:write","store:read","store:write","blog:read","blog:write","blog:publish","analytics:read","diagnostics:read","diagnostics:write"];g.PERMISSION_SET=new Set(g.ALL_PERMISSIONS);function ce(i){return typeof i=="string"&&g.PERMISSION_SET.has(i)}function le(i){if(!ce(i))return null;let e=i.indexOf(":"),t=i.slice(0,e),r=i.slice(e+1);return{resource:t,action:r}}g.SENSITIVE_MCP_RESOURCES=["payment","wallet"];function ue(i){return i==="payment"||i==="wallet"}function de(i){let e=le(i);return e?!ue(e.resource):!1}g.MCP_ELIGIBLE_PERMISSIONS=g.ALL_PERMISSIONS.filter(de);g.MCP_ELIGIBLE_PERMISSION_SET=new Set(g.MCP_ELIGIBLE_PERMISSIONS);g.OFFLINE_ACCESS_SCOPE="offline_access";g.MCP_OAUTH_SCOPES=[...g.MCP_ELIGIBLE_PERMISSIONS,g.OFFLINE_ACCESS_SCOPE];function De(i){return{permissionScopes:i.filter(e=>e!==g.OFFLINE_ACCESS_SCOPE),offlineAccessRequested:i.includes(g.OFFLINE_ACCESS_SCOPE)}}});var Ge={};Ae(Ge,{CredentialVault:()=>P,DEFAULT_CLI_CLIENT_ID:()=>R,DEFAULT_KEYCHAIN_SERVICE:()=>j,DEFAULT_READ_ONLY_SCOPES:()=>ge,DEFAULT_SCOPES:()=>me,DEFAULT_SERVER_URL:()=>pe,DEVICE_CODE_GRANT_TYPE:()=>ye,DefaultKeychainRunner:()=>z,HttpError:()=>T,KeychainService:()=>A,ReauthenticationRequiredError:()=>_,TokenRefreshManager:()=>ee,VaultLock:()=>L,VaultLockError:()=>J,VaultLockTimeoutError:()=>B,decodeJwtPayload:()=>q,defaultIsProcessAlive:()=>Pe,executeLogin:()=>Ne,executeLogout:()=>fe,executeStatus:()=>Ue,formatRemainingDuration:()=>Ee,isCredentialExpired:()=>M,isLocalhost:()=>Z,isValidStoredCredentials:()=>U,normalizeProfileKey:()=>x,openBrowser:()=>ve,pollDeviceToken:()=>Te,redactCredentials:()=>Re,requestDeviceCode:()=>ke,resolveAuthServerUrl:()=>O,resolveMcpResourceUrl:()=>Se,sanitizeSecretString:()=>E,sendRequest:()=>C,validateScopes:()=>we,withVaultLock:()=>qe});module.exports=$e(Ge);function U(i){if(!i||typeof i!="object"||Array.isArray(i))return!1;let e=i;return!(typeof e.serverUrl!="string"||typeof e.clientId!="string"||typeof e.accessToken!="string"||typeof e.tokenType!="string"||typeof e.expiresAt!="number"||!Number.isFinite(e.expiresAt)||!("organizationId"in e)||e.organizationId!==null&&typeof e.organizationId!="string"||e.refreshToken!==void 0&&typeof e.refreshToken!="string"||e.scope!==void 0&&typeof e.scope!="string"||e.resource!==void 0&&typeof e.resource!="string"||e.updatedAt!==void 0&&(typeof e.updatedAt!="number"||!Number.isFinite(e.updatedAt)))}function x(i,e){let t=(e||"").trim(),r=(i||"").trim();/^https?:\/\//i.test(r)||(r=`https://${r}`);try{return`${new URL(r).origin.toLowerCase()}::${t}`}catch{return`${r.replace(/\/+$/,"").toLowerCase()}::${t}`}}function Re(i){let e={...i};return"accessToken"in e&&typeof e.accessToken=="string"&&(e.accessToken="[REDACTED]"),"refreshToken"in e&&typeof e.refreshToken=="string"&&(e.refreshToken="[REDACTED]"),e}function E(i,e){let t=i;for(let r of e)r&&typeof r=="string"&&r.length>=4&&(t=t.split(r).join("[REDACTED]"));return t}function M(i,e=60){if(!i.expiresAt)return!1;let t=i.expiresAt>1e11?i.expiresAt:i.expiresAt*1e3;return Date.now()+e*1e3>=t}var oe=require("child_process");var z=class{defaultTimeoutMs;constructor(e=5e3){this.defaultTimeoutMs=e}spawn(e,t,r){return new Promise(n=>{let s;try{s=(0,oe.spawn)(e,t,{shell:!1,stdio:["pipe","pipe","pipe"]})}catch(a){let d=a instanceof Error?a.message:String(a);return n({stdout:"",stderr:d,exitCode:-1})}let o="",c="",l=!1,u=r?.timeoutMs??this.defaultTimeoutMs,m=setTimeout(()=>{if(!l){l=!0;try{s.kill("SIGKILL")}catch{}n({stdout:o,stderr:`Keychain operation timed out after ${u}ms`,exitCode:-1})}},u);if(s.stdout?.on("data",a=>{o+=a.toString()}),s.stderr?.on("data",a=>{c+=a.toString()}),s.on("error",a=>{c+=a.message}),s.on("close",a=>{l||(l=!0,clearTimeout(m),n({stdout:o,stderr:c,exitCode:a??-1}))}),r?.input!==void 0&&s.stdin)try{s.stdin.write(r.input),s.stdin.end()}catch{}})}},j="iwas-cli",A=class{service;runner;platform;constructor(e){this.service=e?.serviceName||j,this.runner=e?.runner||new z,this.platform=e?.platform||process.platform}availableCache=null;getPlatform(){return this.platform}async isAvailable(e=!1){if(!e&&this.availableCache===!0)return!0;if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["help"]),r=t.exitCode===0||t.stdout.includes("security")||t.stderr.includes("security");return r&&(this.availableCache=!0),r}if(this.platform==="linux"){let r=(await this.runner.spawn("which",["secret-tool"])).exitCode===0;return r&&(this.availableCache=!0),r}return!1}clearAvailabilityCache(){this.availableCache=null}async getSecret(e){if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["find-generic-password","-s",this.service,"-a",e,"-w"]);if(t.exitCode===0){let r=t.stdout.trim();if(r.length>0&&r.length%2===0&&/^[0-9a-fA-F]+$/.test(r)&&(r.startsWith("7b")||r.startsWith("7B")||r.startsWith("5b")||r.startsWith("5B")))try{let n=Buffer.from(r,"hex").toString("utf8");if(!n.includes("\uFFFD"))return n}catch{}return r}if(t.exitCode===44||t.stderr.includes("could not be found")||t.stderr.includes("The specified item could not be found"))return null;throw new Error(`Keychain read failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}if(this.platform==="linux"){let t=await this.runner.spawn("secret-tool",["lookup","service",this.service,"account",e]);if(t.exitCode===0)return t.stdout.length>0?t.stdout.trim():null;let r=t.stderr.trim();if(t.exitCode===1&&(r===""||r.toLowerCase().includes("not found")||r.toLowerCase().includes("no such secret")))return null;throw new Error(`Secret-tool read failed: ${r||`exit code ${t.exitCode}`}`)}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}async setSecret(e,t,r){let n=r||`${this.service} (${e})`,s=[t];try{let o=JSON.parse(t);if(typeof o=="object"&&o!==null)for(let c of Object.values(o))typeof c=="string"&&c.length>=4&&s.push(c)}catch{}if(this.platform==="darwin"){let o=await this.runner.spawn("/usr/bin/security",["add-generic-password","-U","-s",this.service,"-a",e,"-l",n,"-w",t]);if(o.exitCode!==0){let c=E(o.stderr.trim(),s);throw new Error(`Keychain write failed: ${c||`exit code ${o.exitCode}`}`)}return}if(this.platform==="linux"){let o=await this.runner.spawn("secret-tool",["store",`--label=${n}`,"service",this.service,"account",e],{input:t});if(o.exitCode!==0){let c=E(o.stderr.trim(),s);throw new Error(`Secret-tool write failed: ${c||`exit code ${o.exitCode}`}`)}return}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}async deleteSecret(e){if(this.platform==="darwin"){let t=await this.runner.spawn("/usr/bin/security",["delete-generic-password","-s",this.service,"-a",e]);if(t.exitCode===0||t.exitCode===44||t.stderr.includes("could not be found"))return;throw new Error(`Keychain delete failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}if(this.platform==="linux"){let t=await this.runner.spawn("secret-tool",["clear","service",this.service,"account",e]);if(t.exitCode===0||t.exitCode===1)return;throw new Error(`Secret-tool clear failed: ${t.stderr.trim()||`exit code ${t.exitCode}`}`)}throw new Error(`OS keychain not supported on platform: ${this.platform}`)}};var w=I(require("fs")),$=I(require("path")),ae=I(require("os"));var V="__profiles__",P=class{vaultType;configDir;credentialsFilePath;keychain;constructor(e){let t=process.env.IWAS_MCP_VAULT;if(t&&!["auto","keychain","file"].includes(t))throw new Error(`Invalid IWAS_MCP_VAULT value: "${t}". Supported values are "auto", "keychain", or "file".`);let r=t;this.vaultType=e?.vaultType||r||"auto";let n=e?.configDir||process.env.XDG_CONFIG_HOME||$.default.join(ae.default.homedir(),".config");this.configDir=e?.configDir?e.configDir:$.default.join(n,"iwas"),this.credentialsFilePath=$.default.join(this.configDir,"credentials.json"),this.keychain=e?.keychainService||new A({serviceName:e?.serviceName||j,runner:e?.keychainRunner})}getConfigDir(){return this.configDir}getCredentialsFilePath(){return this.credentialsFilePath}async getEffectiveBackend(){if(this.vaultType==="file")return"file";let e=await this.keychain.isAvailable().catch(()=>!1),t=this.keychain.getPlatform();if(this.vaultType==="keychain"){if(!e)throw new Error(`Keychain vault requested (IWAS_MCP_VAULT="keychain") but OS keychain is unavailable on platform: ${t}`);return"keychain"}return e?"keychain":"file"}async load(e){if(await this.getEffectiveBackend()==="keychain"){let r=null;try{let c=await this.keychain.getSecret(e);if(c)try{let l=JSON.parse(c);if(U(l))r=l;else if(this.vaultType==="keychain")throw new Error(`Corrupt credentials stored in keychain for profile: ${e}`)}catch(l){if(this.vaultType==="keychain"){let u=l instanceof Error?l.message:String(l);throw new Error(`Corrupt credentials JSON in keychain for profile ${e}: ${u}`)}}}catch(c){if(this.vaultType==="keychain")throw c}if(this.vaultType==="keychain")return r;let n=this.loadFromFile(e);if(!r){if(n)try{await this.keychain.setSecret(e,JSON.stringify(n),`IWAS MCP Profile (${e})`),await this.addProfileToKeychainIndex(e),this.deleteFromFile(e)}catch{}return n}if(!n)return r;let s=r.updatedAt??r.expiresAt??0;if((n.updatedAt??n.expiresAt??0)>s){try{await this.keychain.setSecret(e,JSON.stringify(n),`IWAS MCP Profile (${e})`),await this.addProfileToKeychainIndex(e),this.deleteFromFile(e)}catch{}return n}else return this.deleteFromFile(e),r}return this.loadFromFile(e)}async save(e,t){let r=await this.getEffectiveBackend(),n={...t,updatedAt:t.updatedAt??Date.now()};if(r==="keychain"){let s=!1;try{let o=JSON.stringify(n);await this.keychain.setSecret(e,o,`IWAS MCP Profile (${e})`),s=!0;try{await this.addProfileToKeychainIndex(e)}catch(c){let l=c instanceof Error?c.message:String(c),u=E(l,[n.accessToken,n.refreshToken]);throw new Error(`Keychain index update failed: ${u}`)}this.deleteFromFile(e);return}catch(o){if(this.vaultType==="keychain"||s){let c=o instanceof Error?o.message:String(o),l=E(c,[n.accessToken,n.refreshToken]);throw o instanceof Error&&o.message.startsWith("Keychain")?o:new Error(`Keychain save failed: ${l}`)}}}this.saveToFile(e,n)}async delete(e){let t=null,r=!1;if(this.vaultType==="file"){this.deleteFromFile(e);return}try{if(r=await this.keychain.isAvailable().catch(()=>!1),this.vaultType==="keychain"&&!r)throw new Error(`Keychain vault requested (IWAS_MCP_VAULT="keychain") but OS keychain is unavailable on platform: ${this.keychain.getPlatform()}`);r&&(await this.keychain.deleteSecret(e),await this.removeProfileFromKeychainIndex(e))}catch(n){t=n}if(this.deleteFromFile(e),t&&(this.vaultType==="keychain"||this.vaultType==="auto"&&r))throw t}async listProfiles(){if(this.vaultType==="file")return this.listProfilesFromFile();if(this.vaultType==="keychain")return this.readKeychainIndex();let e=new Set;try{if(await this.keychain.isAvailable()){let r=await this.readKeychainIndex();for(let n of r)e.add(n)}}catch{}for(let t of this.listProfilesFromFile())e.add(t);return Array.from(e)}async readKeychainIndex(){let e=await this.keychain.getSecret(V);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(r=>typeof r=="string"):[]}catch(t){let r=t instanceof Error?t.message:String(t);throw new Error(`Corrupt keychain profile index at ${V}: ${r}`)}}async addProfileToKeychainIndex(e){let t=await this.readKeychainIndex();t.includes(e)||(t.push(e),await this.keychain.setSecret(V,JSON.stringify(t),"IWAS MCP Profiles Index"))}async removeProfileFromKeychainIndex(e){let t=await this.readKeychainIndex();if(t.includes(e)){let r=t.filter(n=>n!==e);await this.keychain.setSecret(V,JSON.stringify(r),"IWAS MCP Profiles Index")}}ensureConfigDirectory(){w.default.existsSync(this.configDir)||w.default.mkdirSync(this.configDir,{recursive:!0,mode:448});try{w.default.chmodSync(this.configDir,448)}catch{}}readAllFromFile(){if(!w.default.existsSync(this.credentialsFilePath))return{};if(process.platform!=="win32")try{w.default.chmodSync(this.credentialsFilePath,384)}catch{}let e=w.default.readFileSync(this.credentialsFilePath,"utf8").trim();if(!e)return{};try{let t=JSON.parse(e);if(typeof t!="object"||t===null||Array.isArray(t))throw new Error("Root credentials object must be a JSON record");for(let[r,n]of Object.entries(t))if(!U(n))throw new Error(`Profile "${r}" contains invalid or missing credential fields`);return t}catch(t){let r=t instanceof Error?t.message:String(t);throw new Error(`Corrupt credentials file at ${this.credentialsFilePath}: ${r}. Will not overwrite.`)}}writeAllToFile(e){this.ensureConfigDirectory();let t=JSON.stringify(e,null,2)+`
2
+ `,r=$.default.join(this.configDir,`.credentials.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`),n=null;try{n=w.default.openSync(r,"w",384),w.default.writeSync(n,t,void 0,"utf8"),w.default.fsyncSync(n),w.default.closeSync(n),n=null;try{w.default.chmodSync(r,384)}catch{}w.default.renameSync(r,this.credentialsFilePath);try{w.default.chmodSync(this.credentialsFilePath,384)}catch{}if(process.platform!=="win32")try{let s=w.default.openSync(this.configDir,"r");w.default.fsyncSync(s),w.default.closeSync(s)}catch{}}catch(s){if(n!==null)try{w.default.closeSync(n)}catch{}if(w.default.existsSync(r))try{w.default.unlinkSync(r)}catch{}throw s}}loadFromFile(e){return this.readAllFromFile()[e]??null}saveToFile(e,t){let r=this.readAllFromFile();r[e]=t,this.writeAllToFile(r)}deleteFromFile(e){if(!w.default.existsSync(this.credentialsFilePath))return;let t=this.readAllFromFile();e in t&&(delete t[e],this.writeAllToFile(t))}listProfilesFromFile(){if(!w.default.existsSync(this.credentialsFilePath))return[];let e=this.readAllFromFile();return Object.keys(e)}};var T=class extends Error{status;data;constructor(e,t,r){super(e),this.name="HttpError",this.status=t,this.data=r}},Oe=15e3,Fe=2;async function C(i,e){let t=e?.method||"GET",r=e?.timeoutMs??Oe,n=e?.retries??Fe,s={Accept:"application/json",...e?.headers||{}},o;e?.body&&(typeof e.body=="string"?o=e.body:e.body instanceof URLSearchParams?(o=e.body.toString(),s["Content-Type"]||(s["Content-Type"]="application/x-www-form-urlencoded")):(o=JSON.stringify(e.body),s["Content-Type"]||(s["Content-Type"]="application/json")));let c=0,l=null;for(;c<=n;){c++;let u=new AbortController,m=setTimeout(()=>u.abort(),r);try{let d=await(e?.fetchFn||fetch)(i,{method:t,headers:s,body:o,signal:u.signal});clearTimeout(m);let p=d.headers.get("content-type")||"",f;if(p.includes("application/json"))try{f=await d.json()}catch{f=null}else f=await d.text();if(d.status>=500&&d.status<=504&&c<=n){let y=Math.min(1e3*Math.pow(2,c-1)+Math.random()*200,5e3);await new Promise(S=>setTimeout(S,y));continue}return{status:d.status,ok:d.ok,headers:d.headers,data:f}}catch(a){clearTimeout(m);let p=a instanceof Error&&a.name==="AbortError"?`Request timed out after ${r}ms`:a instanceof Error?a.message:String(a);if(l=new Error(`Network request failed for ${t} ${i}: ${p}`),c<=n){let f=Math.min(500*Math.pow(2,c-1)+Math.random()*200,3e3);await new Promise(y=>setTimeout(y,f));continue}}}throw l||new Error(`Network request failed after ${c} attempts`)}var K=require("child_process"),b=I(he());async function fe(i){let e=i?.logger||(f=>process.stdout.write(`${f}
3
3
  `)),t=i?.errorLogger||(f=>process.stderr.write(`${f}
4
- `)),r=O(i?.server),n=(i?.clientId||$).trim(),s=x(r,n),o=i?.vault||new P,c=i?.force===!0,l=await o.load(s);if(!l){let f={success:!0,profileKey:s,serverRevoked:!1,refreshTokenRevoked:!1,localCleared:!1,message:`No stored credentials found for profile: ${s}`};return i?.json?e(JSON.stringify({event:"logout",success:!0,loggedOut:!0,profileKey:s,serverRevoked:!1,refreshTokenRevoked:!1,localCleared:!1,message:f.message})):e(`\u2139\uFE0F No stored credentials found for profile: ${s}`),f}let d=!1,m=null,a=`${r.replace(/\/+$/,"")}/api/v1/mcp-session/logout`;try{let f=l.tokenType||"Bearer",y=await C(a,{method:"POST",headers:{Authorization:`${f} ${l.accessToken}`},fetchFn:i?.fetchFn});if(y.status===204||y.status===200)d=!0;else if(y.status===401||y.status===403)d=!0;else if(y.status===400){let S=y.data;throw new Error(S?.message||"Server rejected logout request (HTTP 400).")}else throw new T(`Server returned status ${y.status} during logout`,y.status,y.data)}catch(f){m=f instanceof Error?f:new Error(String(f))}if(m&&!c){let f=`Server revocation failed: ${m.message}. Local credentials preserved. Use --force to clear local credentials anyway if the server is permanently offline.`;throw new Error(f)}let u=!1;if(l.refreshToken)try{let f=`${r.replace(/\/+$/,"")}/api/auth/oauth2/revoke`,y=new URLSearchParams({token:l.refreshToken,token_type_hint:"refresh_token",client_id:n}),S=await C(f,{method:"POST",body:y,fetchFn:i?.fetchFn});(S.ok||S.status===200||S.status===204||S.status===401||S.status===400)&&(u=!0)}catch{}await o.delete(s);let p={success:!0,profileKey:s,serverRevoked:d,refreshTokenRevoked:u,localCleared:!0,message:m?`Local credentials cleared with --force (server unreachable: ${m.message})`:"Successfully logged out and revoked all grants server-side"};return i?.json?e(JSON.stringify({event:"logout",success:!0,loggedOut:!0,profileKey:s,serverRevoked:d,refreshTokenRevoked:u,localCleared:!0,forced:c&&!!m})):m?(t(`\u26A0\uFE0F Warning: Server revocation failed (${m.message})`),e(`\u2705 Local credentials cleared for profile: ${s} (--force)
4
+ `)),r=O(i?.server),n=(i?.clientId||R).trim(),s=x(r,n),o=i?.vault||new P,c=i?.force===!0,l=await o.load(s);if(!l){let f={success:!0,profileKey:s,serverRevoked:!1,refreshTokenRevoked:!1,localCleared:!1,message:`No stored credentials found for profile: ${s}`};return i?.json?e(JSON.stringify({event:"logout",success:!0,loggedOut:!0,profileKey:s,serverRevoked:!1,refreshTokenRevoked:!1,localCleared:!1,message:f.message})):e(`\u2139\uFE0F No stored credentials found for profile: ${s}`),f}let u=!1,m=null,a=`${r.replace(/\/+$/,"")}/api/v1/mcp-session/logout`;try{let f=l.tokenType||"Bearer",y=await C(a,{method:"POST",headers:{Authorization:`${f} ${l.accessToken}`},fetchFn:i?.fetchFn});if(y.status===204||y.status===200)u=!0;else if(y.status===401||y.status===403)u=!0;else if(y.status===400){let S=y.data;throw new Error(S?.message||"Server rejected logout request (HTTP 400).")}else throw new T(`Server returned status ${y.status} during logout`,y.status,y.data)}catch(f){m=f instanceof Error?f:new Error(String(f))}if(m&&!c){let f=`Server revocation failed: ${m.message}. Local credentials preserved. Use --force to clear local credentials anyway if the server is permanently offline.`;throw new Error(f)}let d=!1;if(l.refreshToken)try{let f=`${r.replace(/\/+$/,"")}/api/auth/oauth2/revoke`,y=new URLSearchParams({token:l.refreshToken,token_type_hint:"refresh_token",client_id:n}),S=await C(f,{method:"POST",body:y,fetchFn:i?.fetchFn});(S.ok||S.status===200||S.status===204||S.status===401||S.status===400)&&(d=!0)}catch{}await o.delete(s);let p={success:!0,profileKey:s,serverRevoked:u,refreshTokenRevoked:d,localCleared:!0,message:m?`Local credentials cleared with --force (server unreachable: ${m.message})`:"Successfully logged out and revoked all grants server-side"};return i?.json?e(JSON.stringify({event:"logout",success:!0,loggedOut:!0,profileKey:s,serverRevoked:u,refreshTokenRevoked:d,localCleared:!0,forced:c&&!!m})):m?(t(`\u26A0\uFE0F Warning: Server revocation failed (${m.message})`),e(`\u2705 Local credentials cleared for profile: ${s} (--force)
5
5
  `)):(e(`
6
6
  \u2705 Successfully logged out from profile: ${s}`),e(" \u2022 Server session & OAuth grants: Revoked"),e(` \u2022 Local credentials: Removed
7
- `)),p}var $="iwas-cli",pe="https://getiwas.com",ge=b.MCP_ELIGIBLE_PERMISSIONS.filter(i=>i.endsWith(":read")),me=[...ge,b.OFFLINE_ACCESS_SCOPE].join(" "),ye="urn:ietf:params:oauth:grant-type:device_code";function we(i){let e=i.split(/\s+/).filter(Boolean);if(e.length===0)return{valid:!1,invalidScopes:[],error:"At least one OAuth scope must be requested."};let t=new Set(b.MCP_OAUTH_SCOPES),r=e.filter(s=>!t.has(s));return r.length>0?{valid:!1,invalidScopes:r,error:`Invalid OAuth scope(s) requested: [${r.join(", ")}]. Supported scopes are: [${b.MCP_OAUTH_SCOPES.join(", ")}]`}:e.filter(s=>s!==b.OFFLINE_ACCESS_SCOPE).length===0?{valid:!1,invalidScopes:[],error:"At least one MCP capability scope besides offline_access must be requested."}:{valid:!0,invalidScopes:[]}}function Z(i){return i==="localhost"||i==="127.0.0.1"||i==="::1"||i==="[::1]"}function O(i){let e=(i||process.env.IWAS_SERVER_URL||pe).trim();if(e=e.replace(/\/+$/,""),e.toLowerCase().endsWith("/mcp"))throw new Error(`Invalid --server value: "${e}". The server argument must be the auth/web origin (e.g. "https://getiwas.com"), not the MCP resource endpoint. Use --resource for "${e}".`);/^https?:\/\//i.test(e)||(e=`https://${e}`);let t;try{t=new URL(e)}catch{throw new Error(`Invalid server URL: "${e}"`)}if(t.pathname&&t.pathname!=="/"){let r=t.origin,n=i?`--server value: "${i}"`:`IWAS_SERVER_URL: "${e}"`;throw new Error(`Invalid ${n}. The server argument must be an origin without a path component (e.g. "${r}").`)}if(!Z(t.hostname)&&t.protocol!=="https:")throw new Error(`Insecure server protocol: "${e}". Non-localhost servers require HTTPS per OAuth 2.1 specifications.`);return t.origin.toLowerCase()}function Se(i,e){if(e&&e.trim()){let t=e.trim();/^https?:\/\//i.test(t)||(t=t.startsWith("localhost")||t.startsWith("127.0.0.1")||t.startsWith("[::1]")||t.startsWith("::1")?`http://${t}`:`https://${t}`);let r;try{r=new URL(t)}catch{throw new Error(`Invalid resource URL: "${t}"`)}if(!Z(r.hostname)&&r.protocol!=="https:")throw new Error(`Insecure resource protocol: "${t}". Non-localhost resource endpoints require HTTPS.`);return r.href.replace(/\/+$/,"")}return`${i}/mcp`}function q(i){let e=i.split(".");if(e.length!==3)return{};try{let t=Buffer.from(e[1],"base64url").toString("utf8"),r=JSON.parse(t);return typeof r=="object"&&r!==null?r:{}}catch{return{}}}function ve(i,e=K.spawn,t){try{let r;try{r=new URL(i)}catch{return t?.(`Invalid verification URL: "${i}". Please open manually.`),!1}if(r.protocol!=="http:"&&r.protocol!=="https:")return t?.(`Insecure verification URL protocol: "${r.protocol}". Expected http: or https:.`),!1;let n=process.platform,s;return n==="darwin"?s=e("open",[r.href],{detached:!0,stdio:"ignore"}):n==="win32"?s=e("explorer.exe",[r.href],{detached:!0,stdio:"ignore"}):s=e("xdg-open",[r.href],{detached:!0,stdio:"ignore"}),s.on("error",o=>{let c=o instanceof Error?o.message:String(o);t?.(`Could not launch browser automatically (${c}). Please open the verification URL manually.`)}),s.unref(),!0}catch(r){let n=r instanceof Error?r.message:String(r);return t?.(`Could not launch browser (${n}). Please open the verification URL manually.`),!1}}async function ke(i,e){let t=`${i}/api/auth/device/code`,r=await C(t,{method:"POST",body:{client_id:e.clientId,scope:e.scope,resource:e.resource},retries:0});if(!r.ok||!r.data||typeof r.data!="object")throw new T(`Failed to initiate device authorization with ${t}: HTTP ${r.status}`,r.status,r.data);let n=r.data;if(!n.device_code||!n.user_code||!n.verification_uri)throw new Error(`Malformed device code response from ${t}: missing required RFC 8628 fields`);return{device_code:n.device_code,user_code:n.user_code,verification_uri:n.verification_uri,verification_uri_complete:n.verification_uri_complete||`${n.verification_uri}?user_code=${encodeURIComponent(n.user_code)}`,expires_in:n.expires_in||600,interval:n.interval||5}}async function Te(i,e,t){let r=`${i}/api/auth/oauth2/token`,n=Math.max(e.initialInterval||5,1),s=e.timeoutMs||3e5,o=Date.now();for(;Date.now()-o<s&&(await new Promise(u=>setTimeout(u,n*1e3)),!(Date.now()-o>=s));){t?.("polling");let c=new URLSearchParams({grant_type:ye,client_id:e.clientId,device_code:e.deviceCode,resource:e.resource}),l=await C(r,{method:"POST",body:c,retries:0});if(l.status===200){let u=l.data;if(!u.access_token)throw new Error("Token endpoint returned HTTP 200 without access_token");return u}if(l.status===429){let u=l.headers?.get?.("retry-after"),p=n+5;if(u){let S=parseInt(u,10);if(!isNaN(S)&&S>0)p=S;else{let G=Date.parse(u);if(!isNaN(G)){let H=Math.ceil((G-Date.now())/1e3);H>0&&(p=H)}}}let f=Math.max(0,s-(Date.now()-o)),y=Math.ceil(f/1e3);n=y>0?Math.min(p,60,y):0,t?.(`slow_down (rate_limited, retry-after: ${n}s)`);continue}let d=l.data||{},m=d.error||"unknown_error",a=d.error_description||m;switch(m){case"authorization_pending":continue;case"slow_down":n+=5,t?.(`slow_down (interval: ${n}s)`);continue;case"expired_token":throw new Error("Device code has expired. Please run `iwas login` again to obtain a new code.");case"access_denied":throw new Error("Authorization was denied by the user.");case"invalid_grant":throw new Error(`Invalid grant: ${a}. The device code may have already been used or revoked.`);case"invalid_client":throw new Error(`Invalid OAuth client: ${a}. Check your --client-id.`);default:if(l.status>=500){n=Math.min(n+2,30);continue}throw new Error(`Device authorization failed: ${a} (HTTP ${l.status})`)}}throw new Error("Device login timed out waiting for user authorization.")}async function De(i){let e=i?.logger||(v=>process.stdout.write(`${v}
7
+ `)),p}var R="iwas-cli",pe="https://getiwas.com",ge=b.MCP_ELIGIBLE_PERMISSIONS.filter(i=>i.endsWith(":read")),me=[...ge,b.OFFLINE_ACCESS_SCOPE].join(" "),ye="urn:ietf:params:oauth:grant-type:device_code";function we(i){let e=i.split(/\s+/).filter(Boolean);if(e.length===0)return{valid:!1,invalidScopes:[],error:"At least one OAuth scope must be requested."};let t=new Set(b.MCP_OAUTH_SCOPES),r=e.filter(s=>!t.has(s));return r.length>0?{valid:!1,invalidScopes:r,error:`Invalid OAuth scope(s) requested: [${r.join(", ")}]. Supported scopes are: [${b.MCP_OAUTH_SCOPES.join(", ")}]`}:e.filter(s=>s!==b.OFFLINE_ACCESS_SCOPE).length===0?{valid:!1,invalidScopes:[],error:"At least one MCP capability scope besides offline_access must be requested."}:{valid:!0,invalidScopes:[]}}function Z(i){return i==="localhost"||i==="127.0.0.1"||i==="::1"||i==="[::1]"}function O(i){let e=(i||process.env.IWAS_SERVER_URL||pe).trim();if(e=e.replace(/\/+$/,""),e.toLowerCase().endsWith("/mcp"))throw new Error(`Invalid --server value: "${e}". The server argument must be the auth/web origin (e.g. "https://getiwas.com"), not the MCP resource endpoint. Use --resource for "${e}".`);/^https?:\/\//i.test(e)||(e=`https://${e}`);let t;try{t=new URL(e)}catch{throw new Error(`Invalid server URL: "${e}"`)}if(t.pathname&&t.pathname!=="/"){let r=t.origin,n=i?`--server value: "${i}"`:`IWAS_SERVER_URL: "${e}"`;throw new Error(`Invalid ${n}. The server argument must be an origin without a path component (e.g. "${r}").`)}if(!Z(t.hostname)&&t.protocol!=="https:")throw new Error(`Insecure server protocol: "${e}". Non-localhost servers require HTTPS per OAuth 2.1 specifications.`);return t.origin.toLowerCase()}function Se(i,e){if(e&&e.trim()){let t=e.trim();/^https?:\/\//i.test(t)||(t=t.startsWith("localhost")||t.startsWith("127.0.0.1")||t.startsWith("[::1]")||t.startsWith("::1")?`http://${t}`:`https://${t}`);let r;try{r=new URL(t)}catch{throw new Error(`Invalid resource URL: "${t}"`)}if(!Z(r.hostname)&&r.protocol!=="https:")throw new Error(`Insecure resource protocol: "${t}". Non-localhost resource endpoints require HTTPS.`);return r.href.replace(/\/+$/,"")}return`${i}/mcp`}function q(i){let e=i.split(".");if(e.length!==3)return{};try{let t=Buffer.from(e[1],"base64url").toString("utf8"),r=JSON.parse(t);return typeof r=="object"&&r!==null?r:{}}catch{return{}}}function ve(i,e=K.spawn,t){try{let r;try{r=new URL(i)}catch{return t?.(`Invalid verification URL: "${i}". Please open manually.`),!1}if(r.protocol!=="http:"&&r.protocol!=="https:")return t?.(`Insecure verification URL protocol: "${r.protocol}". Expected http: or https:.`),!1;let n=process.platform,s;return n==="darwin"?s=e("open",[r.href],{detached:!0,stdio:"ignore"}):n==="win32"?s=e("explorer.exe",[r.href],{detached:!0,stdio:"ignore"}):s=e("xdg-open",[r.href],{detached:!0,stdio:"ignore"}),s.on("error",o=>{let c=o instanceof Error?o.message:String(o);t?.(`Could not launch browser automatically (${c}). Please open the verification URL manually.`)}),s.unref(),!0}catch(r){let n=r instanceof Error?r.message:String(r);return t?.(`Could not launch browser (${n}). Please open the verification URL manually.`),!1}}async function ke(i,e){let t=`${i}/api/auth/device/code`,r=await C(t,{method:"POST",body:{client_id:e.clientId,scope:e.scope,resource:e.resource},retries:0});if(!r.ok||!r.data||typeof r.data!="object")throw new T(`Failed to initiate device authorization with ${t}: HTTP ${r.status}`,r.status,r.data);let n=r.data;if(!n.device_code||!n.user_code||!n.verification_uri)throw new Error(`Malformed device code response from ${t}: missing required RFC 8628 fields`);return{device_code:n.device_code,user_code:n.user_code,verification_uri:n.verification_uri,verification_uri_complete:n.verification_uri_complete||`${n.verification_uri}?user_code=${encodeURIComponent(n.user_code)}`,expires_in:n.expires_in||600,interval:n.interval||5}}async function Te(i,e,t){let r=`${i}/api/auth/oauth2/token`,n=Math.max(e.initialInterval||5,1),s=e.timeoutMs||3e5,o=Date.now();for(;Date.now()-o<s&&(await new Promise(d=>setTimeout(d,n*1e3)),!(Date.now()-o>=s));){t?.("polling");let c=new URLSearchParams({grant_type:ye,client_id:e.clientId,device_code:e.deviceCode,resource:e.resource}),l=await C(r,{method:"POST",body:c,retries:0});if(l.status===200){let d=l.data;if(!d.access_token)throw new Error("Token endpoint returned HTTP 200 without access_token");return d}if(l.status===429){let d=l.headers?.get?.("retry-after"),p=n+5;if(d){let S=parseInt(d,10);if(!isNaN(S)&&S>0)p=S;else{let G=Date.parse(d);if(!isNaN(G)){let H=Math.ceil((G-Date.now())/1e3);H>0&&(p=H)}}}let f=Math.max(0,s-(Date.now()-o)),y=Math.ceil(f/1e3);n=y>0?Math.min(p,60,y):0,t?.(`slow_down (rate_limited, retry-after: ${n}s)`);continue}let u=l.data||{},m=u.error||"unknown_error",a=u.error_description||m;switch(m){case"authorization_pending":continue;case"slow_down":n+=5,t?.(`slow_down (interval: ${n}s)`);continue;case"expired_token":throw new Error("Device code has expired. Please run `iwas login` again to obtain a new code.");case"access_denied":throw new Error("Authorization was denied by the user.");case"invalid_grant":throw new Error(`Invalid grant: ${a}. The device code may have already been used or revoked.`);case"invalid_client":throw new Error(`Invalid OAuth client: ${a}. Check your --client-id.`);default:if(l.status>=500){n=Math.min(n+2,30);continue}throw new Error(`Device authorization failed: ${a} (HTTP ${l.status})`)}}throw new Error("Device login timed out waiting for user authorization.")}async function Ne(i){let e=i?.logger||(v=>process.stdout.write(`${v}
8
8
  `)),t=i?.errorLogger||(v=>process.stderr.write(`${v}
9
- `)),r=O(i?.server),n=Se(r,i?.resource),s=(i?.clientId||$).trim(),o=(i?.scopes||me).trim(),c=i?.vault||new P,l=we(o);if(!l.valid)throw new Error(l.error||"Invalid OAuth scopes requested.");let d=await ke(r,{clientId:s,scope:o,resource:n});if(i?.json){let v={event:"device_authorization",user_code:d.user_code,verification_uri:d.verification_uri,verification_uri_complete:d.verification_uri_complete,expires_in:d.expires_in,interval:d.interval};e(JSON.stringify(v)),t(`
10
- \u{1F449} USER CODE: ${d.user_code}`),t(`\u{1F517} To verify, open: ${d.verification_uri_complete}
9
+ `)),r=O(i?.server),n=Se(r,i?.resource),s=(i?.clientId||R).trim(),o=(i?.scopes||me).trim(),c=i?.vault||new P,l=we(o);if(!l.valid)throw new Error(l.error||"Invalid OAuth scopes requested.");let u=await ke(r,{clientId:s,scope:o,resource:n});if(i?.json){let v={event:"device_authorization",user_code:u.user_code,verification_uri:u.verification_uri,verification_uri_complete:u.verification_uri_complete,expires_in:u.expires_in,interval:u.interval};e(JSON.stringify(v)),t(`
10
+ \u{1F449} USER CODE: ${u.user_code}`),t(`\u{1F517} To verify, open: ${u.verification_uri_complete}
11
11
  `)}else e(`
12
12
  ============================================================`),e(" IWAS MCP Device Authorization "),e(`============================================================
13
13
  `),e("First, enter this code on the verification page:"),e(`
14
- \u{1F449} USER CODE: \x1B[1m\x1B[32m${d.user_code}\x1B[0m
15
- `),e("To verify, open:"),e(` \u{1F517} ${d.verification_uri_complete}
16
- `),e("Waiting for approval in browser...");let m=i?.spawnFn||K.spawn,a=i?.browserOpener||(v=>ve(v,m,ie=>{i?.json?t(`\u26A0\uFE0F ${ie}`):e(`\u26A0\uFE0F ${ie}`)}));i?.noBrowser||a(d.verification_uri_complete);let u=i?.pollTimeoutMs??d.expires_in*1e3,p=await Te(r,{clientId:s,deviceCode:d.device_code,resource:n,initialInterval:d.interval,timeoutMs:u},v=>{!i?.json&&v.startsWith("slow_down")&&e("Rate limit reached: slowing poll interval.")}),f=q(p.access_token);if(!f||Object.keys(f).length===0)throw new Error("Server returned an opaque or non-JWT access token. Expected a JWT access token targeted for MCP resource.");let y=Array.isArray(f.aud)?f.aud:typeof f.aud=="string"?[f.aud]:[],S=n.replace(/\/+$/,"").toLowerCase();if(!y.some(v=>{try{return new URL(String(v)).href.replace(/\/+$/,"").toLowerCase()===S}catch{return String(v).toLowerCase()===S}}))throw new Error(`Access token audience [${y.join(", ")}] does not match MCP resource URL "${n}".`);let te=(typeof p.scope=="string"&&p.scope||typeof f.scope=="string"&&f.scope||"").split(/\s+/).filter(Boolean),re=o.split(/\s+/).filter(Boolean);if(re.filter(v=>te.includes(v)).filter(v=>v!==b.OFFLINE_ACCESS_SCOPE).length===0)throw new Error(`Access token lacks granted capability scopes (requested: [${re.join(", ")}], granted: [${te.join(", ")}]). At least one permission scope besides offline_access must be granted.`);let Y=typeof f.organizationId=="string"?f.organizationId:null,X=p.expires_in?Date.now()+p.expires_in*1e3:typeof f.exp=="number"?f.exp*1e3:Date.now()+36e5,D={serverUrl:r,clientId:s,accessToken:p.access_token,refreshToken:p.refresh_token,tokenType:p.token_type||"Bearer",scope:p.scope||o,resource:n,organizationId:Y,expiresAt:X,updatedAt:Date.now()},Q=x(r,s);await c.save(Q,D);let ne={profileKey:Q,serverUrl:r,clientId:s,organizationId:Y,scope:D.scope,resource:D.resource,expiresAt:X};if(i?.json){let v={event:"login_success",success:!0,...ne};e(JSON.stringify(v))}else e(`
14
+ \u{1F449} USER CODE: \x1B[1m\x1B[32m${u.user_code}\x1B[0m
15
+ `),e("To verify, open:"),e(` \u{1F517} ${u.verification_uri_complete}
16
+ `),e("Waiting for approval in browser...");let m=i?.spawnFn||K.spawn,a=i?.browserOpener||(v=>ve(v,m,ie=>{i?.json?t(`\u26A0\uFE0F ${ie}`):e(`\u26A0\uFE0F ${ie}`)}));i?.noBrowser||a(u.verification_uri_complete);let d=i?.pollTimeoutMs??u.expires_in*1e3,p=await Te(r,{clientId:s,deviceCode:u.device_code,resource:n,initialInterval:u.interval,timeoutMs:d},v=>{!i?.json&&v.startsWith("slow_down")&&e("Rate limit reached: slowing poll interval.")}),f=q(p.access_token);if(!f||Object.keys(f).length===0)throw new Error("Server returned an opaque or non-JWT access token. Expected a JWT access token targeted for MCP resource.");let y=Array.isArray(f.aud)?f.aud:typeof f.aud=="string"?[f.aud]:[],S=n.replace(/\/+$/,"").toLowerCase();if(!y.some(v=>{try{return new URL(String(v)).href.replace(/\/+$/,"").toLowerCase()===S}catch{return String(v).toLowerCase()===S}}))throw new Error(`Access token audience [${y.join(", ")}] does not match MCP resource URL "${n}".`);let te=(typeof p.scope=="string"&&p.scope||typeof f.scope=="string"&&f.scope||"").split(/\s+/).filter(Boolean),re=o.split(/\s+/).filter(Boolean);if(re.filter(v=>te.includes(v)).filter(v=>v!==b.OFFLINE_ACCESS_SCOPE).length===0)throw new Error(`Access token lacks granted capability scopes (requested: [${re.join(", ")}], granted: [${te.join(", ")}]). At least one permission scope besides offline_access must be granted.`);let Y=typeof f.organizationId=="string"?f.organizationId:null,X=p.expires_in?Date.now()+p.expires_in*1e3:typeof f.exp=="number"?f.exp*1e3:Date.now()+36e5,D={serverUrl:r,clientId:s,accessToken:p.access_token,refreshToken:p.refresh_token,tokenType:p.token_type||"Bearer",scope:p.scope||o,resource:n,organizationId:Y,expiresAt:X,updatedAt:Date.now()},Q=x(r,s);await c.save(Q,D);let ne={profileKey:Q,serverUrl:r,clientId:s,organizationId:Y,scope:D.scope,resource:D.resource,expiresAt:X};if(i?.json){let v={event:"login_success",success:!0,...ne};e(JSON.stringify(v))}else e(`
17
17
  \u2705 Login successful!`),e(`- Profile: ${Q}`),e(`- Server: ${r}`),e(`- Client ID: ${s}`),e(`- Organization: ${Y||"(none / platform scope)"}`),e(`- Scopes: ${D.scope||"none"}`),e(`- Resource: ${n}`),e(`- Expires: ${new Date(X).toISOString()}
18
- `);return ne}async function Ne(i){let e=i?.logger||(d=>process.stdout.write(`${d}
19
- `)),t=O(i?.server),r=(i?.clientId||$).trim(),n=x(t,r),o=await(i?.vault||new P).load(n);if(!o){i?.json?e(JSON.stringify({authenticated:!1,profileKey:n,serverUrl:t,clientId:r})):(e(`No active session found for profile: ${n}`),e(`Run \`iwas login --server ${t}\` to authenticate.
20
- `));return}let c=M(o,60),l={authenticated:!c,expired:c,profileKey:n,serverUrl:o.serverUrl,clientId:o.clientId,organizationId:o.organizationId,scope:o.scope,resource:o.resource,expiresAt:new Date(o.expiresAt).toISOString(),expiresInSeconds:Math.max(0,Math.round((o.expiresAt-Date.now())/1e3))};i?.json?e(JSON.stringify(l)):(e(`
18
+ `);return ne}function Ee(i){if(i<=0)return"0s";let e=Math.floor(i/86400),t=Math.floor(i%86400/3600),r=Math.floor(i%3600/60),n=i%60,s=[];return e>0&&s.push(`${e}d`),t>0&&s.push(`${t}h`),r>0&&s.push(`${r}m`),(n>0||s.length===0)&&s.push(`${n}s`),s.join(" ")}async function Ue(i){let e=i?.logger||(a=>process.stdout.write(`${a}
19
+ `)),t=O(i?.server),r=(i?.clientId||R).trim(),n=x(t,r),o=await(i?.vault||new P).load(n);if(!o){i?.json?e(JSON.stringify({authenticated:!1,profileKey:n,serverUrl:t,clientId:r})):(e(`No active session found for profile: ${n}`),e(`Run \`iwas login --server ${t}\` to authenticate.
20
+ `));return}let c=M(o,60),l=Math.max(0,Math.round((o.expiresAt-Date.now())/1e3)),u=Ee(l),m={authenticated:!c,expired:c,profileKey:n,serverUrl:o.serverUrl,clientId:o.clientId,organizationId:o.organizationId,scope:o.scope,resource:o.resource,expiresAt:new Date(o.expiresAt).toISOString(),expiresInSeconds:l,expiresInFormatted:u};i?.json?e(JSON.stringify(m)):(e(`
21
21
  ============================================================`),e(" IWAS MCP Auth Status "),e(`============================================================
22
- `),e(`- Profile: ${n}`),e(`- Server: ${o.serverUrl}`),e(`- Client ID: ${o.clientId}`),e(`- Status: ${c?"\u{1F534} Expired":"\u{1F7E2} Active"}`),e(`- Organization: ${o.organizationId||"(none / platform scope)"}`),e(`- Scopes: ${o.scope||"none"}`),o.resource&&e(`- Resource: ${o.resource}`),e(`- Expires: ${l.expiresAt} (${l.expiresInSeconds}s remaining)
23
- `))}var h=I(require("fs")),k=I(require("path")),W=I(require("os")),F=require("crypto"),J=class extends Error{constructor(e){super(e),this.name="VaultLockError"}},B=class extends J{lockPath;timeoutMs;constructor(e,t){super(`Timed out waiting to acquire credentials lock at "${e}" after ${t}ms.`),this.name="VaultLockTimeoutError",this.lockPath=e,this.timeoutMs=t}},Ue=6e4,ze=6e4,je=50;function Ee(i){if(typeof i!="number"||!Number.isInteger(i)||i<=0)return!1;try{return process.kill(i,0),!0}catch(e){let t=e?.code;return t==="ESRCH"?!1:t==="EPERM"}}var L=class{lockPath;isCustomLockPath;timeoutMs;staleTimeoutMs;corruptLockGraceMs;retryIntervalMs;isProcessAlive;currentLockId=null;currentLockIno;constructor(e){if(e?.lockPath)this.lockPath=e.lockPath,this.isCustomLockPath=!0;else{let t=e?.configDir||process.env.XDG_CONFIG_HOME||k.default.join(W.default.homedir(),".config"),r=e?.configDir?e.configDir:k.default.join(t,"iwas");this.lockPath=k.default.join(r,"credentials.lock"),this.isCustomLockPath=!!e?.configDir}this.timeoutMs=e?.timeoutMs??Ue,this.staleTimeoutMs=e?.staleTimeoutMs??ze,this.corruptLockGraceMs=e?.corruptLockGraceMs??Math.min(this.staleTimeoutMs,1e4),this.retryIntervalMs=e?.retryIntervalMs??je,this.isProcessAlive=e?.isProcessAliveFn??Ee}isHeld(){return this.currentLockId!==null}getCurrentLockId(){return this.currentLockId}async acquire(){if(this.currentLockId)return;let e=k.default.dirname(this.lockPath),t=!1;if(!h.default.existsSync(e))try{h.default.mkdirSync(e,{recursive:!0,mode:448}),t=!0}catch{}if(!this.isCustomLockPath||t)try{h.default.chmodSync(e,448)}catch{}let r=Date.now(),n=(0,F.randomUUID)(),s=this.retryIntervalMs;for(;;){let o=null,c;try{o=h.default.openSync(this.lockPath,"wx",384),c=h.default.fstatSync(o).ino;let u={pid:process.pid,createdAt:Date.now(),lockId:n,hostname:W.default.hostname()};h.default.writeFileSync(o,JSON.stringify(u),"utf8"),h.default.closeSync(o),o=null,this.currentLockId=n,this.currentLockIno=c;return}catch(a){if(o!==null){try{h.default.closeSync(o)}catch{}try{let p=h.default.statSync(this.lockPath);(c===void 0||p.ino===c)&&p.size===0&&h.default.unlinkSync(this.lockPath)}catch{}}if(a?.code!=="EEXIST")throw a}if(this.tryCleanStaleLock(),Date.now()-r>=this.timeoutMs)throw new B(this.lockPath,this.timeoutMs);let d=Math.floor(Math.random()*25),m=Math.min(s+d,250);s=Math.min(s*1.5,200),await new Promise(a=>setTimeout(a,m))}}async release(){this.releaseSync()}releaseSync(){if(!this.currentLockId)return;let e=this.currentLockId,t=this.currentLockIno;this.currentLockId=null,this.currentLockIno=void 0;let r=k.default.dirname(this.lockPath),n=k.default.join(r,`.credentials.lock.release.${Date.now()}.${(0,F.randomUUID)()}`),s=!1;try{if(!h.default.existsSync(this.lockPath))return;try{h.default.linkSync(this.lockPath,n),s=!0}catch{this.releaseSyncLoose(e,t);return}let o=h.default.statSync(n);if(t!==void 0&&o.ino!==t)return;let c=h.default.readFileSync(n,"utf8"),l=null;try{l=JSON.parse(c)}catch{return}if(l&&l.lockId===e&&l.pid===process.pid){let d=h.default.statSync(this.lockPath);if(d.ino===o.ino&&d.nlink>=2)try{h.default.unlinkSync(this.lockPath)}catch{}}}catch{}finally{if(s)try{h.default.unlinkSync(n)}catch{}}}releaseSyncLoose(e,t){try{if(!h.default.existsSync(this.lockPath))return;let r=h.default.statSync(this.lockPath);if(t!==void 0&&r.ino!==t)return;let n=h.default.readFileSync(this.lockPath,"utf8"),s=null;try{s=JSON.parse(n)}catch{return}if(s&&s.lockId===e&&s.pid===process.pid){let o=h.default.statSync(this.lockPath);if(o.ino===r.ino&&o.mtimeMs===r.mtimeMs)try{h.default.unlinkSync(this.lockPath)}catch{}}}catch{}}cleanOrphanGuards(){try{let e=k.default.dirname(this.lockPath);if(!h.default.existsSync(e))return;let r=`.${k.default.basename(this.lockPath)}.`,n=h.default.readdirSync(e),s=/^(release|stale|corrupt)\.(\d+)\.([0-9a-fA-F-]{36})$/,o=null;try{o=h.default.statSync(this.lockPath)}catch{}let c=Date.now();for(let l of n){if(!l.startsWith(r))continue;let d=s.exec(l.slice(r.length));if(!d)continue;let m=Number(d[2]),a=k.default.join(e,l);try{let u=h.default.statSync(a);if(!u.isFile()||u.nlink>1&&o&&u.ino===o.ino)continue;c-m>this.staleTimeoutMs&&h.default.unlinkSync(a)}catch{}}}catch{}}async withLock(e){await this.acquire();try{return await e()}finally{await this.release()}}tryCleanStaleLock(){try{if(this.cleanOrphanGuards(),!h.default.existsSync(this.lockPath))return;let e=h.default.statSync(this.lockPath),t=null;try{let o=h.default.readFileSync(this.lockPath,"utf8");t=JSON.parse(o)}catch{Date.now()-e.mtimeMs>this.corruptLockGraceMs&&this.reclaimCorruptLock(e);return}if(!t||typeof t.createdAt!="number")return;let r=W.default.hostname(),n=!t.hostname||t.hostname===r,s=!1;n?s=!(typeof t.pid=="number"&&this.isProcessAlive(t.pid)):s=Date.now()-t.createdAt>this.staleTimeoutMs,s&&this.reclaimStaleLock(t.lockId,e)}catch{}}reclaimStaleLock(e,t){let r=k.default.dirname(this.lockPath),n=k.default.join(r,`.credentials.lock.stale.${Date.now()}.${(0,F.randomUUID)()}`);try{h.default.linkSync(this.lockPath,n);try{let s=h.default.readFileSync(n,"utf8");if(JSON.parse(s).lockId!==e)return;let c=h.default.statSync(this.lockPath);c.ino===t.ino&&c.nlink>=2&&h.default.unlinkSync(this.lockPath)}finally{try{h.default.unlinkSync(n)}catch{}}}catch{}}reclaimCorruptLock(e){let t=k.default.dirname(this.lockPath),r=k.default.join(t,`.credentials.lock.corrupt.${Date.now()}.${(0,F.randomUUID)()}`);try{h.default.linkSync(this.lockPath,r);try{let n=h.default.statSync(r);if(n.ino!==e.ino||n.size!==e.size||n.mtimeMs!==e.mtimeMs)return;try{let o=h.default.readFileSync(r,"utf8");JSON.parse(o);return}catch{}let s=h.default.statSync(this.lockPath);s.ino===e.ino&&s.nlink>=2&&h.default.unlinkSync(this.lockPath)}finally{try{h.default.unlinkSync(r)}catch{}}}catch{}}};async function Ve(i,e,t){return await new L({lockPath:i,...t}).withLock(e)}var Pe=I(require("path"));var _=class extends Error{profileKey;reason;terminalError;constructor(e,t,r){let n=r?` (${r})`:"";super(`Re-authentication required for profile "${e}": ${t}${n}`),this.name="ReauthenticationRequiredError",this.profileKey=e,this.reason=t,this.terminalError=r}},qe=60,We=6e4,Je=6e4,ee=class{vault;safetyMarginSeconds;lockTimeoutMs;staleLockTimeoutMs;lockPath;fetchFn;logger;errorLogger;inFlightRefreshes=new Map;constructor(e){this.vault=e?.vault||new P,this.safetyMarginSeconds=e?.safetyMarginSeconds??qe,this.lockTimeoutMs=e?.lockTimeoutMs??We,this.staleLockTimeoutMs=e?.staleLockTimeoutMs??e?.staleTimeoutMs??Je,this.lockPath=e?.lockPath,this.fetchFn=e?.fetchFn,this.logger=e?.logger,this.errorLogger=e?.errorLogger}async getValidAccessToken(e){return(await this.getValidCredentials(e)).accessToken}async getValidCredentials(e){let t=await this.vault.load(e);if(!t)throw new _(e,"No credentials found in vault.");return M(t,this.safetyMarginSeconds)?await this.refreshWithSingleFlight(e,!1):t}async refresh(e){let t=this.inFlightRefreshes.get(e);return t?await t:await this.refreshWithSingleFlight(e,!0)}async refreshWithSingleFlight(e,t){let r=this.inFlightRefreshes.get(e);if(r&&!t)return await r;let n=this.executeRefreshWithLock(e,t);this.inFlightRefreshes.set(e,n);try{return await n}finally{this.inFlightRefreshes.delete(e)}}async executeRefreshWithLock(e,t){let r=this.lockPath||Pe.default.join(this.vault.getConfigDir(),"credentials.lock");return await new L({lockPath:r,timeoutMs:this.lockTimeoutMs,staleTimeoutMs:this.staleLockTimeoutMs}).withLock(async()=>{let s=await this.vault.load(e);if(!s)throw new _(e,"Credentials missing from vault.");return!t&&!M(s,this.safetyMarginSeconds)?(this.logger?.(`Token for profile "${e}" was already refreshed by another process.`),s):await this.performTokenExchange(e,s)})}async performTokenExchange(e,t){if(!t.refreshToken)throw new _(e,"Cannot refresh access token: No refresh token stored in credentials.");let r=`${t.serverUrl}/api/auth/oauth2/token`,n=new URLSearchParams;n.set("grant_type","refresh_token"),n.set("refresh_token",t.refreshToken),n.set("client_id",t.clientId),t.resource&&n.set("resource",t.resource);let s;try{let a=await C(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:n,retries:1,fetchFn:this.fetchFn});if(!a.ok)throw new T(`Token refresh request failed with HTTP ${a.status}`,a.status,a.data);s=a.data}catch(a){if(a instanceof T){let u=a.data&&typeof a.data=="object"?a.data:{},p=typeof u.error=="string"?u.error:"",f=typeof u.error_description=="string"?u.error_description:a.message,y=p==="invalid_grant"||p==="invalid_client",S=E(f,[t.refreshToken,t.accessToken]);if(this.errorLogger?.(`Token refresh rejected (${a.status}, ${p||"unknown"}): ${S}`),y){try{await this.vault.delete(e)}catch{}throw new _(e,`Refresh token rejected by authorization server: ${S}`,p)}throw new T(`Token refresh rejected (${a.status}): ${S}`,a.status,a.data)}throw this.errorLogger?.(`Token refresh network/transient error for profile "${e}":
24
- ${a instanceof Error?a.message:String(a)}`),a}if(!s||!s.access_token){let a=s?.error||"unknown_error",u=s?.error_description||"Missing access_token in token response.",p=E(u,[t.refreshToken,t.accessToken]);if(this.errorLogger?.(`Token response invalid for profile "${e}": ${p}`),a==="invalid_grant"||a==="invalid_client"){try{await this.vault.delete(e)}catch{}throw new _(e,p,a)}throw new Error(`Token refresh failed: ${p}`)}let o=q(s.access_token),c;"organizationId"in o?typeof o.organizationId=="string"?c=o.organizationId:o.organizationId===null?c=null:c=t.organizationId??null:c=t.organizationId??null;let l=s.expires_in?Date.now()+s.expires_in*1e3:typeof o.exp=="number"?o.exp*1e3:Date.now()+3600*1e3,d=s.refresh_token||t.refreshToken,m={serverUrl:t.serverUrl,clientId:t.clientId,accessToken:s.access_token,refreshToken:d,tokenType:s.token_type||t.tokenType||"Bearer",scope:s.scope||t.scope,resource:t.resource,organizationId:c,expiresAt:l,updatedAt:Date.now()};try{await this.vault.save(e,m)}catch(a){try{await this.vault.delete(e)}catch{}let u=a instanceof Error?a.message:String(a);throw this.errorLogger?.(`Failed to persist refreshed credentials for profile "${e}": ${u}`),new _(e,`Failed to persist refreshed credentials to vault: ${u}`,"persistence_failure")}return this.logger?.(`Successfully refreshed credentials for profile "${e}"
25
- (expires: ${new Date(l).toISOString()}).`),m}};0&&(module.exports={CredentialVault,DEFAULT_CLI_CLIENT_ID,DEFAULT_KEYCHAIN_SERVICE,DEFAULT_READ_ONLY_SCOPES,DEFAULT_SCOPES,DEFAULT_SERVER_URL,DEVICE_CODE_GRANT_TYPE,DefaultKeychainRunner,HttpError,KeychainService,ReauthenticationRequiredError,TokenRefreshManager,VaultLock,VaultLockError,VaultLockTimeoutError,decodeJwtPayload,defaultIsProcessAlive,executeLogin,executeLogout,executeStatus,isCredentialExpired,isLocalhost,isValidStoredCredentials,normalizeProfileKey,openBrowser,pollDeviceToken,redactCredentials,requestDeviceCode,resolveAuthServerUrl,resolveMcpResourceUrl,sanitizeSecretString,sendRequest,validateScopes,withVaultLock});
22
+ `),e(`- Profile: ${n}`),e(`- Server: ${o.serverUrl}`),e(`- Client ID: ${o.clientId}`),e(`- Status: ${c?"\u{1F534} Expired":"\u{1F7E2} Active"}`),e(`- Organization: ${o.organizationId||"(none / platform scope)"}`),e(`- Scopes: ${o.scope||"none"}`),o.resource&&e(`- Resource: ${o.resource}`),e(`- Expires: ${m.expiresAt} (${u} remaining)
23
+ `))}var h=I(require("fs")),k=I(require("path")),W=I(require("os")),F=require("crypto"),J=class extends Error{constructor(e){super(e),this.name="VaultLockError"}},B=class extends J{lockPath;timeoutMs;constructor(e,t){super(`Timed out waiting to acquire credentials lock at "${e}" after ${t}ms.`),this.name="VaultLockTimeoutError",this.lockPath=e,this.timeoutMs=t}},ze=6e4,je=6e4,Ve=50;function Pe(i){if(typeof i!="number"||!Number.isInteger(i)||i<=0)return!1;try{return process.kill(i,0),!0}catch(e){let t=e?.code;return t==="ESRCH"?!1:t==="EPERM"}}var L=class{lockPath;isCustomLockPath;timeoutMs;staleTimeoutMs;corruptLockGraceMs;retryIntervalMs;isProcessAlive;currentLockId=null;currentLockIno;constructor(e){if(e?.lockPath)this.lockPath=e.lockPath,this.isCustomLockPath=!0;else{let t=e?.configDir||process.env.XDG_CONFIG_HOME||k.default.join(W.default.homedir(),".config"),r=e?.configDir?e.configDir:k.default.join(t,"iwas");this.lockPath=k.default.join(r,"credentials.lock"),this.isCustomLockPath=!!e?.configDir}this.timeoutMs=e?.timeoutMs??ze,this.staleTimeoutMs=e?.staleTimeoutMs??je,this.corruptLockGraceMs=e?.corruptLockGraceMs??Math.min(this.staleTimeoutMs,1e4),this.retryIntervalMs=e?.retryIntervalMs??Ve,this.isProcessAlive=e?.isProcessAliveFn??Pe}isHeld(){return this.currentLockId!==null}getCurrentLockId(){return this.currentLockId}async acquire(){if(this.currentLockId)return;let e=k.default.dirname(this.lockPath),t=!1;if(!h.default.existsSync(e))try{h.default.mkdirSync(e,{recursive:!0,mode:448}),t=!0}catch{}if(!this.isCustomLockPath||t)try{h.default.chmodSync(e,448)}catch{}let r=Date.now(),n=(0,F.randomUUID)(),s=this.retryIntervalMs;for(;;){let o=null,c;try{o=h.default.openSync(this.lockPath,"wx",384),c=h.default.fstatSync(o).ino;let d={pid:process.pid,createdAt:Date.now(),lockId:n,hostname:W.default.hostname()};h.default.writeFileSync(o,JSON.stringify(d),"utf8"),h.default.closeSync(o),o=null,this.currentLockId=n,this.currentLockIno=c;return}catch(a){if(o!==null){try{h.default.closeSync(o)}catch{}try{let p=h.default.statSync(this.lockPath);(c===void 0||p.ino===c)&&p.size===0&&h.default.unlinkSync(this.lockPath)}catch{}}if(a?.code!=="EEXIST")throw a}if(this.tryCleanStaleLock(),Date.now()-r>=this.timeoutMs)throw new B(this.lockPath,this.timeoutMs);let u=Math.floor(Math.random()*25),m=Math.min(s+u,250);s=Math.min(s*1.5,200),await new Promise(a=>setTimeout(a,m))}}async release(){this.releaseSync()}releaseSync(){if(!this.currentLockId)return;let e=this.currentLockId,t=this.currentLockIno;this.currentLockId=null,this.currentLockIno=void 0;let r=k.default.dirname(this.lockPath),n=k.default.join(r,`.credentials.lock.release.${Date.now()}.${(0,F.randomUUID)()}`),s=!1;try{if(!h.default.existsSync(this.lockPath))return;try{h.default.linkSync(this.lockPath,n),s=!0}catch{this.releaseSyncLoose(e,t);return}let o=h.default.statSync(n);if(t!==void 0&&o.ino!==t)return;let c=h.default.readFileSync(n,"utf8"),l=null;try{l=JSON.parse(c)}catch{return}if(l&&l.lockId===e&&l.pid===process.pid){let u=h.default.statSync(this.lockPath);if(u.ino===o.ino&&u.nlink>=2)try{h.default.unlinkSync(this.lockPath)}catch{}}}catch{}finally{if(s)try{h.default.unlinkSync(n)}catch{}}}releaseSyncLoose(e,t){try{if(!h.default.existsSync(this.lockPath))return;let r=h.default.statSync(this.lockPath);if(t!==void 0&&r.ino!==t)return;let n=h.default.readFileSync(this.lockPath,"utf8"),s=null;try{s=JSON.parse(n)}catch{return}if(s&&s.lockId===e&&s.pid===process.pid){let o=h.default.statSync(this.lockPath);if(o.ino===r.ino&&o.mtimeMs===r.mtimeMs)try{h.default.unlinkSync(this.lockPath)}catch{}}}catch{}}cleanOrphanGuards(){try{let e=k.default.dirname(this.lockPath);if(!h.default.existsSync(e))return;let r=`.${k.default.basename(this.lockPath)}.`,n=h.default.readdirSync(e),s=/^(release|stale|corrupt)\.(\d+)\.([0-9a-fA-F-]{36})$/,o=null;try{o=h.default.statSync(this.lockPath)}catch{}let c=Date.now();for(let l of n){if(!l.startsWith(r))continue;let u=s.exec(l.slice(r.length));if(!u)continue;let m=Number(u[2]),a=k.default.join(e,l);try{let d=h.default.statSync(a);if(!d.isFile()||d.nlink>1&&o&&d.ino===o.ino)continue;c-m>this.staleTimeoutMs&&h.default.unlinkSync(a)}catch{}}}catch{}}async withLock(e){await this.acquire();try{return await e()}finally{await this.release()}}tryCleanStaleLock(){try{if(this.cleanOrphanGuards(),!h.default.existsSync(this.lockPath))return;let e=h.default.statSync(this.lockPath),t=null;try{let o=h.default.readFileSync(this.lockPath,"utf8");t=JSON.parse(o)}catch{Date.now()-e.mtimeMs>this.corruptLockGraceMs&&this.reclaimCorruptLock(e);return}if(!t||typeof t.createdAt!="number")return;let r=W.default.hostname(),n=!t.hostname||t.hostname===r,s=!1;n?s=!(typeof t.pid=="number"&&this.isProcessAlive(t.pid)):s=Date.now()-t.createdAt>this.staleTimeoutMs,s&&this.reclaimStaleLock(t.lockId,e)}catch{}}reclaimStaleLock(e,t){let r=k.default.dirname(this.lockPath),n=k.default.join(r,`.credentials.lock.stale.${Date.now()}.${(0,F.randomUUID)()}`);try{h.default.linkSync(this.lockPath,n);try{let s=h.default.readFileSync(n,"utf8");if(JSON.parse(s).lockId!==e)return;let c=h.default.statSync(this.lockPath);c.ino===t.ino&&c.nlink>=2&&h.default.unlinkSync(this.lockPath)}finally{try{h.default.unlinkSync(n)}catch{}}}catch{}}reclaimCorruptLock(e){let t=k.default.dirname(this.lockPath),r=k.default.join(t,`.credentials.lock.corrupt.${Date.now()}.${(0,F.randomUUID)()}`);try{h.default.linkSync(this.lockPath,r);try{let n=h.default.statSync(r);if(n.ino!==e.ino||n.size!==e.size||n.mtimeMs!==e.mtimeMs)return;try{let o=h.default.readFileSync(r,"utf8");JSON.parse(o);return}catch{}let s=h.default.statSync(this.lockPath);s.ino===e.ino&&s.nlink>=2&&h.default.unlinkSync(this.lockPath)}finally{try{h.default.unlinkSync(r)}catch{}}}catch{}}};async function qe(i,e,t){return await new L({lockPath:i,...t}).withLock(e)}var Ce=I(require("path"));var _=class extends Error{profileKey;reason;terminalError;constructor(e,t,r){let n=r?` (${r})`:"";super(`Re-authentication required for profile "${e}": ${t}${n}`),this.name="ReauthenticationRequiredError",this.profileKey=e,this.reason=t,this.terminalError=r}},We=60,Je=6e4,Be=6e4,ee=class{vault;safetyMarginSeconds;lockTimeoutMs;staleLockTimeoutMs;lockPath;fetchFn;logger;errorLogger;inFlightRefreshes=new Map;constructor(e){this.vault=e?.vault||new P,this.safetyMarginSeconds=e?.safetyMarginSeconds??We,this.lockTimeoutMs=e?.lockTimeoutMs??Je,this.staleLockTimeoutMs=e?.staleLockTimeoutMs??e?.staleTimeoutMs??Be,this.lockPath=e?.lockPath,this.fetchFn=e?.fetchFn,this.logger=e?.logger,this.errorLogger=e?.errorLogger}async getValidAccessToken(e){return(await this.getValidCredentials(e)).accessToken}async getValidCredentials(e){let t=await this.vault.load(e);if(!t)throw new _(e,"No credentials found in vault.");return M(t,this.safetyMarginSeconds)?await this.refreshWithSingleFlight(e,!1):t}async refresh(e){let t=this.inFlightRefreshes.get(e);return t?await t:await this.refreshWithSingleFlight(e,!0)}async refreshWithSingleFlight(e,t){let r=this.inFlightRefreshes.get(e);if(r&&!t)return await r;let n=this.executeRefreshWithLock(e,t);this.inFlightRefreshes.set(e,n);try{return await n}finally{this.inFlightRefreshes.delete(e)}}async executeRefreshWithLock(e,t){let r=this.lockPath||Ce.default.join(this.vault.getConfigDir(),"credentials.lock");return await new L({lockPath:r,timeoutMs:this.lockTimeoutMs,staleTimeoutMs:this.staleLockTimeoutMs}).withLock(async()=>{let s=await this.vault.load(e);if(!s)throw new _(e,"Credentials missing from vault.");return!t&&!M(s,this.safetyMarginSeconds)?(this.logger?.(`Token for profile "${e}" was already refreshed by another process.`),s):await this.performTokenExchange(e,s)})}async performTokenExchange(e,t){if(!t.refreshToken)throw new _(e,"Cannot refresh access token: No refresh token stored in credentials.");let r=`${t.serverUrl}/api/auth/oauth2/token`,n=new URLSearchParams;n.set("grant_type","refresh_token"),n.set("refresh_token",t.refreshToken),n.set("client_id",t.clientId),t.resource&&n.set("resource",t.resource);let s;try{let a=await C(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:n,retries:1,fetchFn:this.fetchFn});if(!a.ok)throw new T(`Token refresh request failed with HTTP ${a.status}`,a.status,a.data);s=a.data}catch(a){if(a instanceof T){let d=a.data&&typeof a.data=="object"?a.data:{},p=typeof d.error=="string"?d.error:"",f=typeof d.error_description=="string"?d.error_description:a.message,y=p==="invalid_grant"||p==="invalid_client",S=E(f,[t.refreshToken,t.accessToken]);if(this.errorLogger?.(`Token refresh rejected (${a.status}, ${p||"unknown"}): ${S}`),y){try{await this.vault.delete(e)}catch{}throw new _(e,`Refresh token rejected by authorization server: ${S}`,p)}throw new T(`Token refresh rejected (${a.status}): ${S}`,a.status,a.data)}throw this.errorLogger?.(`Token refresh network/transient error for profile "${e}":
24
+ ${a instanceof Error?a.message:String(a)}`),a}if(!s||!s.access_token){let a=s?.error||"unknown_error",d=s?.error_description||"Missing access_token in token response.",p=E(d,[t.refreshToken,t.accessToken]);if(this.errorLogger?.(`Token response invalid for profile "${e}": ${p}`),a==="invalid_grant"||a==="invalid_client"){try{await this.vault.delete(e)}catch{}throw new _(e,p,a)}throw new Error(`Token refresh failed: ${p}`)}let o=q(s.access_token),c;"organizationId"in o?typeof o.organizationId=="string"?c=o.organizationId:o.organizationId===null?c=null:c=t.organizationId??null:c=t.organizationId??null;let l=s.expires_in?Date.now()+s.expires_in*1e3:typeof o.exp=="number"?o.exp*1e3:Date.now()+3600*1e3,u=s.refresh_token||t.refreshToken,m={serverUrl:t.serverUrl,clientId:t.clientId,accessToken:s.access_token,refreshToken:u,tokenType:s.token_type||t.tokenType||"Bearer",scope:s.scope||t.scope,resource:t.resource,organizationId:c,expiresAt:l,updatedAt:Date.now()};try{await this.vault.save(e,m)}catch(a){try{await this.vault.delete(e)}catch{}let d=a instanceof Error?a.message:String(a);throw this.errorLogger?.(`Failed to persist refreshed credentials for profile "${e}": ${d}`),new _(e,`Failed to persist refreshed credentials to vault: ${d}`,"persistence_failure")}return this.logger?.(`Successfully refreshed credentials for profile "${e}"
25
+ (expires: ${new Date(l).toISOString()}).`),m}};0&&(module.exports={CredentialVault,DEFAULT_CLI_CLIENT_ID,DEFAULT_KEYCHAIN_SERVICE,DEFAULT_READ_ONLY_SCOPES,DEFAULT_SCOPES,DEFAULT_SERVER_URL,DEVICE_CODE_GRANT_TYPE,DefaultKeychainRunner,HttpError,KeychainService,ReauthenticationRequiredError,TokenRefreshManager,VaultLock,VaultLockError,VaultLockTimeoutError,decodeJwtPayload,defaultIsProcessAlive,executeLogin,executeLogout,executeStatus,formatRemainingDuration,isCredentialExpired,isLocalhost,isValidStoredCredentials,normalizeProfileKey,openBrowser,pollDeviceToken,redactCredentials,requestDeviceCode,resolveAuthServerUrl,resolveMcpResourceUrl,sanitizeSecretString,sendRequest,validateScopes,withVaultLock});