@quantiya/codevibe-claude-plugin 1.0.49 → 1.0.51
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/.claude-plugin/plugin.json +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/appsync/event-throttle.d.ts +28 -0
- package/node_modules/@quantiya/codevibe-core/dist/auth/auth-telemetry.d.ts +18 -0
- package/node_modules/@quantiya/codevibe-core/dist/index.js +31 -29
- package/node_modules/@quantiya/codevibe-core/dist/keychain/keychain-backend.d.ts +59 -0
- package/node_modules/@quantiya/codevibe-core/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevibe-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"description": "Sync Claude Code sessions with iOS mobile app via AWS backend. Control Claude Code from your phone with real-time bidirectional synchronization.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodeVibe Team"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Exposed for the single log line in createEvent. */
|
|
2
|
+
export declare const CLIENT_THROTTLE_EVENTS_PER_WINDOW: number;
|
|
3
|
+
interface RateWindow {
|
|
4
|
+
windowStartMs: number;
|
|
5
|
+
count: number;
|
|
6
|
+
toolUseAboveThreshold: number;
|
|
7
|
+
suppressed: number;
|
|
8
|
+
logged: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Decide whether this outbound event should be suppressed (not sent).
|
|
12
|
+
* Deterministic given (sessionId, type, now) and prior counter state.
|
|
13
|
+
* Exported for unit tests; resetThrottleState() clears the module map.
|
|
14
|
+
*/
|
|
15
|
+
export declare function shouldSuppressEvent(sessionId: string, type: string, now: number): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the session's rate window exactly once per window — the first time the
|
|
18
|
+
* throttle engages — for a single log line; later calls in the same window
|
|
19
|
+
* return undefined. Keeps logging out of the hot path.
|
|
20
|
+
*/
|
|
21
|
+
export declare function consumeThrottleLogOnce(sessionId: string): RateWindow | undefined;
|
|
22
|
+
/** Test-only: clears the per-process rate map. */
|
|
23
|
+
export declare function resetThrottleState(): void;
|
|
24
|
+
/** Test-only: current size of the per-process rate map (eviction-bound check). */
|
|
25
|
+
export declare function throttleMapSize(): number;
|
|
26
|
+
/** Test-only: whether a session is currently tracked (eviction assertions). */
|
|
27
|
+
export declare function throttleMapHas(sessionId: string): boolean;
|
|
28
|
+
export {};
|
|
@@ -61,6 +61,24 @@ export declare function fireAuthFailedBeacon(reason: AuthFailureReason, extra?:
|
|
|
61
61
|
stage?: AuthStage;
|
|
62
62
|
errorFragment?: string;
|
|
63
63
|
}): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Coarse reason a machine fell back to file-based credential storage.
|
|
66
|
+
* - `no_keyring_service`: keytar's native addon LOADED but there's no OS
|
|
67
|
+
* keyring service to talk to (structurally-headless Linux — no D-Bus session).
|
|
68
|
+
* - `keytar_load_failed`: keytar's native addon failed to load AND the machine
|
|
69
|
+
* is structurally headless (libsecret absent on a no-keyring box).
|
|
70
|
+
*/
|
|
71
|
+
export type KeychainFallbackReason = 'no_keyring_service' | 'keytar_load_failed';
|
|
72
|
+
/**
|
|
73
|
+
* Fire the `keychain_file_fallback` beacon when keychain-backend auto-selects
|
|
74
|
+
* the file backend on a structurally-headless machine. Deliberately a DISTINCT
|
|
75
|
+
* event from `auth_failed` — this is a successful, working fallback, not a
|
|
76
|
+
* failure. Lets analytics see the headless cohort and confirm the headless fix
|
|
77
|
+
* is reaching the field (the old `keychain_write_failed` signal goes quiet for
|
|
78
|
+
* these users, so we need a positive beacon, not just an absence). No PII: only
|
|
79
|
+
* platform / source / the coarse reason literal.
|
|
80
|
+
*/
|
|
81
|
+
export declare function fireKeychainFileFallbackBeacon(reason: KeychainFallbackReason): Promise<void>;
|
|
64
82
|
/**
|
|
65
83
|
* Attach the reason + beaconed marker to an Error. Non-enumerable so
|
|
66
84
|
* the symbols don't serialize if the error object is ever JSON'd.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,
|
|
3
|
-
`)}catch{}if(this.enableConsole)switch(e){case"error":console.error(i);break;case"warn":console.warn(i);break;default:console.log(i)}}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}setLevel(e){this.level=e}};c=new
|
|
4
|
-
`)}catch{}}function
|
|
5
|
-
`))}function jt(n){return n.replace(/[^a-zA-Z0-9._-]/g,"_")}function ot(n){return Re.join(De,`${jt(n)}.json`)}function Ke(n){try{let e=N.readFileSync(ot(n),"utf-8"),t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function at(n,e){let t=ot(n);N.writeFileSync(t,JSON.stringify(e,null,2),{mode:384});try{N.chmodSync(t,384)}catch{}}function Pe(){if(Jt(),q===null)throw _e??new ve("Keychain backend not initialized")}async function Oe(n,e){return Pe(),q==="keytar"&&_?_.getPassword(n,e):Ke(n)[e]??null}async function Ne(n,e,t){if(Pe(),q==="keytar"&&_){await _.setPassword(n,e,t);return}let r=Ke(n);r[e]=t,at(n,r)}async function Ue(n,e){if(Pe(),q==="keytar"&&_)return _.deletePassword(n,e);let t=Ke(n);return e in t?(delete t[e],at(n,t),!0):!1}var st,Re,N,ve,q,_,De,_e,ct=D(()=>{"use strict";st=v(require("os")),Re=v(require("path")),N=v(require("fs"));H();ve=class extends Error{constructor(e){super(e),this.name="KeychainBackendUnavailableError"}},q=null,_=null,De="",_e=null});var T,R,$e,Yt,Q,E,dt=D(()=>{"use strict";T=v(require("crypto")),R=class extends Error{constructor(e){super(e),this.name="CryptoError"}},$e=1,Yt="CodeVibe E2E v1",Q=class n{constructor(){}static getInstance(){return n.instance||(n.instance=new n),n.instance}generateKeyPair(){let e=T.createECDH("prime256v1");e.generateKeys();let r=e.getPublicKey().subarray(1).toString("base64");return{privateKey:e.getPrivateKey().toString("base64"),publicKey:r}}generateSessionKey(){return T.randomBytes(32).toString("base64")}deriveSharedKey(e,t){try{let r=T.createECDH("prime256v1"),i=Buffer.from(e,"base64");r.setPrivateKey(i);let s=Buffer.from(t,"base64"),o=s.length===65&&s[0]===4?s:Buffer.concat([Buffer.from([4]),s]),a=r.computeSecret(o),d=T.hkdfSync("sha256",a,Buffer.alloc(0),Buffer.from(Yt,"utf8"),32);return Buffer.from(d)}catch(r){throw new R(`Failed to derive shared key: ${r}`)}}encryptSessionKey(e,t){let r=this.generateKeyPair(),i=this.deriveSharedKey(r.privateKey,t),s=Buffer.from(e,"base64");return{encryptedKey:this.encrypt(s,i).toString("base64"),ephemeralPublicKey:r.publicKey}}decryptSessionKey(e,t){let r=this.deriveSharedKey(t,e.ephemeralPublicKey),i=Buffer.from(e.encryptedKey,"base64");return this.decrypt(i,r).toString("base64")}encryptContent(e,t){let r=Buffer.from(t,"base64"),i=Buffer.from(e,"utf8");return this.encrypt(i,r).toString("base64")}decryptContent(e,t){let r=Buffer.from(t,"base64"),i=Buffer.from(e,"base64");return this.decrypt(i,r).toString("utf8")}encryptMetadata(e,t){let r=JSON.stringify(e);return this.encryptContent(r,t)}decryptMetadata(e,t){let r=this.decryptContent(e,t);return JSON.parse(r)}encryptData(e,t){let r=Buffer.from(t,"base64");return this.encrypt(e,r)}decryptData(e,t){let r=Buffer.from(t,"base64");return this.decrypt(e,r)}encrypt(e,t){let r=T.randomBytes(12),i=T.createCipheriv("aes-256-gcm",t,r),s=Buffer.concat([i.update(e),i.final()]),o=i.getAuthTag();return Buffer.concat([r,s,o])}decrypt(e,t){let r=e.subarray(0,12),i=e.subarray(e.length-16),s=e.subarray(12,e.length-16),o=T.createDecipheriv("aes-256-gcm",t,r);o.setAuthTag(i);try{return Buffer.concat([o.update(s),o.final()])}catch{throw new R("Decryption failed: Invalid ciphertext or authentication tag")}}serializePrivateKey(e){return e}deserializePrivateKey(e){return e}},E=Q.getInstance()});var ae=D(()=>{"use strict";dt()});function A(){let n=process.env.ENVIRONMENT;return n==="development"||n==="production"?n:"production"}function we(n){let e=n||A();return Se={...V[e],aws:{...V[e].aws,region:process.env.AWS_REGION||V[e].aws.region,appsyncUrl:process.env.APPSYNC_URL||V[e].aws.appsyncUrl,cognitoUserPoolId:process.env.COGNITO_USER_POOL_ID||V[e].aws.cognitoUserPoolId,cognitoClientId:process.env.COGNITO_CLIENT_ID||V[e].aws.cognitoClientId,cognitoDomain:process.env.COGNITO_DOMAIN||V[e].aws.cognitoDomain}},lt=!0,Se}function w(){return(!lt||!Se)&&we(),Se}var ce,de,V,Se,lt,pt=D(()=>{"use strict";ce=v(require("os")),de=v(require("path")),V={development:{environment:"development",aws:{region:"us-east-1",appsyncUrl:"https://api-dev.codevibe.quantiya.ai/graphql",cognitoUserPoolId:"us-east-1_yVwWDPvvJ",cognitoClientId:"e9r5apv6v5uui3l928r2ris0r",cognitoDomain:"codevibe-development.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:de.default.join(ce.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:de.default.join(ce.default.homedir(),".gemini","tmp")}},production:{environment:"production",aws:{region:"us-east-1",appsyncUrl:"https://api.codevibe.quantiya.ai/graphql",cognitoUserPoolId:"us-east-1_mNRO0j5og",cognitoClientId:"5p04dbc9ojptc5r8n7605fg78f",cognitoDomain:"codevibe-production.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:de.default.join(ce.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:de.default.join(ce.default.homedir(),".gemini","tmp")}}},Se=null,lt=!1});var ee=D(()=>{"use strict";pt()});var ke,ut,K,Le,Xt,G,g,ht=D(()=>{"use strict";ke=v(require("os")),ut=require("uuid");ct();ae();ee();H();K=class extends Error{constructor(e){super(e),this.name="KeychainError"}},Le="device-identity",Xt="tokens-",G=class n{constructor(){this.deviceIdentity=null;this.sessionKeyCache=new Map;this.isRegistered=!1;this._serviceName=null}get serviceName(){return this._serviceName||(this._serviceName=w().keychain.serviceName),this._serviceName}static getInstance(){return n.instance||(n.instance=new n),n.instance}async getDeviceIdentity(){if(this.deviceIdentity)return this.deviceIdentity;let e=await Oe(this.serviceName,Le);return e?(this.deviceIdentity=JSON.parse(e),c.info(`[KeychainManager] Loaded device identity: ${this.deviceIdentity.deviceId}`),this.deviceIdentity):null}async setDeviceIdentity(e){try{await Ne(this.serviceName,Le,JSON.stringify(e)),this.deviceIdentity=e,c.info(`[KeychainManager] Saved device identity: ${e.deviceId}`)}catch(t){throw c.error(`[KeychainManager] Failed to save device identity: ${t}`),new K(`Failed to save device identity: ${t}`)}}async getOrCreateDeviceIdentity(){let e=await this.getDeviceIdentity();if(e)return e;let t=E.generateKeyPair();return e={deviceId:(0,ut.v4)().toUpperCase(),privateKey:t.privateKey,publicKey:t.publicKey,createdAt:new Date().toISOString()},await this.setDeviceIdentity(e),c.info(`[KeychainManager] Generated new device identity: ${e.deviceId}`),e}async getDeviceId(){return(await this.getOrCreateDeviceIdentity()).deviceId}async getDevicePublicKey(){return(await this.getOrCreateDeviceIdentity()).publicKey}async getDevicePrivateKey(){return(await this.getOrCreateDeviceIdentity()).privateKey}async hasDeviceIdentity(){return await this.getDeviceIdentity()!==null}async deleteDeviceIdentity(){try{await Ue(this.serviceName,Le),this.deviceIdentity=null,this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Deleted device identity")}catch(e){throw c.error(`[KeychainManager] Failed to delete device identity: ${e}`),new K(`Failed to delete device identity: ${e}`)}}getTokenAccount(e){return`${Xt}${e}`}async getTokens(e="production"){let t=await Oe(this.serviceName,this.getTokenAccount(e));if(!t)return null;let r=JSON.parse(t);return c.debug(`[KeychainManager] Loaded tokens for ${e}`),r}async setTokens(e,t="production"){try{await Ne(this.serviceName,this.getTokenAccount(t),JSON.stringify(e)),c.info(`[KeychainManager] Saved tokens for ${t}`,{userId:e.userId,email:e.email})}catch(r){throw c.error(`[KeychainManager] Failed to save tokens: ${r}`),new K(`Failed to save tokens: ${r}`)}}async deleteTokens(e="production"){try{let t=await Ue(this.serviceName,this.getTokenAccount(e));return t&&c.info(`[KeychainManager] Deleted tokens for ${e}`),t}catch(t){return c.error(`[KeychainManager] Failed to delete tokens: ${t}`),!1}}isTokenExpired(e){return Date.now()>=e.expiresAt-3e5}async getSessionKey(e,t){let r=this.sessionKeyCache.get(e);if(r)return r;if(!t||t.length===0)return null;let i=await this.getDeviceId(),s=t.find(d=>d.deviceId===i);if(!s)return c.warn(`[KeychainManager] Device ${i} not found in encryptedKeys`),null;let o=await this.getDevicePrivateKey(),a=E.decryptSessionKey(s,o);return this.sessionKeyCache.set(e,a),c.info(`[KeychainManager] Decrypted and cached session key for ${e}`),a}createSessionKey(e,t){let r=E.generateSessionKey(),i=[],s=[];for(let o of e)try{let a=E.encryptSessionKey(r,o.publicKey);i.push({deviceId:o.deviceId,encryptedKey:a.encryptedKey,ephemeralPublicKey:a.ephemeralPublicKey})}catch(a){c.warn("[KeychainManager] Skipping device with invalid public key",{deviceId:o.deviceId,error:a instanceof Error?a.message:String(a)}),s.push(o.deviceId);try{t?.onDeviceSkipped?.(s.length)}catch{}}if(i.length===0)throw new R(`Failed to encrypt session key for any of ${e.length} devices`);return c.info("[KeychainManager] Created session key",{encryptedCount:i.length,skippedCount:s.length,totalCount:e.length}),{sessionKey:r,encryptedKeys:i,skippedDeviceIds:s}}cacheSessionKey(e,t){this.sessionKeyCache.set(e,t)}getCachedSessionKey(e){return this.sessionKeyCache.get(e)??null}getCachedSessionIds(){return Array.from(this.sessionKeyCache.keys())}clearSessionKey(e){this.sessionKeyCache.delete(e)}clearAllSessionKeys(){this.sessionKeyCache.clear()}getIsRegistered(){return this.isRegistered}setIsRegistered(e){this.isRegistered=e}getDeviceName(){return ke.hostname()||"CLI Client"}getDevicePlatform(){let e=ke.platform();return e==="darwin"?"MACOS":e==="linux"?"LINUX":e==="win32"?"WINDOWS":"CLI"}async clearAllData(){await this.deleteDeviceIdentity(),await this.deleteTokens("development"),await this.deleteTokens("production"),this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Cleared all data")}},g=G.getInstance()});var gt={};et(gt,{KeychainError:()=>K,KeychainManager:()=>G,keychainManager:()=>g});var P=D(()=>{"use strict";ht()});var Er={};et(Er,{AgentType:()=>St,AppSyncClient:()=>le,AuthService:()=>se,CryptoError:()=>R,CryptoService:()=>Q,DeliveryStatus:()=>vt,ENCRYPTION_VERSION:()=>$e,EventSource:()=>Me,EventType:()=>ft,KeychainError:()=>K,KeychainManager:()=>G,Logger:()=>F,PORT_RANGE_SIZE:()=>ie,PRIMARY_PORT:()=>ne,SessionStatus:()=>be,_resetPrepareEventTimestampForTesting:()=>Xe,authService:()=>O,bindOAuthServer:()=>he,createLogger:()=>xe,cryptoService:()=>E,errorWasBeaconed:()=>ue,fireAuthCompletedBeacon:()=>pe,fireAuthFailedBeacon:()=>I,getConfig:()=>w,getEnvironment:()=>A,getErrorReason:()=>Fe,keychainManager:()=>g,loadConfig:()=>we,logger:()=>c,markErrorBeaconed:()=>C,mutations:()=>x,normalizeSnapshot:()=>Ge,parseInteractivePrompt:()=>Ot,prepareEventTimestamp:()=>Ye,prepareSessionEncryption:()=>Ae,queries:()=>U,registerDeviceEncryptionKey:()=>ge,rekeySessionForNewDevices:()=>Y,resumeOrCreateSession:()=>je,runAuthCli:()=>Ie,startDeviceKeyWatcher:()=>ze,subscriptions:()=>J});module.exports=Ht(Er);P();ae();var j=v(require("ws")),z=require("uuid");ee();H();P();var yt=v(require("dns")),mt=v(require("fs"));if(Zt())try{yt.setDefaultResultOrder("ipv4first")}catch{}function Zt(){if(process.platform!=="linux")return!1;try{let n=mt.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(n)}catch{return!1}}async function te(n,e,t){try{return await fetch(n,e)}catch(r){let i=r?.cause?.code,s=r?.cause?.message,o=i||s||r?.message||"unknown",a=Qt(i),d=t?`${t}: `:"",l=`Node ${process.version} on ${process.platform}`,y=[`${d}Cannot reach ${n}`,` Underlying error: ${o}`];a&&y.push(` Suggested fix: ${a}`),y.push(` Platform: ${l}`);let p=new Error(y.join(`
|
|
6
|
-
`));
|
|
1
|
+
"use strict";var Xt=Object.create;var ke=Object.defineProperty;var Zt=Object.getOwnPropertyDescriptor;var Qt=Object.getOwnPropertyNames;var er=Object.getPrototypeOf,tr=Object.prototype.hasOwnProperty;var D=(n,e)=>()=>(n&&(e=n(n=0)),e);var st=(n,e)=>{for(var t in e)ke(n,t,{get:e[t],enumerable:!0})},ot=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Qt(e))!tr.call(n,i)&&i!==t&&ke(n,i,{get:()=>e[i],enumerable:!(r=Zt(e,i))||r.enumerable});return n};var v=(n,e,t)=>(t=n!=null?Xt(er(n)):{},ot(e||!n||!n.__esModule?ke(t,"default",{value:n,enumerable:!0}):t,n)),rr=n=>ot(ke({},"__esModule",{value:!0}),n);function nr(n,e){if(e instanceof Error){let t={name:e.name,message:e.message};e.stack&&(t.stack=e.stack);for(let r of Object.keys(e))r in t||(t[r]=e[r]);return t}return e}function Oe(n){return new H(n)}var ee,we,ct,at,H,c,dt=D(()=>{"use strict";ee=v(require("fs")),we=v(require("path")),ct=v(require("os")),at={debug:0,info:1,warn:2,error:3};H=class{constructor(e){this.name=e.name,this.logFile=e.logFile,this.level=e.level||"info",this.enableConsole=e.console??!1,this.logFile&&this.ensureLogDir()}ensureLogDir(){if(this.logFile){let e=we.dirname(this.logFile);ee.existsSync(e)||ee.mkdirSync(e,{recursive:!0})}}shouldLog(e){return at[e]>=at[this.level]}formatMessage(e,t,r){let i=new Date().toISOString(),s=e.toUpperCase().padEnd(5),o=`[${i}] [${s}] [${this.name}] ${t}`;return r!==void 0&&(r instanceof Error?(o+=` ${r.name}: ${r.message}`,r.stack&&(o+=`
|
|
2
|
+
${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,nr)}`:o+=` ${r}`),o}log(e,t,r){if(!this.shouldLog(e))return;let i=this.formatMessage(e,t,r);if(this.logFile)try{ee.appendFileSync(this.logFile,i+`
|
|
3
|
+
`)}catch{}if(this.enableConsole)switch(e){case"error":console.error(i);break;case"warn":console.warn(i);break;default:console.log(i)}}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}setLevel(e){this.level=e}};c=new H({name:"codevibe-core",logFile:we.join(ct.tmpdir(),"codevibe-core.log"),level:"info"})});var q=D(()=>{"use strict";dt()});function dr(){let n=typeof process.getuid=="function"?process.getuid():0;return Pe.createHash("sha256").update(`${pt.hostname()}-${n}`).digest("hex").substring(0,36)}function K(){return{platform:process.platform,source:process.env.CODEVIBE_TELEMETRY_SOURCE||"production"}}async function O(n,e){try{let t=JSON.stringify({client_id:dr(),events:[{name:n,params:e}]});await new Promise(r=>{let i=lt.request({hostname:or,path:ar,method:"POST",headers:{"Content-Type":"application/json"}},()=>r());i.on("error",()=>r()),i.write(t),i.end(),setTimeout(r,2e3)})}catch{}}async function le(n){await O("auth_completed",{...K(),user_id:n})}async function I(n,e){let t={...K(),reason:n,stage:e?.stage??cr[n]};if(typeof e?.httpStatus=="number"&&(t.http_status=e.httpStatus),e?.errorFragment){let{homedir:r}=await import("os"),i=e.errorFragment.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,"").replace(/\\/g,"/").replace(/[\n\r\t"]/g," ").replace(/[^\x20-\x7E]/g,"").trim(),s=[process.env.HOME,process.env.USERPROFILE,(()=>{try{return r()}catch{return}})()].filter(d=>typeof d=="string"&&d.length>0).map(d=>d.replace(/\\/g,"/"));for(let d of s){let l=d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=i.replace(new RegExp(l,"g"),"~")}i=i.replace(/\/Users\/[^/ ]+/g,"/Users/<user>").replace(/\/home\/[^/ ]+/g,"/home/<user>").replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,"<email>");let o=i.substring(0,100),a=i.substring(100,200);o&&(t.error_fragment=o),a&&(t.error_fragment_2=a)}await O("auth_failed",t)}async function ut(n){await O("keychain_file_fallback",{...K(),reason:n})}function _(n,e){try{Object.defineProperty(n,Ne,{value:!0,enumerable:!1,configurable:!0,writable:!1}),Object.defineProperty(n,ht,{value:e,enumerable:!1,configurable:!0,writable:!1})}catch{}return n}function pe(n){return!!(n&&typeof n=="object"&&n[Ne])}function Ue(n){if(n&&typeof n=="object"&&n[Ne]){let e=n[ht];if(typeof e=="string")return e}}function te(n){return n<=0?"0":n===1?"1":n<=5?"2-5":"6+"}function Ee(n){return Pe.createHash("sha256").update(n).digest("hex").slice(0,8)}async function gt(n){return O("session_encryption_device_skipped",{...K(),...n})}async function yt(n){return O("session_encryption_partial_success",{...K(),...n})}async function ft(n){return O("session_encryption_catch_up_grant",{...K(),...n})}async function mt(n){return O("session_encryption_self_rekey_request",{...K(),...n})}async function vt(n){return O("session_encryption_self_rekey_success",{...K(),...n})}async function St(n){return O("session_encryption_self_rekey_timeout",{...K(),...n})}var Pe,lt,pt,ir,sr,or,ar,cr,Ne,ht,re=D(()=>{"use strict";Pe=v(require("crypto")),lt=v(require("https")),pt=v(require("os")),ir="G-GS74YEQTB8",sr="lAfOF6OxRzSQ-NsLBRjhAg",or="www.google-analytics.com",ar=`/mp/collect?measurement_id=${ir}&api_secret=${sr}`,cr={port_in_use:"server_start",port_range_exhausted:"server_start",server_listen_failed:"server_start",browser_open_failed:"browser_open",login_timeout:"awaiting_callback",cognito_rejected:"awaiting_callback",state_mismatch:"awaiting_callback",no_authorization_code:"awaiting_callback",token_exchange_failed:"exchanging_code",token_exchange_network_error:"exchanging_code",keychain_write_failed:"storing_tokens",user_aborted:"unknown",unknown:"unknown"};Ne=Symbol.for("codevibe.auth.beaconed"),ht=Symbol.for("codevibe.auth.failureReason")});function Et(n){for(let e of n)try{process.stderr.write(e+`
|
|
4
|
+
`)}catch{}}function Le(){$e=V.join(he.homedir(),".codevibe");try{w.mkdirSync($e,{recursive:!0,mode:448})}catch{}L="file"}function bt(){if(process.platform!=="linux"||process.env.DISPLAY||process.env.WAYLAND_DISPLAY||process.env.DBUS_SESSION_BUS_ADDRESS)return!1;try{let n=process.env.XDG_RUNTIME_DIR;if(n&&w.existsSync(V.join(n,"bus")))return!1;let e=typeof process.getuid=="function"?process.getuid():void 0;if(e!==void 0&&w.existsSync(`/run/user/${e}/bus`))return!1}catch{}return!0}function It(){return V.join(he.homedir(),".codevibe",".keyring-used")}function lr(){if(process.platform==="linux")try{w.mkdirSync(V.join(he.homedir(),".codevibe"),{recursive:!0,mode:448}),w.writeFileSync(It(),`keytar
|
|
5
|
+
`,{mode:384})}catch{}}function pr(){try{return w.existsSync(It())}catch{return!1}}function ur(){try{let n=V.join(he.homedir(),".codevibe");return w.readdirSync(n).some(e=>e.endsWith(".json"))}catch{return!1}}function hr(n){Et(["","\u26A0 CodeVibe: no OS keyring service detected on this machine.","\u26A0 Using file-based credential storage at ~/.codevibe/ instead","\u26A0 (directory 0700, files 0600 \u2014 trust level equivalent to ~/.ssh/id_rsa,","\u26A0 weaker than an OS keyring). This is expected on headless / SSH / Docker / CI.","\u26A0 For the OS keyring, run inside a desktop session with a keyring daemon","\u26A0 (Linux) or on macOS / Windows.",""]),c.warn(`[keychain-backend] No OS keyring service (${n}); auto-selected file storage at ~/.codevibe (headless fallback)`),wt=!0,Le(),ut(n)}function gr(){c.info("[keychain-backend] OS keyring is reachable, but durable file credentials already exist at ~/.codevibe; continuing on the file backend to avoid forking the device identity"),Le()}function kt(n){if(pr()){Ie=new ue(["CodeVibe used the OS keyring on this machine before, but it is not","reachable in this session (no desktop session / no D-Bus session bus \u2014","e.g. SSH without a forwarded bus).","","Auto-switching to file storage here would create a SEPARATE credential","identity and break your existing encrypted sessions, so we stop instead.","","Options:"," 1. Run inside the desktop session where the keyring is unlocked, or"," 2. Explicitly switch THIS machine to file-based storage (a new, separate"," credential identity):"," export CODEVIBE_ALLOW_FILE_KEYCHAIN=1"].join(`
|
|
6
|
+
`)),c.warn("[keychain-backend] OS keyring used here before but unreachable now; refusing silent file fallback (set CODEVIBE_ALLOW_FILE_KEYCHAIN=1 to override)");return}hr(n)}function Qr(){return wt}function en(){return L}function yr(){if(L!==null||Ie!==null)return;let optedIn=process.env.CODEVIBE_ALLOW_FILE_KEYCHAIN==="1";if(optedIn){Et(["","\u26A0 CodeVibe: file-based credential storage selected (CODEVIBE_ALLOW_FILE_KEYCHAIN=1).","\u26A0 Location: ~/.codevibe/ (directory 0700, files 0600)","\u26A0 Trust level: equivalent to ~/.ssh/id_rsa \u2014 weaker than OS keyring.","\u26A0 To use the OS keyring instead, unset CODEVIBE_ALLOW_FILE_KEYCHAIN and","\u26A0 install libsecret-1-0 + a running keyring daemon (Linux) or use the","\u26A0 native Keychain (macOS) / Credential Manager (Windows).",""]),c.warn("[keychain-backend] Using file-based storage at ~/.codevibe (CODEVIBE_ALLOW_FILE_KEYCHAIN=1 explicit opt-in)"),Le();return}if(ur()){gr();return}let keytarLoadError=null;try{let nodeRequire=eval("require");R=nodeRequire("keytar")}catch(n){keytarLoadError=n instanceof Error?n.message:String(n),R=null}if(R){if(bt()){R=null,kt("no_keyring_service");return}L="keytar",c.info("[keychain-backend] Using keytar (OS-native keyring)"),lr();return}if(bt()){kt("keytar_load_failed");return}Ie=new ue(["CodeVibe could not load the OS-native keyring (keytar).",`Reason: ${keytarLoadError??"unknown"}`,"","Options to fix this:"," 1. (Linux) Install libsecret and a keyring daemon:"," sudo apt install libsecret-1-0 gnome-keyring"," Then unlock the keyring for your user session.",""," 2. (Headless / CI / Docker) Opt in to file-based credential"," storage at ~/.codevibe/ (0600 files). This is equivalent"," in trust to ~/.ssh/id_rsa \u2014 not the OS keyring:"," export CODEVIBE_ALLOW_FILE_KEYCHAIN=1"].join(`
|
|
7
|
+
`))}function fr(n){return n.replace(/[^a-zA-Z0-9._-]/g,"_")}function Tt(n){return V.join($e,`${fr(n)}.json`)}function Me(n){try{let e=w.readFileSync(Tt(n),"utf-8"),t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function At(n,e){let t=Tt(n);w.writeFileSync(t,JSON.stringify(e,null,2),{mode:384});try{w.chmodSync(t,384)}catch{}}function We(){if(yr(),L===null)throw Ie??new ue("Keychain backend not initialized")}async function Be(n,e){return We(),L==="keytar"&&R?R.getPassword(n,e):Me(n)[e]??null}async function Fe(n,e,t){if(We(),L==="keytar"&&R){await R.setPassword(n,e,t);return}let r=Me(n);r[e]=t,At(n,r)}async function He(n,e){if(We(),L==="keytar"&&R)return R.deletePassword(n,e);let t=Me(n);return e in t?(delete t[e],At(n,t),!0):!1}var he,V,w,ue,L,R,$e,Ie,wt,_t=D(()=>{"use strict";he=v(require("os")),V=v(require("path")),w=v(require("fs"));q();re();ue=class extends Error{constructor(e){super(e),this.name="KeychainBackendUnavailableError"}},L=null,R=null,$e="",Ie=null,wt=!1});var x,P,qe,vr,ne,T,xt=D(()=>{"use strict";x=v(require("crypto")),P=class extends Error{constructor(e){super(e),this.name="CryptoError"}},qe=1,vr="CodeVibe E2E v1",ne=class n{constructor(){}static getInstance(){return n.instance||(n.instance=new n),n.instance}generateKeyPair(){let e=x.createECDH("prime256v1");e.generateKeys();let r=e.getPublicKey().subarray(1).toString("base64");return{privateKey:e.getPrivateKey().toString("base64"),publicKey:r}}generateSessionKey(){return x.randomBytes(32).toString("base64")}deriveSharedKey(e,t){try{let r=x.createECDH("prime256v1"),i=Buffer.from(e,"base64");r.setPrivateKey(i);let s=Buffer.from(t,"base64"),o=s.length===65&&s[0]===4?s:Buffer.concat([Buffer.from([4]),s]),a=r.computeSecret(o),d=x.hkdfSync("sha256",a,Buffer.alloc(0),Buffer.from(vr,"utf8"),32);return Buffer.from(d)}catch(r){throw new P(`Failed to derive shared key: ${r}`)}}encryptSessionKey(e,t){let r=this.generateKeyPair(),i=this.deriveSharedKey(r.privateKey,t),s=Buffer.from(e,"base64");return{encryptedKey:this.encrypt(s,i).toString("base64"),ephemeralPublicKey:r.publicKey}}decryptSessionKey(e,t){let r=this.deriveSharedKey(t,e.ephemeralPublicKey),i=Buffer.from(e.encryptedKey,"base64");return this.decrypt(i,r).toString("base64")}encryptContent(e,t){let r=Buffer.from(t,"base64"),i=Buffer.from(e,"utf8");return this.encrypt(i,r).toString("base64")}decryptContent(e,t){let r=Buffer.from(t,"base64"),i=Buffer.from(e,"base64");return this.decrypt(i,r).toString("utf8")}encryptMetadata(e,t){let r=JSON.stringify(e);return this.encryptContent(r,t)}decryptMetadata(e,t){let r=this.decryptContent(e,t);return JSON.parse(r)}encryptData(e,t){let r=Buffer.from(t,"base64");return this.encrypt(e,r)}decryptData(e,t){let r=Buffer.from(t,"base64");return this.decrypt(e,r)}encrypt(e,t){let r=x.randomBytes(12),i=x.createCipheriv("aes-256-gcm",t,r),s=Buffer.concat([i.update(e),i.final()]),o=i.getAuthTag();return Buffer.concat([r,s,o])}decrypt(e,t){let r=e.subarray(0,12),i=e.subarray(e.length-16),s=e.subarray(12,e.length-16),o=x.createDecipheriv("aes-256-gcm",t,r);o.setAuthTag(i);try{return Buffer.concat([o.update(s),o.final()])}catch{throw new P("Decryption failed: Invalid ciphertext or authentication tag")}}serializePrivateKey(e){return e}deserializePrivateKey(e){return e}},T=ne.getInstance()});var ge=D(()=>{"use strict";xt()});function A(){let n=process.env.ENVIRONMENT;return n==="development"||n==="production"?n:"production"}function Ae(n){let e=n||A();return Te={...j[e],aws:{...j[e].aws,region:process.env.AWS_REGION||j[e].aws.region,appsyncUrl:process.env.APPSYNC_URL||j[e].aws.appsyncUrl,cognitoUserPoolId:process.env.COGNITO_USER_POOL_ID||j[e].aws.cognitoUserPoolId,cognitoClientId:process.env.COGNITO_CLIENT_ID||j[e].aws.cognitoClientId,cognitoDomain:process.env.COGNITO_DOMAIN||j[e].aws.cognitoDomain}},Ct=!0,Te}function k(){return(!Ct||!Te)&&Ae(),Te}var ye,fe,j,Te,Ct,Dt=D(()=>{"use strict";ye=v(require("os")),fe=v(require("path")),j={development:{environment:"development",aws:{region:"us-east-1",appsyncUrl:"https://api-dev.codevibe.quantiya.ai/graphql",cognitoUserPoolId:"us-east-1_yVwWDPvvJ",cognitoClientId:"e9r5apv6v5uui3l928r2ris0r",cognitoDomain:"codevibe-development.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:fe.default.join(ye.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:fe.default.join(ye.default.homedir(),".gemini","tmp")}},production:{environment:"production",aws:{region:"us-east-1",appsyncUrl:"https://api.codevibe.quantiya.ai/graphql",cognitoUserPoolId:"us-east-1_mNRO0j5og",cognitoClientId:"5p04dbc9ojptc5r8n7605fg78f",cognitoDomain:"codevibe-production.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:fe.default.join(ye.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:fe.default.join(ye.default.homedir(),".gemini","tmp")}}},Te=null,Ct=!1});var ie=D(()=>{"use strict";Dt()});var _e,Rt,N,Ve,Sr,G,g,Kt=D(()=>{"use strict";_e=v(require("os")),Rt=require("uuid");_t();ge();ie();q();N=class extends Error{constructor(e){super(e),this.name="KeychainError"}},Ve="device-identity",Sr="tokens-",G=class n{constructor(){this.deviceIdentity=null;this.sessionKeyCache=new Map;this.isRegistered=!1;this._serviceName=null}get serviceName(){return this._serviceName||(this._serviceName=k().keychain.serviceName),this._serviceName}static getInstance(){return n.instance||(n.instance=new n),n.instance}async getDeviceIdentity(){if(this.deviceIdentity)return this.deviceIdentity;let e=await Be(this.serviceName,Ve);return e?(this.deviceIdentity=JSON.parse(e),c.info(`[KeychainManager] Loaded device identity: ${this.deviceIdentity.deviceId}`),this.deviceIdentity):null}async setDeviceIdentity(e){try{await Fe(this.serviceName,Ve,JSON.stringify(e)),this.deviceIdentity=e,c.info(`[KeychainManager] Saved device identity: ${e.deviceId}`)}catch(t){throw c.error(`[KeychainManager] Failed to save device identity: ${t}`),new N(`Failed to save device identity: ${t}`)}}async getOrCreateDeviceIdentity(){let e=await this.getDeviceIdentity();if(e)return e;let t=T.generateKeyPair();return e={deviceId:(0,Rt.v4)().toUpperCase(),privateKey:t.privateKey,publicKey:t.publicKey,createdAt:new Date().toISOString()},await this.setDeviceIdentity(e),c.info(`[KeychainManager] Generated new device identity: ${e.deviceId}`),e}async getDeviceId(){return(await this.getOrCreateDeviceIdentity()).deviceId}async getDevicePublicKey(){return(await this.getOrCreateDeviceIdentity()).publicKey}async getDevicePrivateKey(){return(await this.getOrCreateDeviceIdentity()).privateKey}async hasDeviceIdentity(){return await this.getDeviceIdentity()!==null}async deleteDeviceIdentity(){try{await He(this.serviceName,Ve),this.deviceIdentity=null,this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Deleted device identity")}catch(e){throw c.error(`[KeychainManager] Failed to delete device identity: ${e}`),new N(`Failed to delete device identity: ${e}`)}}getTokenAccount(e){return`${Sr}${e}`}async getTokens(e="production"){let t=await Be(this.serviceName,this.getTokenAccount(e));if(!t)return null;let r=JSON.parse(t);return c.debug(`[KeychainManager] Loaded tokens for ${e}`),r}async setTokens(e,t="production"){try{await Fe(this.serviceName,this.getTokenAccount(t),JSON.stringify(e)),c.info(`[KeychainManager] Saved tokens for ${t}`,{userId:e.userId,email:e.email})}catch(r){throw c.error(`[KeychainManager] Failed to save tokens: ${r}`),new N(`Failed to save tokens: ${r}`)}}async deleteTokens(e="production"){try{let t=await He(this.serviceName,this.getTokenAccount(e));return t&&c.info(`[KeychainManager] Deleted tokens for ${e}`),t}catch(t){return c.error(`[KeychainManager] Failed to delete tokens: ${t}`),!1}}isTokenExpired(e){return Date.now()>=e.expiresAt-3e5}async getSessionKey(e,t){let r=this.sessionKeyCache.get(e);if(r)return r;if(!t||t.length===0)return null;let i=await this.getDeviceId(),s=t.find(d=>d.deviceId===i);if(!s)return c.warn(`[KeychainManager] Device ${i} not found in encryptedKeys`),null;let o=await this.getDevicePrivateKey(),a=T.decryptSessionKey(s,o);return this.sessionKeyCache.set(e,a),c.info(`[KeychainManager] Decrypted and cached session key for ${e}`),a}createSessionKey(e,t){let r=T.generateSessionKey(),i=[],s=[];for(let o of e)try{let a=T.encryptSessionKey(r,o.publicKey);i.push({deviceId:o.deviceId,encryptedKey:a.encryptedKey,ephemeralPublicKey:a.ephemeralPublicKey})}catch(a){c.warn("[KeychainManager] Skipping device with invalid public key",{deviceId:o.deviceId,error:a instanceof Error?a.message:String(a)}),s.push(o.deviceId);try{t?.onDeviceSkipped?.(s.length)}catch{}}if(i.length===0)throw new P(`Failed to encrypt session key for any of ${e.length} devices`);return c.info("[KeychainManager] Created session key",{encryptedCount:i.length,skippedCount:s.length,totalCount:e.length}),{sessionKey:r,encryptedKeys:i,skippedDeviceIds:s}}cacheSessionKey(e,t){this.sessionKeyCache.set(e,t)}getCachedSessionKey(e){return this.sessionKeyCache.get(e)??null}getCachedSessionIds(){return Array.from(this.sessionKeyCache.keys())}clearSessionKey(e){this.sessionKeyCache.delete(e)}clearAllSessionKeys(){this.sessionKeyCache.clear()}getIsRegistered(){return this.isRegistered}setIsRegistered(e){this.isRegistered=e}getDeviceName(){return _e.hostname()||"CLI Client"}getDevicePlatform(){let e=_e.platform();return e==="darwin"?"MACOS":e==="linux"?"LINUX":e==="win32"?"WINDOWS":"CLI"}async clearAllData(){await this.deleteDeviceIdentity(),await this.deleteTokens("development"),await this.deleteTokens("production"),this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Cleared all data")}},g=G.getInstance()});var Ot={};st(Ot,{KeychainError:()=>N,KeychainManager:()=>G,keychainManager:()=>g});var U=D(()=>{"use strict";Kt()});var qr={};st(qr,{AgentType:()=>Bt,AppSyncClient:()=>me,AuthService:()=>ce,CryptoError:()=>P,CryptoService:()=>ne,DeliveryStatus:()=>Pt,ENCRYPTION_VERSION:()=>qe,EventSource:()=>Ge,EventType:()=>je,KeychainError:()=>N,KeychainManager:()=>G,Logger:()=>H,PORT_RANGE_SIZE:()=>ae,PRIMARY_PORT:()=>oe,SessionStatus:()=>xe,_resetPrepareEventTimestampForTesting:()=>rt,authService:()=>$,bindOAuthServer:()=>ve,createLogger:()=>Oe,cryptoService:()=>T,errorWasBeaconed:()=>pe,fireAuthCompletedBeacon:()=>le,fireAuthFailedBeacon:()=>I,getConfig:()=>k,getEnvironment:()=>A,getErrorReason:()=>Ue,keychainManager:()=>g,loadConfig:()=>Ae,logger:()=>c,markErrorBeaconed:()=>_,mutations:()=>C,normalizeSnapshot:()=>Xe,parseInteractivePrompt:()=>Gt,prepareEventTimestamp:()=>tt,prepareSessionEncryption:()=>De,queries:()=>M,registerDeviceEncryptionKey:()=>Se,rekeySessionForNewDevices:()=>Z,resumeOrCreateSession:()=>Qe,runAuthCli:()=>Ce,startDeviceKeyWatcher:()=>et,subscriptions:()=>J});module.exports=rr(qr);U();ge();var Y=v(require("ws")),X=require("uuid");ie();q();var je=(a=>(a.USER_PROMPT="USER_PROMPT",a.ASSISTANT_RESPONSE="ASSISTANT_RESPONSE",a.TOOL_USE="TOOL_USE",a.NOTIFICATION="NOTIFICATION",a.INTERACTIVE_PROMPT="INTERACTIVE_PROMPT",a.PROMPT_RESPONSE="PROMPT_RESPONSE",a.REASONING="REASONING",a))(je||{}),Ge=(t=>(t.DESKTOP="DESKTOP",t.MOBILE="MOBILE",t))(Ge||{}),Pt=(r=>(r.SENT="SENT",r.DELIVERED="DELIVERED",r.EXECUTED="EXECUTED",r))(Pt||{});var br=Number(process.env.CODEVIBE_THROTTLE_WINDOW_MS)||1e4,Nt=Number(process.env.CODEVIBE_THROTTLE_EVENTS_PER_WINDOW)||50,kr=Number(process.env.CODEVIBE_THROTTLE_TOOL_USE_KEEP_EVERY)||10,wr=process.env.CODEVIBE_THROTTLE_DISABLED==="1",Ut=Nt,Er=new Set(["USER_PROMPT","ASSISTANT_RESPONSE","INTERACTIVE_PROMPT","PROMPT_RESPONSE","NOTIFICATION"]),z=new Map,Ir=2e3;function $t(n,e,t){if(wr)return!1;let r=z.get(n);if(!r||t-r.windowStartMs>=br||t<r.windowStartMs){if(z.delete(n),z.size>=Ir){let i=z.keys().next().value;i!==void 0&&z.delete(i)}r={windowStartMs:t,count:0,toolUseAboveThreshold:0,suppressed:0,logged:!1},z.set(n,r)}if(r.count+=1,r.count<=Nt||Er.has(e))return!1;if(e==="REASONING")return r.suppressed+=1,!0;if(e==="TOOL_USE"){r.toolUseAboveThreshold+=1;let i=r.toolUseAboveThreshold%kr===0;return i||(r.suppressed+=1),!i}return!1}function Lt(n){let e=z.get(n);if(e&&!e.logged)return e.logged=!0,e}U();var Mt=v(require("dns")),Wt=v(require("fs"));if(Tr())try{Mt.setDefaultResultOrder("ipv4first")}catch{}function Tr(){if(process.platform!=="linux")return!1;try{let n=Wt.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(n)}catch{return!1}}async function se(n,e,t){try{return await fetch(n,e)}catch(r){let i=r?.cause?.code,s=r?.cause?.message,o=i||s||r?.message||"unknown",a=Ar(i),d=t?`${t}: `:"",l=`Node ${process.version} on ${process.platform}`,y=[`${d}Cannot reach ${n}`,` Underlying error: ${o}`];a&&y.push(` Suggested fix: ${a}`),y.push(` Platform: ${l}`);let p=new Error(y.join(`
|
|
8
|
+
`));throw p.cause=r,p}}function Ar(n){if(!n)return null;switch(n){case"ENOTFOUND":case"EAI_AGAIN":return'DNS resolution failed. On WSL Ubuntu, check /etc/resolv.conf, or try running with NODE_OPTIONS="--dns-result-order=ipv4first".';case"ETIMEDOUT":case"ECONNREFUSED":case"ECONNRESET":case"EHOSTUNREACH":case"ENETUNREACH":return`Network unreachable. On WSL Ubuntu, try NODE_OPTIONS="--dns-result-order=ipv4first" (WSL's IPv6 is often broken). If behind a corporate proxy, set HTTPS_PROXY.`;case"CERT_HAS_EXPIRED":case"CERT_NOT_YET_VALID":return"TLS certificate time error \u2014 likely system clock drift. On WSL, run `sudo hwclock -s`, or shut down WSL from PowerShell with `wsl --shutdown` and restart.";case"UNABLE_TO_GET_ISSUER_CERT_LOCALLY":case"SELF_SIGNED_CERT_IN_CHAIN":case"UNABLE_TO_VERIFY_LEAF_SIGNATURE":case"DEPTH_ZERO_SELF_SIGNED_CERT":return"Corporate HTTPS proxy detected \u2014 the TLS cert is not trusted by Node. Set NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem, or configure HTTPS_PROXY if a proxy is required.";default:return null}}var M={getSession:`
|
|
7
9
|
query GetSession($sessionId: ID!) {
|
|
8
10
|
getSession(sessionId: $sessionId) {
|
|
9
11
|
sessionId
|
|
@@ -78,7 +80,7 @@ ${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,qt)}`:o+=` ${r}`),o}log
|
|
|
78
80
|
nextToken
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
|
-
`},
|
|
83
|
+
`},C={createSession:`
|
|
82
84
|
mutation CreateSession($input: CreateSessionInput!) {
|
|
83
85
|
createSession(input: $input) {
|
|
84
86
|
sessionId
|
|
@@ -202,7 +204,7 @@ ${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,qt)}`:o+=` ${r}`),o}log
|
|
|
202
204
|
updatedAt
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
|
-
`};var ft=(a=>(a.USER_PROMPT="USER_PROMPT",a.ASSISTANT_RESPONSE="ASSISTANT_RESPONSE",a.TOOL_USE="TOOL_USE",a.NOTIFICATION="NOTIFICATION",a.INTERACTIVE_PROMPT="INTERACTIVE_PROMPT",a.PROMPT_RESPONSE="PROMPT_RESPONSE",a.REASONING="REASONING",a))(ft||{}),Me=(t=>(t.DESKTOP="DESKTOP",t.MOBILE="MOBILE",t))(Me||{}),vt=(r=>(r.SENT="SENT",r.DELIVERED="DELIVERED",r.EXECUTED="EXECUTED",r))(vt||{});var be=(r=>(r.ACTIVE="ACTIVE",r.INACTIVE="INACTIVE",r.PAUSED="PAUSED",r))(be||{}),St=(i=>(i.CLAUDE="CLAUDE",i.GEMINI="GEMINI",i.CODEX="CODEX",i.ANTIGRAVITY="ANTIGRAVITY",i))(St||{});var k={urgentMaxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4,backoffMultiplier:2,persistentDelayMs:300*1e3},le=class n{constructor(){this.authenticated=!1;this.currentUserId=null;this.currentEmail=null;this.tokens=null;this.activeSubscriptions=new Map;this.pendingRefresh=null;this.lastRefreshFailureAt=null;this.deviceKeyWatcher=null;this.sessionUpdateWatchers=new Map;this.statusWriteChains=new Map;this.heartbeatTimers=new Map;this.environment=A(),c.info("[AppSyncClient] Initialized",{environment:this.environment})}static{this.REFRESH_BACKOFF_MS=3e4}getCurrentUserId(){if(!this.currentUserId)throw new Error("Not authenticated. Call authenticateWithStoredTokens() first.");return this.currentUserId}getCurrentUserEmail(){return this.currentEmail}async authenticateWithStoredTokens(){try{let e=await g.getTokens(this.environment);if(!e)return c.debug("[AppSyncClient] No stored tokens found"),!1;if(c.info("[AppSyncClient] Found stored OAuth tokens",{userId:e.userId,email:e.email,expired:g.isTokenExpired(e)}),g.isTokenExpired(e)){if(c.info("[AppSyncClient] Tokens expired, attempting refresh..."),!await this.refreshTokens(e))return c.warn("[AppSyncClient] Token refresh failed"),!1}else this.tokens=e;return this.currentUserId=this.tokens.userId,this.currentEmail=this.tokens.email,this.authenticated=!0,c.info("[AppSyncClient] Authenticated successfully",{userId:this.currentUserId,email:this.currentEmail}),!0}catch(e){return c.error("[AppSyncClient] Authentication failed:",e),!1}}async refreshTokens(e){if(this.pendingRefresh)return this.pendingRefresh;if(this.lastRefreshFailureAt!==null&&Date.now()-this.lastRefreshFailureAt<n.REFRESH_BACKOFF_MS)return!1;this.pendingRefresh=this.performRefresh(e);try{return await this.pendingRefresh}finally{this.pendingRefresh=null}}async performRefresh(e){let t=await this.callCognitoRefresh(e.refreshToken);if(t!==null)return this.applyRefreshedTokens(e,t);let r=null;try{r=await g.getTokens(this.environment)}catch(i){c.warn("[AppSyncClient] Failed to re-read tokens from storage during refresh recovery",{error:i instanceof Error?i.message:String(i)})}if(r&&r.refreshToken&&r.refreshToken!==e.refreshToken){c.info("[AppSyncClient] In-memory refresh token rejected; retrying with storage-backed token (likely out-of-band re-auth)");let i=await this.callCognitoRefresh(r.refreshToken);if(i!==null)return this.applyRefreshedTokens(r,i)}return this.lastRefreshFailureAt=Date.now(),!1}async callCognitoRefresh(e){try{let t=w(),r=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),s=await te(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");return s.ok?await s.json():(c.error("[AppSyncClient] Token refresh failed",{status:s.status}),null)}catch(t){return c.error("[AppSyncClient] Token refresh error:",t),null}}async applyRefreshedTokens(e,t){let r={...e,accessToken:t.access_token,idToken:t.id_token,expiresAt:Date.now()+t.expires_in*1e3};this.tokens=r,this.lastRefreshFailureAt=null;try{await g.setTokens(r,this.environment),c.info("[AppSyncClient] Tokens refreshed",{expiresAt:new Date(r.expiresAt).toISOString()})}catch(i){c.warn("[AppSyncClient] Tokens refreshed but persistence failed; daemon keeps using fresh tokens in memory. A restart while persistence is still broken would lose them.",{error:i instanceof Error?i.message:String(i),expiresAt:new Date(r.expiresAt).toISOString()})}return!0}isAuthenticated(){return this.authenticated}signOut(){this.authenticated=!1,this.tokens=null,this.currentUserId=null,this.currentEmail=null,this.cleanupSubscriptions(),c.info("[AppSyncClient] Signed out")}async graphqlRequest(e,t,r=!1){let i=w();if(!this.tokens?.idToken)throw new Error('Not authenticated. Run "codevibe login" first.');let s={"Content-Type":"application/json",Authorization:this.tokens.idToken},o=await te(i.aws.appsyncUrl,{method:"POST",headers:s,body:JSON.stringify({query:e,variables:t})},"AppSync GraphQL request"),a=await o.json();if(o.status===401&&!r&&this.tokens){if(c.info("[AppSyncClient] 401 Unauthorized, refreshing token..."),await this.refreshTokens(this.tokens))return this.graphqlRequest(e,t,!0);throw new Error("Token expired and refresh failed")}if(!o.ok)throw new Error(`GraphQL request failed: ${o.status}`);if(a.errors?.length)throw new Error(`GraphQL error: ${a.errors[0].message}`);return a}async createSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},r=await this.graphqlRequest(x.createSession,{input:t});return c.info("[AppSyncClient] Session created",{sessionId:r.data.createSession.sessionId}),r.data.createSession}async updateSession(e){if(e.status===void 0)return this.doUpdateSession(e);let r=(this.statusWriteChains.get(e.sessionId)??Promise.resolve()).catch(()=>{}).then(()=>this.doUpdateSession(e));return this.statusWriteChains.set(e.sessionId,r.catch(()=>{})),r}async doUpdateSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},r=await this.graphqlRequest(x.updateSession,{input:t});return c.debug("[AppSyncClient] Session updated",{sessionId:r.data.updateSession.sessionId}),r.data.updateSession}async getSession(e){return(await this.graphqlRequest(U.getSession,{sessionId:e})).data.getSession}async createEvent(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},r=await this.graphqlRequest(x.createEvent,{input:t});return c.debug("[AppSyncClient] Event created",{eventId:r.data.createEvent.eventId,type:r.data.createEvent.type}),r.data.createEvent}async updateEventStatus(e){return(await this.graphqlRequest(x.updateEventStatus,{input:e})).data.updateEventStatus}async listEvents(e,t,r){return(await this.graphqlRequest(U.listEvents,{sessionId:e,source:t,limit:r})).data.listEvents.items}async listSessions(e=100){if(!this.currentUserId)throw new Error("Not authenticated");let t=[],r=null;do{let s=(await this.graphqlRequest(U.listSessions,{userId:this.currentUserId,limit:e,nextToken:r})).data?.listSessions;s?.items&&t.push(...s.items),r=s?.nextToken??null}while(r);return t}async sweepOrphanSessions(e){let t=e.staleThresholdMs??9e5,r=new Set(e.excludeSessionIds??[]),i=Date.now(),s;try{s=await this.listSessions()}catch(a){return c.warn("[AppSyncClient] OrphanSweep: listSessions failed, skipping sweep",{agentType:e.agentType,error:a instanceof Error?a.message:String(a)}),0}let o=0;for(let a of s){if(a.agentType!==e.agentType||a.status!=="ACTIVE"||r.has(a.sessionId)||!a.lastHeartbeatAt)continue;let d=i-new Date(a.lastHeartbeatAt).getTime();if(!(d<t)){c.warn("[AppSyncClient] OrphanSweep: marking stale session INACTIVE",{sessionId:a.sessionId,agentType:a.agentType,lastHeartbeatAt:a.lastHeartbeatAt,heartbeatAgeMinutes:Math.round(d/6e4)});try{await this.updateSession({sessionId:a.sessionId,status:"INACTIVE"}),o++}catch(l){c.warn("[AppSyncClient] OrphanSweep: updateSession failed, leaving row as-is",{sessionId:a.sessionId,error:l instanceof Error?l.message:String(l)})}}}return o>0&&c.info("[AppSyncClient] OrphanSweep complete",{agentType:e.agentType,swept:o}),o}async listUserDeviceKeys(){return(await this.graphqlRequest(U.listUserDeviceKeys,{})).data.listUserDeviceKeys||[]}async registerDeviceKey(e,t,r,i){let s={deviceId:e,publicKey:t,platform:r,deviceName:i};await this.graphqlRequest(x.registerDeviceKey,{input:s}),c.info("[AppSyncClient] Device key registered",{deviceId:e,platform:r})}async grantSessionKey(e){await this.graphqlRequest(x.grantSessionKey,{input:e}),c.info("[AppSyncClient] Session key granted",{sessionId:e.sessionId,deviceId:e.deviceId})}async getAttachmentDownloadUrl(e){return(await this.graphqlRequest(x.getAttachmentDownloadUrl,{s3Key:e})).data.getAttachmentDownloadUrl}subscribeToEvents(e,t,r){c.info("[AppSyncClient] Subscribing to events",{sessionId:e});let i=this.activeSubscriptions.get(e);i&&(this.cleanupSubscriptionState(i),this.activeSubscriptions.delete(e));let s={ws:null,subscriptionId:(0,z.v4)(),sessionId:e,onEvent:t,onError:r,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.activeSubscriptions.set(e,s),this.createSubscription(s),()=>{this.cleanupSubscriptionState(s),this.activeSubscriptions.delete(e)}}buildRealtimeUrl(){let e=w(),t=new URL(e.aws.appsyncUrl),i=/\.appsync-api\.[^.]+\.amazonaws\.com$/.test(t.host)?e.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"):`wss://${t.host}/graphql/realtime`,s={host:t.host};this.tokens?.idToken&&(s.Authorization=this.tokens.idToken);let o=Buffer.from(JSON.stringify(s)).toString("base64"),a=Buffer.from(JSON.stringify({})).toString("base64");return`${i}?header=${o}&payload=${a}`}createSubscription(e){let{sessionId:t,subscriptionId:r,onEvent:i,onError:s}=e;try{let o=this.buildRealtimeUrl(),a=new j.default(o,["graphql-ws"]);a.on("open",()=>{c.info("[AppSyncClient] WebSocket connected",{sessionId:t}),a.send(JSON.stringify({type:"connection_init"}))}),a.on("message",d=>{try{let l=JSON.parse(d.toString());switch(l.type){case"connection_ack":this.sendSubscriptionStart(a,e);break;case"start_ack":if(e.destroyed)break;c.info("[AppSyncClient] Subscription started",{sessionId:t});let y=e.reconnectAttempts>0;e.isReconnecting=!1,e.reconnectAttempts=0,this.startHeartbeat(t),y&&this.updateSession({sessionId:t,status:"ACTIVE"}).then(()=>c.info("[AppSyncClient] Re-asserted session ACTIVE after reconnect",{sessionId:t})).catch(m=>c.warn("[AppSyncClient] Re-assert ACTIVE after reconnect failed",{sessionId:t,error:m instanceof Error?m.message:String(m)}));break;case"data":this.resetKeepAliveTimer(e);let p=l.payload?.data?.onEventCreated;p&&p.source==="MOBILE"&&i(p);break;case"ka":this.resetKeepAliveTimer(e);break;case"error":let h=l.payload?.errors?.[0]?.message||"Unknown error";this.handleSubscriptionError(e,new Error(h));break}}catch(l){c.error("[AppSyncClient] Failed to parse message",{error:l})}}),a.on("error",d=>{c.error("[AppSyncClient] WebSocket error",{sessionId:t,error:d.message}),this.handleSubscriptionError(e,d)}),a.on("close",(d,l)=>{c.info("[AppSyncClient] WebSocket closed",{sessionId:t,code:d}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.activeSubscriptions.get(t)===e&&this.handleSubscriptionError(e,new Error(`WebSocket closed: ${d}`))}),e.ws=a,this.resetKeepAliveTimer(e)}catch(o){this.handleSubscriptionError(e,o)}}sendSubscriptionStart(e,t){let r=w(),{sessionId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onEventCreated,variables:{sessionId:i}}),extensions:{authorization:o}}}))}resetKeepAliveTimer(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSubscriptionError(e,new Error("Keep-alive timeout"))},300*1e3)}handleSubscriptionError(e,t){let{sessionId:r,onError:i}=e;if(e.isReconnecting||!this.activeSubscriptions.has(r))return;e.isReconnecting=!0,e.reconnectAttempts++,this.stopHeartbeat(r);let s=e.reconnectAttempts<=k.urgentMaxAttempts,o;if(s?o=Math.min(k.baseDelayMs*Math.pow(k.backoffMultiplier,e.reconnectAttempts-1),k.maxDelayMs):(o=k.persistentDelayMs,e.reconnectAttempts===k.urgentMaxAttempts+1&&c.info("[AppSyncClient] Switching to persistent reconnect (every 5min)",{sessionId:r})),c.info("[AppSyncClient] Scheduling reconnect",{sessionId:r,attempt:e.reconnectAttempts,phase:s?"urgent":"persistent",delayMs:o}),e.ws){try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,e.destroyed||this.activeSubscriptions.get(r)!==e){c.info("[AppSyncClient] Reconnect skipped \u2014 state is no longer canonical",{sessionId:r});return}try{let a=await g.getTokens(this.environment);a&&(g.isTokenExpired(a)?await this.refreshTokens(a)&&c.info("[AppSyncClient] Tokens refreshed before reconnect",{sessionId:r}):this.tokens=a)}catch{c.warn("[AppSyncClient] Token refresh failed before reconnect, using existing tokens",{sessionId:r})}if(e.destroyed||this.activeSubscriptions.get(r)!==e){c.info("[AppSyncClient] Reconnect skipped after token refresh \u2014 state no longer canonical",{sessionId:r});return}e.subscriptionId=(0,z.v4)(),this.createSubscription(e)},o)}cleanupSubscriptionState(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===j.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}}subscribeToDeviceKeyRegistered(e,t,r,i){c.info("[AppSyncClient] Subscribing to device key registrations",{userId:e}),this.deviceKeyWatcher&&this.stopDeviceKeyWatcherInternal();let s={userId:e,subscriptionId:(0,z.v4)(),ws:null,onNewDevice:t,onReconnect:r,onError:i,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.deviceKeyWatcher=s,this.createDeviceKeyWatcherConnection(s),()=>{this.stopDeviceKeyWatcherInternal()}}stopDeviceKeyWatcher(){this.stopDeviceKeyWatcherInternal()}stopDeviceKeyWatcherInternal(){let e=this.deviceKeyWatcher;if(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===j.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}this.deviceKeyWatcher=null,c.info("[AppSyncClient] Device key watcher stopped")}}createDeviceKeyWatcherConnection(e){try{let t=this.buildRealtimeUrl(),r=new j.default(t,["graphql-ws"]);r.on("open",()=>{c.info("[AppSyncClient] Device key watcher WebSocket connected",{userId:e.userId}),r.send(JSON.stringify({type:"connection_init"}))}),r.on("message",i=>{try{let s=JSON.parse(i.toString());switch(s.type){case"connection_ack":this.sendDeviceKeyWatcherStart(r,e);break;case"start_ack":c.info("[AppSyncClient] Device key watcher subscription started",{userId:e.userId});let o=e.isReconnecting;if(e.isReconnecting=!1,e.reconnectAttempts=0,o&&e.onReconnect)try{e.onReconnect()}catch(l){c.warn("[AppSyncClient] Device key watcher onReconnect handler threw",{error:l})}break;case"data":this.resetDeviceKeyWatcherKeepAlive(e);let a=s.payload?.data?.onDeviceKeyRegistered;if(a){c.info("[AppSyncClient] Device key registration observed",{userId:e.userId,newDeviceId:a.deviceId,platform:a.platform});try{e.onNewDevice(a)}catch(l){c.warn("[AppSyncClient] Device key watcher onNewDevice handler threw",{error:l})}}break;case"ka":this.resetDeviceKeyWatcherKeepAlive(e);break;case"error":let d=s.payload?.errors?.[0]?.message||"Unknown error";this.handleDeviceKeyWatcherError(e,new Error(d));break}}catch(s){c.error("[AppSyncClient] Failed to parse device key watcher message",{error:s})}}),r.on("error",i=>{c.error("[AppSyncClient] Device key watcher WebSocket error",{userId:e.userId,error:i.message}),this.handleDeviceKeyWatcherError(e,i)}),r.on("close",i=>{c.info("[AppSyncClient] Device key watcher WebSocket closed",{userId:e.userId,code:i}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.deviceKeyWatcher===e&&this.handleDeviceKeyWatcherError(e,new Error(`WebSocket closed: ${i}`))}),e.ws=r,this.resetDeviceKeyWatcherKeepAlive(e)}catch(t){this.handleDeviceKeyWatcherError(e,t)}}sendDeviceKeyWatcherStart(e,t){let r=w(),{userId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onDeviceKeyRegistered,variables:{userId:i}}),extensions:{authorization:o}}}))}resetDeviceKeyWatcherKeepAlive(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleDeviceKeyWatcherError(e,new Error("Device key watcher keep-alive timeout"))},300*1e3)}handleDeviceKeyWatcherError(e,t){if(e.isReconnecting||e.destroyed||this.deviceKeyWatcher!==e)return;if(e.isReconnecting=!0,e.reconnectAttempts++,e.onError)try{e.onError(t)}catch{}if(e.ws){try{e.ws.removeAllListeners()}catch{}try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0);let i=e.reconnectAttempts<=k.urgentMaxAttempts?Math.min(k.baseDelayMs*Math.pow(k.backoffMultiplier,e.reconnectAttempts-1),k.maxDelayMs):k.persistentDelayMs;c.warn("[AppSyncClient] Device key watcher reconnect scheduled",{userId:e.userId,attempts:e.reconnectAttempts,delayMs:i,error:t.message}),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,e.destroyed||this.deviceKeyWatcher!==e){c.info("[AppSyncClient] Device key watcher reconnect skipped \u2014 state no longer canonical",{userId:e.userId});return}try{let s=await g.getTokens(this.environment);s&&(g.isTokenExpired(s)?await this.refreshTokens(s)&&c.info("[AppSyncClient] Tokens refreshed before device key watcher reconnect",{userId:e.userId}):this.tokens=s)}catch{c.warn("[AppSyncClient] Token refresh failed before device key watcher reconnect, using existing tokens",{userId:e.userId})}e.destroyed||this.deviceKeyWatcher!==e||(e.subscriptionId=(0,z.v4)(),this.createDeviceKeyWatcherConnection(e))},i)}watchForMobileEnd(e,t){c.info("[AppSyncClient] Starting mobile-end watcher",{sessionId:e});let r=this.sessionUpdateWatchers.get(e);r&&(c.info("[AppSyncClient] Replacing existing mobile-end watcher",{sessionId:e}),this.cleanupSessionUpdateWatcherState(r),this.sessionUpdateWatchers.delete(e));let i={sessionId:e,subscriptionId:(0,z.v4)(),ws:null,onMobileEndRequested:t,priorStatus:"ACTIVE",firedOnce:!1,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.sessionUpdateWatchers.set(e,i),this.createSessionUpdateWatcherConnection(i),{stop:()=>{this.sessionUpdateWatchers.get(e)===i&&(this.cleanupSessionUpdateWatcherState(i),this.sessionUpdateWatchers.delete(e),c.info("[AppSyncClient] Mobile-end watcher stopped",{sessionId:e}))}}}createSessionUpdateWatcherConnection(e){try{let t=this.buildRealtimeUrl(),r=new j.default(t,["graphql-ws"]);r.on("open",()=>{c.info("[AppSyncClient] Mobile-end watcher WebSocket connected",{sessionId:e.sessionId}),r.send(JSON.stringify({type:"connection_init"}))}),r.on("message",i=>{try{let s=JSON.parse(i.toString());switch(s.type){case"connection_ack":this.sendSessionUpdateWatcherStart(r,e);break;case"start_ack":c.info("[AppSyncClient] Mobile-end watcher subscription started",{sessionId:e.sessionId}),e.isReconnecting=!1,e.reconnectAttempts=0;break;case"data":this.resetSessionUpdateWatcherKeepAlive(e),this.handleSessionUpdatePayload(e,s.payload);break;case"ka":this.resetSessionUpdateWatcherKeepAlive(e);break;case"error":let o=s.payload?.errors?.[0]?.message||"Unknown error";this.handleSessionUpdateWatcherError(e,new Error(o));break}}catch(s){c.error("[AppSyncClient] Failed to parse mobile-end watcher message",{error:s})}}),r.on("error",i=>{c.error("[AppSyncClient] Mobile-end watcher WebSocket error",{sessionId:e.sessionId,error:i.message}),this.handleSessionUpdateWatcherError(e,i)}),r.on("close",i=>{c.info("[AppSyncClient] Mobile-end watcher WebSocket closed",{sessionId:e.sessionId,code:i}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.sessionUpdateWatchers.get(e.sessionId)===e&&this.handleSessionUpdateWatcherError(e,new Error(`WebSocket closed: ${i}`))}),e.ws=r,this.resetSessionUpdateWatcherKeepAlive(e)}catch(t){this.handleSessionUpdateWatcherError(e,t)}}handleSessionUpdatePayload(e,t){let r=t?.data?.onSessionUpdated;if(!r){c.warn("[AppSyncClient] Mobile-end watcher received malformed payload",{sessionId:e.sessionId});return}if(e.firedOnce)return;let i=r.status;if(i==null){c.debug("[AppSyncClient] Mobile-end watcher skipped non-status payload",{sessionId:e.sessionId});return}if(e.priorStatus==="ACTIVE"&&i==="INACTIVE"){e.firedOnce=!0,e.priorStatus="INACTIVE",c.info("[AppSyncClient] Mobile end requested for session",{sessionId:e.sessionId}),Promise.resolve().then(()=>e.onMobileEndRequested()).catch(s=>{c.warn("[AppSyncClient] Mobile-end callback threw",{sessionId:e.sessionId,error:s})});return}e.priorStatus=i}sendSessionUpdateWatcherStart(e,t){let r=w(),{sessionId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onSessionUpdated,variables:{sessionId:i}}),extensions:{authorization:o}}}))}resetSessionUpdateWatcherKeepAlive(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSessionUpdateWatcherError(e,new Error("Mobile-end watcher keep-alive timeout"))},300*1e3)}handleSessionUpdateWatcherError(e,t){if(e.isReconnecting||e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e)return;if(e.isReconnecting=!0,e.reconnectAttempts++,e.ws){try{e.ws.removeAllListeners()}catch{}try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0);let i=e.reconnectAttempts<=k.urgentMaxAttempts?Math.min(k.baseDelayMs*Math.pow(k.backoffMultiplier,e.reconnectAttempts-1),k.maxDelayMs):k.persistentDelayMs;c.warn("[AppSyncClient] Mobile-end watcher reconnect scheduled",{sessionId:e.sessionId,attempts:e.reconnectAttempts,delayMs:i,error:t.message}),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,!(e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e)){try{let s=await g.getTokens(this.environment);s&&(g.isTokenExpired(s)?await this.refreshTokens(s):this.tokens=s)}catch{c.warn("[AppSyncClient] Token refresh failed before mobile-end watcher reconnect",{sessionId:e.sessionId})}e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e||(e.subscriptionId=(0,z.v4)(),this.createSessionUpdateWatcherConnection(e))}},i)}cleanupSessionUpdateWatcherState(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===j.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}}startHeartbeat(e,t=120*1e3){this.stopHeartbeat(e),this.sendHeartbeat(e);let r=setInterval(()=>{this.sendHeartbeat(e)},t);this.heartbeatTimers.set(e,r),c.info("[AppSyncClient] Heartbeat started",{sessionId:e,intervalMs:t})}stopHeartbeat(e){let t=this.heartbeatTimers.get(e);t&&(clearInterval(t),this.heartbeatTimers.delete(e),c.info("[AppSyncClient] Heartbeat stopped",{sessionId:e}))}async sendHeartbeat(e){try{await this.updateSession({sessionId:e,lastHeartbeatAt:new Date().toISOString()}),c.debug("[AppSyncClient] Heartbeat sent",{sessionId:e})}catch(t){c.warn("[AppSyncClient] Heartbeat failed",{sessionId:e,error:t})}}cleanupSubscription(e){let t=this.activeSubscriptions.get(e);t&&(this.cleanupSubscriptionState(t),this.activeSubscriptions.get(e)===t&&this.activeSubscriptions.delete(e))}cleanupSubscriptions(){this.activeSubscriptions.forEach(e=>{this.cleanupSubscriptionState(e)}),this.activeSubscriptions.clear(),this.stopDeviceKeyWatcherInternal(),this.sessionUpdateWatchers.forEach(e=>{this.cleanupSessionUpdateWatcherState(e)}),this.sessionUpdateWatchers.clear(),this.heartbeatTimers.forEach(e=>clearInterval(e)),this.heartbeatTimers.clear()}};var Dt=v(require("crypto")),_t=v(require("fs")),Rt=v(require("http")),Kt=require("child_process");ee();P();H();var We=v(require("crypto")),wt=v(require("https")),kt=v(require("os")),er="G-GS74YEQTB8",tr="lAfOF6OxRzSQ-NsLBRjhAg",rr="www.google-analytics.com",nr=`/mp/collect?measurement_id=${er}&api_secret=${tr}`,ir={port_in_use:"server_start",port_range_exhausted:"server_start",server_listen_failed:"server_start",browser_open_failed:"browser_open",login_timeout:"awaiting_callback",cognito_rejected:"awaiting_callback",state_mismatch:"awaiting_callback",no_authorization_code:"awaiting_callback",token_exchange_failed:"exchanging_code",token_exchange_network_error:"exchanging_code",keychain_write_failed:"storing_tokens",user_aborted:"unknown",unknown:"unknown"};function sr(){let n=typeof process.getuid=="function"?process.getuid():0;return We.createHash("sha256").update(`${kt.hostname()}-${n}`).digest("hex").substring(0,36)}function $(){return{platform:process.platform,source:process.env.CODEVIBE_TELEMETRY_SOURCE||"production"}}async function L(n,e){try{let t=JSON.stringify({client_id:sr(),events:[{name:n,params:e}]});await new Promise(r=>{let i=wt.request({hostname:rr,path:nr,method:"POST",headers:{"Content-Type":"application/json"}},()=>r());i.on("error",()=>r()),i.write(t),i.end(),setTimeout(r,2e3)})}catch{}}async function pe(n){await L("auth_completed",{...$(),user_id:n})}async function I(n,e){let t={...$(),reason:n,stage:e?.stage??ir[n]};if(typeof e?.httpStatus=="number"&&(t.http_status=e.httpStatus),e?.errorFragment){let{homedir:r}=await import("os"),i=e.errorFragment.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,"").replace(/\\/g,"/").replace(/[\n\r\t"]/g," ").replace(/[^\x20-\x7E]/g,"").trim(),s=[process.env.HOME,process.env.USERPROFILE,(()=>{try{return r()}catch{return}})()].filter(d=>typeof d=="string"&&d.length>0).map(d=>d.replace(/\\/g,"/"));for(let d of s){let l=d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=i.replace(new RegExp(l,"g"),"~")}i=i.replace(/\/Users\/[^/ ]+/g,"/Users/<user>").replace(/\/home\/[^/ ]+/g,"/home/<user>").replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,"<email>");let o=i.substring(0,100),a=i.substring(100,200);o&&(t.error_fragment=o),a&&(t.error_fragment_2=a)}await L("auth_failed",t)}var Be=Symbol.for("codevibe.auth.beaconed"),bt=Symbol.for("codevibe.auth.failureReason");function C(n,e){try{Object.defineProperty(n,Be,{value:!0,enumerable:!1,configurable:!0,writable:!1}),Object.defineProperty(n,bt,{value:e,enumerable:!1,configurable:!0,writable:!1})}catch{}return n}function ue(n){return!!(n&&typeof n=="object"&&n[Be])}function Fe(n){if(n&&typeof n=="object"&&n[Be]){let e=n[bt];if(typeof e=="string")return e}}function re(n){return n<=0?"0":n===1?"1":n<=5?"2-5":"6+"}function Ee(n){return We.createHash("sha256").update(n).digest("hex").slice(0,8)}async function Et(n){return L("session_encryption_device_skipped",{...$(),...n})}async function It(n){return L("session_encryption_partial_success",{...$(),...n})}async function At(n){return L("session_encryption_catch_up_grant",{...$(),...n})}async function Tt(n){return L("session_encryption_self_rekey_request",{...$(),...n})}async function Ct(n){return L("session_encryption_self_rekey_success",{...$(),...n})}async function xt(n){return L("session_encryption_self_rekey_timeout",{...$(),...n})}var ne=8080,ie=20,He="/callback";async function he(n){let e=null;for(let t=0;t<ie;t++){let r=ne+t;try{let i=await new Promise((s,o)=>{let a=Rt.createServer(n),d=y=>{a.removeListener("listening",l),o(y)},l=()=>{a.removeListener("error",d),a.on("error",y=>{c.error("[AuthService] OAuth server post-bind error",{port:r,code:y?.code,message:y?.message})}),s(a)};a.once("error",d),a.once("listening",l),a.listen(r,"localhost")});return c.info(`[AuthService] OAuth server bound on port ${r} (attempt ${t+1}/${ie})`),{server:i,port:r}}catch(i){if(e=i,i?.code==="EADDRINUSE")continue;throw i}}throw Object.assign(new Error(`All ports ${ne}-${ne+ie-1} are in use. Free at least one for OAuth callback or quit a conflicting service (common collisions: Vite, Webpack, Spring Boot, Docker exposed ports). Underlying: ${e?.message??"EADDRINUSE"}`),{code:"EADDRINUSE_ALL"})}var se=class n{constructor(){}static getInstance(){return n.instance||(n.instance=new n),n.instance}openBrowser(e){console.error(""),console.error("Opening your browser for sign-in..."),this.isRunningInWSL()?console.error("If your browser does not open, paste this URL in your Windows browser:"):console.error("If your browser does not open automatically, visit this URL:"),console.error(` ${e}`),console.error("");let t=this.getBrowserCommands();this.tryBrowserCommand(t,e,0)}getBrowserCommands(){let e=process.platform;if(e==="darwin")return[{cmd:"open",fixedArgs:[]}];if(e==="win32")return[{cmd:"cmd",fixedArgs:["/c","start",""]}];let t=[];return this.isRunningInWSL()&&(t.push({cmd:"wslview",fixedArgs:[]}),t.push({cmd:"cmd.exe",fixedArgs:["/c","start",""]}),t.push({cmd:"powershell.exe",fixedArgs:["-NoProfile","-Command","Start-Process"]})),t.push({cmd:"xdg-open",fixedArgs:[]}),t}isRunningInWSL(){if(process.platform!=="linux")return!1;try{let e=_t.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(e)}catch{return!1}}tryBrowserCommand(e,t,r){if(r>=e.length){c.debug("[AuthService] No browser-opening command succeeded. User must open the sign-in URL manually (printed to stderr above)."),console.error(""),console.error("\u26A0\uFE0F Could not open browser automatically."),this.isRunningInWSL()?console.error(" WSL detected \u2014 paste this URL in your Windows browser:"):console.error(" Please copy and paste this URL into your browser:"),console.error(` ${t}`),console.error("");return}let i=e[r],s=[...i.fixedArgs,t],o=!1,a=p=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${p}; trying next fallback`),this.tryBrowserCommand(e,t,r+1))},d=p=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${p}`))},l;try{l=(0,Kt.spawn)(i.cmd,s,{detached:!0,stdio:"ignore"})}catch(p){a(`threw synchronously: ${p?.message||p}`);return}l.on("error",p=>{a(`failed to spawn: ${p?.message||p}`)}),l.on("exit",(p,h)=>{p===0?d("exited successfully"):a(h?`terminated by signal ${h}`:`exited with code ${p}`)}),setTimeout(()=>{d("still running after 3s, assuming success")},3e3).unref(),l.unref()}generateState(){return Dt.randomBytes(32).toString("hex")}buildAuthUrl(e,t){let r=w(),i=new URLSearchParams({client_id:r.aws.cognitoClientId,response_type:"code",scope:"email openid profile",redirect_uri:t,state:e});return`https://${r.aws.cognitoDomain}/oauth2/authorize?${i.toString()}`}async exchangeCodeForTokens(e,t){let r=w(),i=`https://${r.aws.cognitoDomain}/oauth2/token`,s=new URLSearchParams({grant_type:"authorization_code",client_id:r.aws.cognitoClientId,code:e,redirect_uri:t}),o;try{o=await te(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},"Token exchange")}catch(d){throw await I("token_exchange_network_error"),C(d,"token_exchange_network_error"),d}if(!o.ok){let d=await o.text(),l=new Error(`Token exchange failed: ${o.status} ${d}`);throw await I("token_exchange_failed",{httpStatus:o.status}),C(l,"token_exchange_failed"),l}let a=await o.json();return{accessToken:a.access_token,idToken:a.id_token,refreshToken:a.refresh_token,expiresIn:a.expires_in}}decodeJwt(e){let t=e.split(".");if(t.length!==3)throw new Error("Invalid JWT");return JSON.parse(Buffer.from(t[1],"base64").toString("utf-8"))}async refreshTokens(e){let t=w(),r=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),s=await te(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");if(!s.ok)throw new Error(`Token refresh failed: ${s.status}`);let o=await s.json();return{accessToken:o.access_token,idToken:o.id_token,expiresIn:o.expires_in}}async login(){let e=await g.getTokens(A());if(e&&!g.isTokenExpired(e))return e;let t=this.generateState();return new Promise((r,i)=>{let s={},o=null,a=!1,d=!1,l=h=>{h.closeAllConnections?.()},y=h=>{if(a)return;a=!0,o&&(clearTimeout(o),o=null);let m=s.server;m?(l(m),m.close(()=>r(h))):r(h)},p=h=>{if(a)return;a=!0,o&&(clearTimeout(o),o=null);let m=s.server;m?(l(m),m.close(()=>i(h))):i(h)};(async()=>{let h;try{h=await he(async(f,S)=>{if(d||a){S.writeHead(200,{Connection:"close"}),S.end();return}let Ze=`http://localhost:${f.socket?.localPort??h.port}${He}`,ye=new URL(f.url||"",Ze);if(ye.pathname!==He){S.writeHead(404,{Connection:"close"}),S.end("Not found");return}try{let B=ye.searchParams.get("code"),Te=ye.searchParams.get("state"),X=ye.searchParams.get("error");if(X){let b=new Error(`OAuth error: ${X}`);throw await I("cognito_rejected"),C(b,"cognito_rejected"),b}if(Te!==t){let b=new Error("State mismatch");throw await I("state_mismatch"),C(b,"state_mismatch"),b}if(!B){let b=new Error("No authorization code");throw await I("no_authorization_code"),C(b,"no_authorization_code"),b}d=!0;let oe=await this.exchangeCodeForTokens(B,Ze),Qe=this.decodeJwt(oe.idToken),Ce={accessToken:oe.accessToken,idToken:oe.idToken,refreshToken:oe.refreshToken,expiresAt:Date.now()+oe.expiresIn*1e3,userId:Qe.sub,email:Qe.email||"unknown"};try{await g.setTokens(Ce,A())}catch(b){throw await I("keychain_write_failed"),C(b,"keychain_write_failed"),b}S.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),S.end(`
|
|
207
|
+
`};var xe=(r=>(r.ACTIVE="ACTIVE",r.INACTIVE="INACTIVE",r.PAUSED="PAUSED",r))(xe||{}),Bt=(i=>(i.CLAUDE="CLAUDE",i.GEMINI="GEMINI",i.CODEX="CODEX",i.ANTIGRAVITY="ANTIGRAVITY",i))(Bt||{});var E={urgentMaxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4,backoffMultiplier:2,persistentDelayMs:300*1e3},me=class n{constructor(){this.authenticated=!1;this.currentUserId=null;this.currentEmail=null;this.tokens=null;this.activeSubscriptions=new Map;this.pendingRefresh=null;this.lastRefreshFailureAt=null;this.deviceKeyWatcher=null;this.sessionUpdateWatchers=new Map;this.statusWriteChains=new Map;this.heartbeatTimers=new Map;this.environment=A(),c.info("[AppSyncClient] Initialized",{environment:this.environment})}static{this.REFRESH_BACKOFF_MS=3e4}getCurrentUserId(){if(!this.currentUserId)throw new Error("Not authenticated. Call authenticateWithStoredTokens() first.");return this.currentUserId}getCurrentUserEmail(){return this.currentEmail}async authenticateWithStoredTokens(){try{let e=await g.getTokens(this.environment);if(!e)return c.debug("[AppSyncClient] No stored tokens found"),!1;if(c.info("[AppSyncClient] Found stored OAuth tokens",{userId:e.userId,email:e.email,expired:g.isTokenExpired(e)}),g.isTokenExpired(e)){if(c.info("[AppSyncClient] Tokens expired, attempting refresh..."),!await this.refreshTokens(e))return c.warn("[AppSyncClient] Token refresh failed"),!1}else this.tokens=e;return this.currentUserId=this.tokens.userId,this.currentEmail=this.tokens.email,this.authenticated=!0,c.info("[AppSyncClient] Authenticated successfully",{userId:this.currentUserId,email:this.currentEmail}),!0}catch(e){return c.error("[AppSyncClient] Authentication failed:",e),!1}}async refreshTokens(e){if(this.pendingRefresh)return this.pendingRefresh;if(this.lastRefreshFailureAt!==null&&Date.now()-this.lastRefreshFailureAt<n.REFRESH_BACKOFF_MS)return!1;this.pendingRefresh=this.performRefresh(e);try{return await this.pendingRefresh}finally{this.pendingRefresh=null}}async performRefresh(e){let t=await this.callCognitoRefresh(e.refreshToken);if(t!==null)return this.applyRefreshedTokens(e,t);let r=null;try{r=await g.getTokens(this.environment)}catch(i){c.warn("[AppSyncClient] Failed to re-read tokens from storage during refresh recovery",{error:i instanceof Error?i.message:String(i)})}if(r&&r.refreshToken&&r.refreshToken!==e.refreshToken){c.info("[AppSyncClient] In-memory refresh token rejected; retrying with storage-backed token (likely out-of-band re-auth)");let i=await this.callCognitoRefresh(r.refreshToken);if(i!==null)return this.applyRefreshedTokens(r,i)}return this.lastRefreshFailureAt=Date.now(),!1}async callCognitoRefresh(e){try{let t=k(),r=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),s=await se(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");return s.ok?await s.json():(c.error("[AppSyncClient] Token refresh failed",{status:s.status}),null)}catch(t){return c.error("[AppSyncClient] Token refresh error:",t),null}}async applyRefreshedTokens(e,t){let r={...e,accessToken:t.access_token,idToken:t.id_token,expiresAt:Date.now()+t.expires_in*1e3};this.tokens=r,this.lastRefreshFailureAt=null;try{await g.setTokens(r,this.environment),c.info("[AppSyncClient] Tokens refreshed",{expiresAt:new Date(r.expiresAt).toISOString()})}catch(i){c.warn("[AppSyncClient] Tokens refreshed but persistence failed; daemon keeps using fresh tokens in memory. A restart while persistence is still broken would lose them.",{error:i instanceof Error?i.message:String(i),expiresAt:new Date(r.expiresAt).toISOString()})}return!0}isAuthenticated(){return this.authenticated}signOut(){this.authenticated=!1,this.tokens=null,this.currentUserId=null,this.currentEmail=null,this.cleanupSubscriptions(),c.info("[AppSyncClient] Signed out")}async graphqlRequest(e,t,r=!1){let i=k();if(!this.tokens?.idToken)throw new Error('Not authenticated. Run "codevibe login" first.');let s={"Content-Type":"application/json",Authorization:this.tokens.idToken},o=await se(i.aws.appsyncUrl,{method:"POST",headers:s,body:JSON.stringify({query:e,variables:t})},"AppSync GraphQL request"),a=await o.json();if(o.status===401&&!r&&this.tokens){if(c.info("[AppSyncClient] 401 Unauthorized, refreshing token..."),await this.refreshTokens(this.tokens))return this.graphqlRequest(e,t,!0);throw new Error("Token expired and refresh failed")}if(!o.ok)throw new Error(`GraphQL request failed: ${o.status}`);if(a.errors?.length)throw new Error(`GraphQL error: ${a.errors[0].message}`);return a}async createSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},r=await this.graphqlRequest(C.createSession,{input:t});return c.info("[AppSyncClient] Session created",{sessionId:r.data.createSession.sessionId}),r.data.createSession}async updateSession(e){if(e.status===void 0)return this.doUpdateSession(e);let r=(this.statusWriteChains.get(e.sessionId)??Promise.resolve()).catch(()=>{}).then(()=>this.doUpdateSession(e));return this.statusWriteChains.set(e.sessionId,r.catch(()=>{})),r}async doUpdateSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},r=await this.graphqlRequest(C.updateSession,{input:t});return c.debug("[AppSyncClient] Session updated",{sessionId:r.data.updateSession.sessionId}),r.data.updateSession}async getSession(e){return(await this.graphqlRequest(M.getSession,{sessionId:e})).data.getSession}async createEvent(e){let t=Date.now();if(e.sessionId&&$t(e.sessionId,e.type,t)){let s=Lt(e.sessionId);return s&&c.info("[AppSyncClient] client event throttle engaged",{sessionId:e.sessionId,type:e.type,windowCount:s.count,threshold:Ut}),{eventId:`local-throttled-${t}-${Math.random().toString(36).slice(2,11)}`,sessionId:e.sessionId,type:e.type,source:e.source,content:e.content,timestamp:e.timestamp??new Date(t).toISOString(),...e.promptId!==void 0?{promptId:e.promptId}:{},...e.metadata!==void 0?{metadata:e.metadata}:{},...e.isEncrypted!==void 0?{isEncrypted:e.isEncrypted}:{}}}let r={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},i=await this.graphqlRequest(C.createEvent,{input:r});return c.debug("[AppSyncClient] Event created",{eventId:i.data.createEvent.eventId,type:i.data.createEvent.type}),i.data.createEvent}async updateEventStatus(e){return(await this.graphqlRequest(C.updateEventStatus,{input:e})).data.updateEventStatus}async listEvents(e,t,r){return(await this.graphqlRequest(M.listEvents,{sessionId:e,source:t,limit:r})).data.listEvents.items}async listSessions(e=100){if(!this.currentUserId)throw new Error("Not authenticated");let t=[],r=null;do{let s=(await this.graphqlRequest(M.listSessions,{userId:this.currentUserId,limit:e,nextToken:r})).data?.listSessions;s?.items&&t.push(...s.items),r=s?.nextToken??null}while(r);return t}async sweepOrphanSessions(e){let t=e.staleThresholdMs??9e5,r=new Set(e.excludeSessionIds??[]),i=Date.now(),s;try{s=await this.listSessions()}catch(a){return c.warn("[AppSyncClient] OrphanSweep: listSessions failed, skipping sweep",{agentType:e.agentType,error:a instanceof Error?a.message:String(a)}),0}let o=0;for(let a of s){if(a.agentType!==e.agentType||a.status!=="ACTIVE"||r.has(a.sessionId)||!a.lastHeartbeatAt)continue;let d=i-new Date(a.lastHeartbeatAt).getTime();if(!(d<t)){c.warn("[AppSyncClient] OrphanSweep: marking stale session INACTIVE",{sessionId:a.sessionId,agentType:a.agentType,lastHeartbeatAt:a.lastHeartbeatAt,heartbeatAgeMinutes:Math.round(d/6e4)});try{await this.updateSession({sessionId:a.sessionId,status:"INACTIVE"}),o++}catch(l){c.warn("[AppSyncClient] OrphanSweep: updateSession failed, leaving row as-is",{sessionId:a.sessionId,error:l instanceof Error?l.message:String(l)})}}}return o>0&&c.info("[AppSyncClient] OrphanSweep complete",{agentType:e.agentType,swept:o}),o}async listUserDeviceKeys(){return(await this.graphqlRequest(M.listUserDeviceKeys,{})).data.listUserDeviceKeys||[]}async registerDeviceKey(e,t,r,i){let s={deviceId:e,publicKey:t,platform:r,deviceName:i};await this.graphqlRequest(C.registerDeviceKey,{input:s}),c.info("[AppSyncClient] Device key registered",{deviceId:e,platform:r})}async grantSessionKey(e){await this.graphqlRequest(C.grantSessionKey,{input:e}),c.info("[AppSyncClient] Session key granted",{sessionId:e.sessionId,deviceId:e.deviceId})}async getAttachmentDownloadUrl(e){return(await this.graphqlRequest(C.getAttachmentDownloadUrl,{s3Key:e})).data.getAttachmentDownloadUrl}subscribeToEvents(e,t,r){c.info("[AppSyncClient] Subscribing to events",{sessionId:e});let i=this.activeSubscriptions.get(e);i&&(this.cleanupSubscriptionState(i),this.activeSubscriptions.delete(e));let s={ws:null,subscriptionId:(0,X.v4)(),sessionId:e,onEvent:t,onError:r,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.activeSubscriptions.set(e,s),this.createSubscription(s),()=>{this.cleanupSubscriptionState(s),this.activeSubscriptions.delete(e)}}buildRealtimeUrl(){let e=k(),t=new URL(e.aws.appsyncUrl),i=/\.appsync-api\.[^.]+\.amazonaws\.com$/.test(t.host)?e.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"):`wss://${t.host}/graphql/realtime`,s={host:t.host};this.tokens?.idToken&&(s.Authorization=this.tokens.idToken);let o=Buffer.from(JSON.stringify(s)).toString("base64"),a=Buffer.from(JSON.stringify({})).toString("base64");return`${i}?header=${o}&payload=${a}`}createSubscription(e){let{sessionId:t,subscriptionId:r,onEvent:i,onError:s}=e;try{let o=this.buildRealtimeUrl(),a=new Y.default(o,["graphql-ws"]);a.on("open",()=>{c.info("[AppSyncClient] WebSocket connected",{sessionId:t}),a.send(JSON.stringify({type:"connection_init"}))}),a.on("message",d=>{try{let l=JSON.parse(d.toString());switch(l.type){case"connection_ack":this.sendSubscriptionStart(a,e);break;case"start_ack":if(e.destroyed)break;c.info("[AppSyncClient] Subscription started",{sessionId:t});let y=e.reconnectAttempts>0;e.isReconnecting=!1,e.reconnectAttempts=0,this.startHeartbeat(t),y&&this.updateSession({sessionId:t,status:"ACTIVE"}).then(()=>c.info("[AppSyncClient] Re-asserted session ACTIVE after reconnect",{sessionId:t})).catch(f=>c.warn("[AppSyncClient] Re-assert ACTIVE after reconnect failed",{sessionId:t,error:f instanceof Error?f.message:String(f)}));break;case"data":this.resetKeepAliveTimer(e);let p=l.payload?.data?.onEventCreated;p&&p.source==="MOBILE"&&i(p);break;case"ka":this.resetKeepAliveTimer(e);break;case"error":let h=l.payload?.errors?.[0]?.message||"Unknown error";this.handleSubscriptionError(e,new Error(h));break}}catch(l){c.error("[AppSyncClient] Failed to parse message",{error:l})}}),a.on("error",d=>{c.error("[AppSyncClient] WebSocket error",{sessionId:t,error:d.message}),this.handleSubscriptionError(e,d)}),a.on("close",(d,l)=>{c.info("[AppSyncClient] WebSocket closed",{sessionId:t,code:d}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.activeSubscriptions.get(t)===e&&this.handleSubscriptionError(e,new Error(`WebSocket closed: ${d}`))}),e.ws=a,this.resetKeepAliveTimer(e)}catch(o){this.handleSubscriptionError(e,o)}}sendSubscriptionStart(e,t){let r=k(),{sessionId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onEventCreated,variables:{sessionId:i}}),extensions:{authorization:o}}}))}resetKeepAliveTimer(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSubscriptionError(e,new Error("Keep-alive timeout"))},300*1e3)}handleSubscriptionError(e,t){let{sessionId:r,onError:i}=e;if(e.isReconnecting||!this.activeSubscriptions.has(r))return;e.isReconnecting=!0,e.reconnectAttempts++,this.stopHeartbeat(r);let s=e.reconnectAttempts<=E.urgentMaxAttempts,o;if(s?o=Math.min(E.baseDelayMs*Math.pow(E.backoffMultiplier,e.reconnectAttempts-1),E.maxDelayMs):(o=E.persistentDelayMs,e.reconnectAttempts===E.urgentMaxAttempts+1&&c.info("[AppSyncClient] Switching to persistent reconnect (every 5min)",{sessionId:r})),c.info("[AppSyncClient] Scheduling reconnect",{sessionId:r,attempt:e.reconnectAttempts,phase:s?"urgent":"persistent",delayMs:o}),e.ws){try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,e.destroyed||this.activeSubscriptions.get(r)!==e){c.info("[AppSyncClient] Reconnect skipped \u2014 state is no longer canonical",{sessionId:r});return}try{let a=await g.getTokens(this.environment);a&&(g.isTokenExpired(a)?await this.refreshTokens(a)&&c.info("[AppSyncClient] Tokens refreshed before reconnect",{sessionId:r}):this.tokens=a)}catch{c.warn("[AppSyncClient] Token refresh failed before reconnect, using existing tokens",{sessionId:r})}if(e.destroyed||this.activeSubscriptions.get(r)!==e){c.info("[AppSyncClient] Reconnect skipped after token refresh \u2014 state no longer canonical",{sessionId:r});return}e.subscriptionId=(0,X.v4)(),this.createSubscription(e)},o)}cleanupSubscriptionState(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===Y.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}}subscribeToDeviceKeyRegistered(e,t,r,i){c.info("[AppSyncClient] Subscribing to device key registrations",{userId:e}),this.deviceKeyWatcher&&this.stopDeviceKeyWatcherInternal();let s={userId:e,subscriptionId:(0,X.v4)(),ws:null,onNewDevice:t,onReconnect:r,onError:i,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.deviceKeyWatcher=s,this.createDeviceKeyWatcherConnection(s),()=>{this.stopDeviceKeyWatcherInternal()}}stopDeviceKeyWatcher(){this.stopDeviceKeyWatcherInternal()}stopDeviceKeyWatcherInternal(){let e=this.deviceKeyWatcher;if(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===Y.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}this.deviceKeyWatcher=null,c.info("[AppSyncClient] Device key watcher stopped")}}createDeviceKeyWatcherConnection(e){try{let t=this.buildRealtimeUrl(),r=new Y.default(t,["graphql-ws"]);r.on("open",()=>{c.info("[AppSyncClient] Device key watcher WebSocket connected",{userId:e.userId}),r.send(JSON.stringify({type:"connection_init"}))}),r.on("message",i=>{try{let s=JSON.parse(i.toString());switch(s.type){case"connection_ack":this.sendDeviceKeyWatcherStart(r,e);break;case"start_ack":c.info("[AppSyncClient] Device key watcher subscription started",{userId:e.userId});let o=e.isReconnecting;if(e.isReconnecting=!1,e.reconnectAttempts=0,o&&e.onReconnect)try{e.onReconnect()}catch(l){c.warn("[AppSyncClient] Device key watcher onReconnect handler threw",{error:l})}break;case"data":this.resetDeviceKeyWatcherKeepAlive(e);let a=s.payload?.data?.onDeviceKeyRegistered;if(a){c.info("[AppSyncClient] Device key registration observed",{userId:e.userId,newDeviceId:a.deviceId,platform:a.platform});try{e.onNewDevice(a)}catch(l){c.warn("[AppSyncClient] Device key watcher onNewDevice handler threw",{error:l})}}break;case"ka":this.resetDeviceKeyWatcherKeepAlive(e);break;case"error":let d=s.payload?.errors?.[0]?.message||"Unknown error";this.handleDeviceKeyWatcherError(e,new Error(d));break}}catch(s){c.error("[AppSyncClient] Failed to parse device key watcher message",{error:s})}}),r.on("error",i=>{c.error("[AppSyncClient] Device key watcher WebSocket error",{userId:e.userId,error:i.message}),this.handleDeviceKeyWatcherError(e,i)}),r.on("close",i=>{c.info("[AppSyncClient] Device key watcher WebSocket closed",{userId:e.userId,code:i}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.deviceKeyWatcher===e&&this.handleDeviceKeyWatcherError(e,new Error(`WebSocket closed: ${i}`))}),e.ws=r,this.resetDeviceKeyWatcherKeepAlive(e)}catch(t){this.handleDeviceKeyWatcherError(e,t)}}sendDeviceKeyWatcherStart(e,t){let r=k(),{userId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onDeviceKeyRegistered,variables:{userId:i}}),extensions:{authorization:o}}}))}resetDeviceKeyWatcherKeepAlive(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleDeviceKeyWatcherError(e,new Error("Device key watcher keep-alive timeout"))},300*1e3)}handleDeviceKeyWatcherError(e,t){if(e.isReconnecting||e.destroyed||this.deviceKeyWatcher!==e)return;if(e.isReconnecting=!0,e.reconnectAttempts++,e.onError)try{e.onError(t)}catch{}if(e.ws){try{e.ws.removeAllListeners()}catch{}try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0);let i=e.reconnectAttempts<=E.urgentMaxAttempts?Math.min(E.baseDelayMs*Math.pow(E.backoffMultiplier,e.reconnectAttempts-1),E.maxDelayMs):E.persistentDelayMs;c.warn("[AppSyncClient] Device key watcher reconnect scheduled",{userId:e.userId,attempts:e.reconnectAttempts,delayMs:i,error:t.message}),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,e.destroyed||this.deviceKeyWatcher!==e){c.info("[AppSyncClient] Device key watcher reconnect skipped \u2014 state no longer canonical",{userId:e.userId});return}try{let s=await g.getTokens(this.environment);s&&(g.isTokenExpired(s)?await this.refreshTokens(s)&&c.info("[AppSyncClient] Tokens refreshed before device key watcher reconnect",{userId:e.userId}):this.tokens=s)}catch{c.warn("[AppSyncClient] Token refresh failed before device key watcher reconnect, using existing tokens",{userId:e.userId})}e.destroyed||this.deviceKeyWatcher!==e||(e.subscriptionId=(0,X.v4)(),this.createDeviceKeyWatcherConnection(e))},i)}watchForMobileEnd(e,t){c.info("[AppSyncClient] Starting mobile-end watcher",{sessionId:e});let r=this.sessionUpdateWatchers.get(e);r&&(c.info("[AppSyncClient] Replacing existing mobile-end watcher",{sessionId:e}),this.cleanupSessionUpdateWatcherState(r),this.sessionUpdateWatchers.delete(e));let i={sessionId:e,subscriptionId:(0,X.v4)(),ws:null,onMobileEndRequested:t,priorStatus:"ACTIVE",firedOnce:!1,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.sessionUpdateWatchers.set(e,i),this.createSessionUpdateWatcherConnection(i),{stop:()=>{this.sessionUpdateWatchers.get(e)===i&&(this.cleanupSessionUpdateWatcherState(i),this.sessionUpdateWatchers.delete(e),c.info("[AppSyncClient] Mobile-end watcher stopped",{sessionId:e}))}}}createSessionUpdateWatcherConnection(e){try{let t=this.buildRealtimeUrl(),r=new Y.default(t,["graphql-ws"]);r.on("open",()=>{c.info("[AppSyncClient] Mobile-end watcher WebSocket connected",{sessionId:e.sessionId}),r.send(JSON.stringify({type:"connection_init"}))}),r.on("message",i=>{try{let s=JSON.parse(i.toString());switch(s.type){case"connection_ack":this.sendSessionUpdateWatcherStart(r,e);break;case"start_ack":c.info("[AppSyncClient] Mobile-end watcher subscription started",{sessionId:e.sessionId}),e.isReconnecting=!1,e.reconnectAttempts=0;break;case"data":this.resetSessionUpdateWatcherKeepAlive(e),this.handleSessionUpdatePayload(e,s.payload);break;case"ka":this.resetSessionUpdateWatcherKeepAlive(e);break;case"error":let o=s.payload?.errors?.[0]?.message||"Unknown error";this.handleSessionUpdateWatcherError(e,new Error(o));break}}catch(s){c.error("[AppSyncClient] Failed to parse mobile-end watcher message",{error:s})}}),r.on("error",i=>{c.error("[AppSyncClient] Mobile-end watcher WebSocket error",{sessionId:e.sessionId,error:i.message}),this.handleSessionUpdateWatcherError(e,i)}),r.on("close",i=>{c.info("[AppSyncClient] Mobile-end watcher WebSocket closed",{sessionId:e.sessionId,code:i}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.sessionUpdateWatchers.get(e.sessionId)===e&&this.handleSessionUpdateWatcherError(e,new Error(`WebSocket closed: ${i}`))}),e.ws=r,this.resetSessionUpdateWatcherKeepAlive(e)}catch(t){this.handleSessionUpdateWatcherError(e,t)}}handleSessionUpdatePayload(e,t){let r=t?.data?.onSessionUpdated;if(!r){c.warn("[AppSyncClient] Mobile-end watcher received malformed payload",{sessionId:e.sessionId});return}if(e.firedOnce)return;let i=r.status;if(i==null){c.debug("[AppSyncClient] Mobile-end watcher skipped non-status payload",{sessionId:e.sessionId});return}if(e.priorStatus==="ACTIVE"&&i==="INACTIVE"){e.firedOnce=!0,e.priorStatus="INACTIVE",c.info("[AppSyncClient] Mobile end requested for session",{sessionId:e.sessionId}),Promise.resolve().then(()=>e.onMobileEndRequested()).catch(s=>{c.warn("[AppSyncClient] Mobile-end callback threw",{sessionId:e.sessionId,error:s})});return}e.priorStatus=i}sendSessionUpdateWatcherStart(e,t){let r=k(),{sessionId:i,subscriptionId:s}=t,o={host:new URL(r.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:J.onSessionUpdated,variables:{sessionId:i}}),extensions:{authorization:o}}}))}resetSessionUpdateWatcherKeepAlive(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSessionUpdateWatcherError(e,new Error("Mobile-end watcher keep-alive timeout"))},300*1e3)}handleSessionUpdateWatcherError(e,t){if(e.isReconnecting||e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e)return;if(e.isReconnecting=!0,e.reconnectAttempts++,e.ws){try{e.ws.removeAllListeners()}catch{}try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0);let i=e.reconnectAttempts<=E.urgentMaxAttempts?Math.min(E.baseDelayMs*Math.pow(E.backoffMultiplier,e.reconnectAttempts-1),E.maxDelayMs):E.persistentDelayMs;c.warn("[AppSyncClient] Mobile-end watcher reconnect scheduled",{sessionId:e.sessionId,attempts:e.reconnectAttempts,delayMs:i,error:t.message}),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,!(e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e)){try{let s=await g.getTokens(this.environment);s&&(g.isTokenExpired(s)?await this.refreshTokens(s):this.tokens=s)}catch{c.warn("[AppSyncClient] Token refresh failed before mobile-end watcher reconnect",{sessionId:e.sessionId})}e.destroyed||this.sessionUpdateWatchers.get(e.sessionId)!==e||(e.subscriptionId=(0,X.v4)(),this.createSessionUpdateWatcherConnection(e))}},i)}cleanupSessionUpdateWatcherState(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===Y.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}}startHeartbeat(e,t=120*1e3){this.stopHeartbeat(e),this.sendHeartbeat(e);let r=setInterval(()=>{this.sendHeartbeat(e)},t);this.heartbeatTimers.set(e,r),c.info("[AppSyncClient] Heartbeat started",{sessionId:e,intervalMs:t})}stopHeartbeat(e){let t=this.heartbeatTimers.get(e);t&&(clearInterval(t),this.heartbeatTimers.delete(e),c.info("[AppSyncClient] Heartbeat stopped",{sessionId:e}))}async sendHeartbeat(e){try{await this.updateSession({sessionId:e,lastHeartbeatAt:new Date().toISOString()}),c.debug("[AppSyncClient] Heartbeat sent",{sessionId:e})}catch(t){c.warn("[AppSyncClient] Heartbeat failed",{sessionId:e,error:t})}}cleanupSubscription(e){let t=this.activeSubscriptions.get(e);t&&(this.cleanupSubscriptionState(t),this.activeSubscriptions.get(e)===t&&this.activeSubscriptions.delete(e))}cleanupSubscriptions(){this.activeSubscriptions.forEach(e=>{this.cleanupSubscriptionState(e)}),this.activeSubscriptions.clear(),this.stopDeviceKeyWatcherInternal(),this.sessionUpdateWatchers.forEach(e=>{this.cleanupSessionUpdateWatcherState(e)}),this.sessionUpdateWatchers.clear(),this.heartbeatTimers.forEach(e=>clearInterval(e)),this.heartbeatTimers.clear()}};var Ft=v(require("crypto")),Ht=v(require("fs")),qt=v(require("http")),Vt=require("child_process");ie();U();q();re();var oe=8080,ae=20,ze="/callback";async function ve(n){let e=null;for(let t=0;t<ae;t++){let r=oe+t;try{let i=await new Promise((s,o)=>{let a=qt.createServer(n),d=y=>{a.removeListener("listening",l),o(y)},l=()=>{a.removeListener("error",d),a.on("error",y=>{c.error("[AuthService] OAuth server post-bind error",{port:r,code:y?.code,message:y?.message})}),s(a)};a.once("error",d),a.once("listening",l),a.listen(r,"localhost")});return c.info(`[AuthService] OAuth server bound on port ${r} (attempt ${t+1}/${ae})`),{server:i,port:r}}catch(i){if(e=i,i?.code==="EADDRINUSE")continue;throw i}}throw Object.assign(new Error(`All ports ${oe}-${oe+ae-1} are in use. Free at least one for OAuth callback or quit a conflicting service (common collisions: Vite, Webpack, Spring Boot, Docker exposed ports). Underlying: ${e?.message??"EADDRINUSE"}`),{code:"EADDRINUSE_ALL"})}var ce=class n{constructor(){}static getInstance(){return n.instance||(n.instance=new n),n.instance}openBrowser(e){console.error(""),console.error("Opening your browser for sign-in..."),this.isRunningInWSL()?console.error("If your browser does not open, paste this URL in your Windows browser:"):console.error("If your browser does not open automatically, visit this URL:"),console.error(` ${e}`),console.error("");let t=this.getBrowserCommands();this.tryBrowserCommand(t,e,0)}getBrowserCommands(){let e=process.platform;if(e==="darwin")return[{cmd:"open",fixedArgs:[]}];if(e==="win32")return[{cmd:"cmd",fixedArgs:["/c","start",""]}];let t=[];return this.isRunningInWSL()&&(t.push({cmd:"wslview",fixedArgs:[]}),t.push({cmd:"cmd.exe",fixedArgs:["/c","start",""]}),t.push({cmd:"powershell.exe",fixedArgs:["-NoProfile","-Command","Start-Process"]})),t.push({cmd:"xdg-open",fixedArgs:[]}),t}isRunningInWSL(){if(process.platform!=="linux")return!1;try{let e=Ht.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(e)}catch{return!1}}tryBrowserCommand(e,t,r){if(r>=e.length){c.debug("[AuthService] No browser-opening command succeeded. User must open the sign-in URL manually (printed to stderr above)."),console.error(""),console.error("\u26A0\uFE0F Could not open browser automatically."),this.isRunningInWSL()?console.error(" WSL detected \u2014 paste this URL in your Windows browser:"):console.error(" Please copy and paste this URL into your browser:"),console.error(` ${t}`),console.error("");return}let i=e[r],s=[...i.fixedArgs,t],o=!1,a=p=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${p}; trying next fallback`),this.tryBrowserCommand(e,t,r+1))},d=p=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${p}`))},l;try{l=(0,Vt.spawn)(i.cmd,s,{detached:!0,stdio:"ignore"})}catch(p){a(`threw synchronously: ${p?.message||p}`);return}l.on("error",p=>{a(`failed to spawn: ${p?.message||p}`)}),l.on("exit",(p,h)=>{p===0?d("exited successfully"):a(h?`terminated by signal ${h}`:`exited with code ${p}`)}),setTimeout(()=>{d("still running after 3s, assuming success")},3e3).unref(),l.unref()}generateState(){return Ft.randomBytes(32).toString("hex")}buildAuthUrl(e,t){let r=k(),i=new URLSearchParams({client_id:r.aws.cognitoClientId,response_type:"code",scope:"email openid profile",redirect_uri:t,state:e});return`https://${r.aws.cognitoDomain}/oauth2/authorize?${i.toString()}`}async exchangeCodeForTokens(e,t){let r=k(),i=`https://${r.aws.cognitoDomain}/oauth2/token`,s=new URLSearchParams({grant_type:"authorization_code",client_id:r.aws.cognitoClientId,code:e,redirect_uri:t}),o;try{o=await se(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},"Token exchange")}catch(d){throw await I("token_exchange_network_error"),_(d,"token_exchange_network_error"),d}if(!o.ok){let d=await o.text(),l=new Error(`Token exchange failed: ${o.status} ${d}`);throw await I("token_exchange_failed",{httpStatus:o.status}),_(l,"token_exchange_failed"),l}let a=await o.json();return{accessToken:a.access_token,idToken:a.id_token,refreshToken:a.refresh_token,expiresIn:a.expires_in}}decodeJwt(e){let t=e.split(".");if(t.length!==3)throw new Error("Invalid JWT");return JSON.parse(Buffer.from(t[1],"base64").toString("utf-8"))}async refreshTokens(e){let t=k(),r=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),s=await se(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");if(!s.ok)throw new Error(`Token refresh failed: ${s.status}`);let o=await s.json();return{accessToken:o.access_token,idToken:o.id_token,expiresIn:o.expires_in}}async login(){let e=await g.getTokens(A());if(e&&!g.isTokenExpired(e))return e;let t=this.generateState();return new Promise((r,i)=>{let s={},o=null,a=!1,d=!1,l=h=>{h.closeAllConnections?.()},y=h=>{if(a)return;a=!0,o&&(clearTimeout(o),o=null);let f=s.server;f?(l(f),f.close(()=>r(h))):r(h)},p=h=>{if(a)return;a=!0,o&&(clearTimeout(o),o=null);let f=s.server;f?(l(f),f.close(()=>i(h))):i(h)};(async()=>{let h;try{h=await ve(async(m,S)=>{if(d||a){S.writeHead(200,{Connection:"close"}),S.end();return}let nt=`http://localhost:${m.socket?.localPort??h.port}${ze}`,be=new URL(m.url||"",nt);if(be.pathname!==ze){S.writeHead(404,{Connection:"close"}),S.end("Not found");return}try{let F=be.searchParams.get("code"),Re=be.searchParams.get("state"),Q=be.searchParams.get("error");if(Q){let b=new Error(`OAuth error: ${Q}`);throw await I("cognito_rejected"),_(b,"cognito_rejected"),b}if(Re!==t){let b=new Error("State mismatch");throw await I("state_mismatch"),_(b,"state_mismatch"),b}if(!F){let b=new Error("No authorization code");throw await I("no_authorization_code"),_(b,"no_authorization_code"),b}d=!0;let de=await this.exchangeCodeForTokens(F,nt),it=this.decodeJwt(de.idToken),Ke={accessToken:de.accessToken,idToken:de.idToken,refreshToken:de.refreshToken,expiresAt:Date.now()+de.expiresIn*1e3,userId:it.sub,email:it.email||"unknown"};try{await g.setTokens(Ke,A())}catch(b){throw await I("keychain_write_failed",{errorFragment:b?.message?String(b.message):String(b)}),_(b,"keychain_write_failed"),b}S.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),S.end(`
|
|
206
208
|
<!DOCTYPE html>
|
|
207
209
|
<html>
|
|
208
210
|
<head><title>Success</title></head>
|
|
@@ -211,17 +213,17 @@ ${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,qt)}`:o+=` ${r}`),o}log
|
|
|
211
213
|
<p>You can close this window.</p>
|
|
212
214
|
</body>
|
|
213
215
|
</html>
|
|
214
|
-
`),a=!0,o&&(clearTimeout(o),o=null),setTimeout(()=>{let b=s.server;b?(l(b),b.close(()=>r(
|
|
216
|
+
`),a=!0,o&&(clearTimeout(o),o=null),setTimeout(()=>{let b=s.server;b?(l(b),b.close(()=>r(Ke))):r(Ke)},500)}catch(F){let Re=String(F?.message||F).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");S.writeHead(400,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),S.end(`
|
|
215
217
|
<!DOCTYPE html>
|
|
216
218
|
<html>
|
|
217
219
|
<head><title>Error</title></head>
|
|
218
220
|
<body style="font-family: system-ui; max-width: 720px; margin: 50px auto; padding: 0 16px;">
|
|
219
221
|
<h1 style="color: #ef4444; text-align: center;">✗ Authentication Failed</h1>
|
|
220
|
-
<pre style="background: #f4f4f5; padding: 16px; border-radius: 8px; white-space: pre-wrap; word-wrap: break-word; font-size: 13px; line-height: 1.5;">${
|
|
222
|
+
<pre style="background: #f4f4f5; padding: 16px; border-radius: 8px; white-space: pre-wrap; word-wrap: break-word; font-size: 13px; line-height: 1.5;">${Re}</pre>
|
|
221
223
|
<p style="text-align: center; color: #71717a; margin-top: 24px;">You can close this window and try again in your terminal.</p>
|
|
222
224
|
</body>
|
|
223
225
|
</html>
|
|
224
|
-
`),a=!0,o&&(clearTimeout(o),o=null),setTimeout(()=>{let
|
|
226
|
+
`),a=!0,o&&(clearTimeout(o),o=null),setTimeout(()=>{let Q=s.server;Q?(l(Q),Q.close(()=>i(F))):i(F)},500)}})}catch(m){let S=m?.code==="EADDRINUSE_ALL"?"port_range_exhausted":"server_listen_failed";return await I(S),_(m,S),p(m)}s.server=h.server;let f=`http://localhost:${h.port}${ze}`,B=this.buildAuthUrl(t,f);this.openBrowser(B),o=setTimeout(async()=>{let m=new Error("Login timeout");await I("login_timeout"),_(m,"login_timeout"),p(m)},120*1e3)})().catch(h=>{p(h)})})}async logout(){let e=k(),t=await g.deleteTokens(A());return t&&new Promise(r=>{let i={},s=null,o=!1,a=d=>{if(o)return;o=!0,s&&(clearTimeout(s),s=null);let l=i.server;l?(l.closeAllConnections?.(),l.close(()=>r(d))):r(d)};(async()=>{try{let d=await ve((h,f)=>{h.url?.startsWith("/signout")?(f.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),f.end(`
|
|
225
227
|
<!DOCTYPE html>
|
|
226
228
|
<html>
|
|
227
229
|
<head><title>Signed Out</title></head>
|
|
@@ -230,27 +232,27 @@ ${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,qt)}`:o+=` ${r}`),o}log
|
|
|
230
232
|
<p>You can close this window.</p>
|
|
231
233
|
</body>
|
|
232
234
|
</html>
|
|
233
|
-
`),setTimeout(()=>a(!0),500)):(
|
|
234
|
-
`);try{let n=await
|
|
235
|
+
`),setTimeout(()=>a(!0),500)):(f.writeHead(404,{Connection:"close"}),f.end("Not found"))});i.server=d.server;let l=`http://localhost:${d.port}/signout`,y=new URLSearchParams({client_id:e.aws.cognitoClientId,logout_uri:l}),p=`https://${e.aws.cognitoDomain}/logout?${y.toString()}`;this.openBrowser(p),s=setTimeout(()=>a(!0),30*1e3)}catch(d){c.warn("[AuthService] Logout server bind failed; tokens deleted but Cognito session may persist",{code:d?.code,message:d?.message}),a(!0)}})()})}async getStatus(){let e=await g.getTokens(A());return e?{authenticated:!g.isTokenExpired(e),tokens:e}:{authenticated:!1}}},$=ce.getInstance();ie();re();var u={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",dim:"\x1B[2m"};async function _r(){console.log(`${u.cyan}CodeVibe Login${u.reset}
|
|
236
|
+
`);try{let n=await $.getStatus();if(n.authenticated&&n.tokens){console.log(`${u.yellow}Already logged in as: ${n.tokens.email}${u.reset}`),console.log(`Token expires: ${new Date(n.tokens.expiresAt).toLocaleString()}`),console.log(`
|
|
235
237
|
Run '${u.dim}codevibe logout${u.reset}' to sign out first.`),process.exit(0);return}console.log("Opening browser for authentication..."),console.log(`${u.dim}Waiting for callback...${u.reset}
|
|
236
|
-
`);let e=await
|
|
237
|
-
${u.green}\u2713 Authentication successful!${u.reset}`),console.log(` User: ${e.email}`),console.log(` User ID: ${e.userId}`),console.log(` Expires: ${new Date(e.expiresAt).toLocaleString()}`),await
|
|
238
|
-
${u.red}\u2717 Authentication failed${u.reset}`),console.error(` Error: ${e}`),
|
|
239
|
-
`);try{let n=await
|
|
240
|
-
${u.dim}Clearing browser session...${u.reset}`)):console.log(`${u.red}\u2717 Failed to log out.${u.reset}`),process.exit(0)}catch(n){console.error(`${u.red}\u2717 Logout failed: ${n.message}${u.reset}`),process.exit(1)}}async function
|
|
241
|
-
`);try{let n=await
|
|
238
|
+
`);let e=await $.login();e&&(console.log(`
|
|
239
|
+
${u.green}\u2713 Authentication successful!${u.reset}`),console.log(` User: ${e.email}`),console.log(` User ID: ${e.userId}`),console.log(` Expires: ${new Date(e.expiresAt).toLocaleString()}`),await le(e.userId)),process.exit(0)}catch(n){let e=(()=>{let t=n?.message;return typeof t=="string"&&t.length>0?t:n==null?"(null/undefined error)":`[no_message ctor=${n?.constructor?.name??typeof n}] ${String(n).substring(0,80)}`})();console.error(`
|
|
240
|
+
${u.red}\u2717 Authentication failed${u.reset}`),console.error(` Error: ${e}`),pe(n)||await I("unknown",{errorFragment:e}),process.exit(1)}}async function xr(){console.log(`${u.cyan}CodeVibe Logout${u.reset}
|
|
241
|
+
`);try{let n=await $.getStatus();if(!n.authenticated){console.log(`${u.yellow}Not logged in.${u.reset}`),process.exit(0);return}let e=n.tokens?.email;await $.logout()?(console.log(`${u.green}\u2713 Logged out successfully.${u.reset}`),console.log(` Previous user: ${e}`),console.log(`
|
|
242
|
+
${u.dim}Clearing browser session...${u.reset}`)):console.log(`${u.red}\u2717 Failed to log out.${u.reset}`),process.exit(0)}catch(n){console.error(`${u.red}\u2717 Logout failed: ${n.message}${u.reset}`),process.exit(1)}}async function Cr(){console.log(`${u.cyan}CodeVibe Auth Status${u.reset}
|
|
243
|
+
`);try{let n=await $.getStatus();if(!n.tokens){console.log(`${u.yellow}Not authenticated.${u.reset}`),console.log(`
|
|
242
244
|
Run '${u.dim}codevibe login${u.reset}' to sign in.`),process.exit(0);return}let e=!n.authenticated;console.log(e?`${u.yellow}\u26A0 Token expired${u.reset}`:`${u.green}\u2713 Authenticated${u.reset}`),console.log(` User: ${n.tokens.email}`),console.log(` User ID: ${n.tokens.userId}`),console.log(` Expires: ${new Date(n.tokens.expiresAt).toLocaleString()}`),e&&console.log(`
|
|
243
|
-
${u.dim}Token will be refreshed automatically.${u.reset}`),process.exit(0)}catch(n){console.error(`${u.red}\u2717 Status check failed: ${n.message}${u.reset}`),process.exit(1)}}async function
|
|
245
|
+
${u.dim}Token will be refreshed automatically.${u.reset}`),process.exit(0)}catch(n){console.error(`${u.red}\u2717 Status check failed: ${n.message}${u.reset}`),process.exit(1)}}async function Dr(){console.log(`${u.cyan}CodeVibe Reset Device${u.reset}
|
|
244
246
|
`),console.log(`${u.red}\u26A0 WARNING: This will delete your device identity.${u.reset}`),console.log(`${u.red} Old encrypted sessions will become inaccessible.${u.reset}
|
|
245
|
-
`);let{keychainManager:n}=await Promise.resolve().then(()=>(
|
|
247
|
+
`);let{keychainManager:n}=await Promise.resolve().then(()=>(U(),Ot));try{await n.clearAllData(),console.log(`${u.green}\u2713 Device reset complete.${u.reset}`),console.log(` Run '${u.dim}codevibe login${u.reset}' to set up again.`),process.exit(0)}catch(e){console.error(`${u.red}\u2717 Reset failed: ${e.message}${u.reset}`),process.exit(1)}}function Rr(){console.log(`CodeVibe Authentication
|
|
246
248
|
`),console.log("Usage:"),console.log(" codevibe login - Sign in via browser"),console.log(" codevibe logout - Sign out"),console.log(" codevibe status - Show auth status"),console.log(" codevibe reset-device - Reset device identity (destructive)"),console.log(`
|
|
247
|
-
Environment:`),console.log(' Set ENVIRONMENT env var to "development" or "production" (default)'),console.log(" Example: ENVIRONMENT=development codevibe login")}async function
|
|
248
|
-
`);let r=n.slice(2).filter(i=>!i.startsWith("--"))[0];switch(r){case"login":await
|
|
249
|
-
`).replace(
|
|
249
|
+
Environment:`),console.log(' Set ENVIRONMENT env var to "development" or "production" (default)'),console.log(" Example: ENVIRONMENT=development codevibe login")}async function Ce(n){let e=A();console.log(`${u.dim}Environment: ${e}${u.reset}
|
|
250
|
+
`);let r=n.slice(2).filter(i=>!i.startsWith("--"))[0];switch(r){case"login":await _r();break;case"logout":await xr();break;case"status":await Cr();break;case"reset-device":await Dr();break;default:Rr(),process.exit(r?1:0)}}require.main===module&&Ce(process.argv).catch(n=>{console.error("Error:",n),process.exit(1)});re();ie();q();var Kr=/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;function Gt(n){let e=Xe(n);if(!e)return null;let t=Or(e);if(t)return t;let r=Pr(e);return r||null}function Xe(n){return n.replace(/\r/g,`
|
|
251
|
+
`).replace(Kr,"").replace(/[│┌┐└┘─├┤┬┴┼╌╎╭╮╯╰║═╔╗╚╝╠╣╦╩╬]/g," ").replace(/[ \t]+\n/g,`
|
|
250
252
|
`).replace(/\n{3,}/g,`
|
|
251
253
|
|
|
252
|
-
`).trim()}function
|
|
253
|
-
`).map(y=>y.trim()),t=
|
|
254
|
-
`):r,o=s.toLowerCase(),a=o.includes("what to change")||o.includes("what should")||o.includes("provide")||o.includes("instructions");return{kind:"yes_no",promptText:s,options:a?[{number:"1",text:"Yes"},{number:"2",text:"No, provide instructions"}]:[{number:"1",text:"Yes"},{number:"2",text:"No"}],submitMap:{1:"y",2:"n"},requiresFollowUpText:a}}function
|
|
255
|
-
`).map(d=>d.trim()),t
|
|
256
|
-
`):"Select an option",options:r,submitMap:i}}function gr(n,e){for(let t=n.length-1;t>=0;t-=1)if(e(n[t]))return t;return-1}function Je(n){let e=n.match(/^(?:[>›❯▸▶➜➤*●]\s*)?(\d+)\.\s+(.*)$/);return e?{number:e[1],text:e[2]}:null}function yr(n){return Je(n)!==null}function mr(n){let e=[];for(let r=0;r<n.length;r+=1){let i=Je(n[r]);i&&e.push({index:r,number:Number(i.number)})}if(e.length===0)return[];let t=[e[e.length-1]];for(let r=e.length-2;r>=0;r-=1){let i=e[r],s=t[0];if(fr(n,i.index,s.index))break;i.number===s.number-1&&t.unshift(i)}return t.map((r,i)=>{let s=i+1<t.length?t[i+1].index-1:vr(n,r.index);return{index:r.index,line:Sr(n,r.index,s)}})}function fr(n,e,t){for(let r=e+1;r<t;r+=1)if(!n[r])return!0;return!1}var Pt=/\((?:[a-z0-9]|esc|escape)\)\s*$/i;function vr(n,e){let t=Pt.test(n[e])?e:-1;for(let r=e+1;r<n.length&&!(!n[r]||yr(n[r]));r+=1)Pt.test(n[r])&&(t=r);return t>=0?t:e}function Sr(n,e,t){let r=n[e];for(let i=e+1;i<=t;i+=1)r+=n[i];return r}function Nt(n,e){if(e<0)return[];let t=qe(n,e);if(t<0)return[];let{start:r,end:i}=Ve(n,t),s=n.slice(r,i+1).filter(Boolean);if(kr(s)){let p=wr(n,r-1);return p.length>0?p:s}if(r<=1)return s;let o=r-1;if(o=qe(n,o),o<0||o===r-1)return s;let{start:a,end:d}=Ve(n,o),l=n.slice(a,d+1).filter(Boolean);return l.some(Ut)?[...l,...s]:s}function Ut(n){return/^(?:would you like to|do you want to|the model would like to|action required|confirm)\b/i.test(n)}function qe(n,e){let t=e;for(;t>=0&&!n[t];)t-=1;return t}function Ve(n,e){let t=e;for(;t>=0&&n[t];)t-=1;return{start:t+1,end:e}}function wr(n,e){let t=[],r=e;for(;r>=0&&t.length<2&&(r=qe(n,r),!(r<0));){let{start:s,end:o}=Ve(n,r),a=n.slice(s,o+1).filter(Boolean);a.length>0&&t.unshift(a),r=s-1}if(t.length===0)return[];let i=t.findIndex(s=>s.some(Ut));return i>=0?t.slice(i).flat():t[t.length-1]}function kr(n){return n.length===0?!1:n.filter(br).length>=Math.max(2,Math.ceil(n.length/2))}function br(n){return/^\d+\s/.test(n)}ae();P();ae();P();H();async function Y(n,e,t,r={}){let i;try{i=await t.getSession(n)}catch(p){return c.warn("[SessionRekey] Failed to fetch session state for re-key",{sessionId:n,error:p instanceof Error?p.message:String(p)}),0}if(!i)return c.warn("[SessionRekey] Session not found, skipping re-key",{sessionId:n}),0;if(!i.isEncrypted)return 0;let s=i.encryptedKeys||[],o=new Set(s.map(p=>p.deviceId)),a=r.forceDeviceIds??new Set,d;try{d=await t.listUserDeviceKeys()}catch(p){return c.warn("[SessionRekey] Failed to fetch user device keys",{sessionId:n,error:p instanceof Error?p.message:String(p)}),0}let l=d.filter(p=>!o.has(p.deviceId)||a.has(p.deviceId));if(l.length===0)return 0;c.info("[SessionRekey] Granting session key to devices",{sessionId:n,existingDeviceCount:s.length,grantCount:l.length,grantDeviceIds:l.map(p=>p.deviceId),forceCount:a.size});let y=0;for(let p of l)try{let h=E.encryptSessionKey(e,p.publicKey);await t.grantSessionKey({sessionId:n,deviceId:p.deviceId,encryptedKey:h.encryptedKey,ephemeralPublicKey:h.ephemeralPublicKey}),y++,c.info("[SessionRekey] Granted session key to device",{sessionId:n,deviceId:p.deviceId,platform:p.platform})}catch(h){c.warn("[SessionRekey] Failed to grant session key to device",{sessionId:n,deviceId:p.deviceId,error:h instanceof Error?h.message:String(h)})}return y>0&&c.info("[SessionRekey] Re-key complete",{sessionId:n,grantedCount:y,requestedCount:l.length}),y}async function $t(n,e){let t=e.pollIntervalMs??5e3,r=e.maxAttempts??6,i,s;try{i=await g.getDeviceId(),s=await g.getDevicePrivateKey()}catch(o){c.warn("[SessionRekey] A1 pre-loop keychain read failed",{sessionId:n,error:o instanceof Error?o.message:String(o)});try{e.onTimeout?.(0)}catch{}return null}for(let o=1;o<=r;o++){o>1&&await new Promise(h=>setTimeout(h,t));let a;try{a=await e.appSyncClient.getSession(n)}catch(h){c.warn("[SessionRekey] A1 getSession failed during poll, will retry",{sessionId:n,attempt:o,error:h instanceof Error?h.message:String(h)});continue}let d=a?.encryptedKeys??[],l=d.filter(h=>h.deviceId===i);if(l.length===0){c.info("[SessionRekey] A1 our deviceId still not in encryptedKeys",{sessionId:n,attempt:o,freshDeviceCount:d.length});continue}let y=null,p=[];for(let h=l.length-1;h>=0;h--)try{y=E.decryptSessionKey(l[h],s);break}catch(m){p.push(m instanceof Error?m.message:String(m))}if(y){g.cacheSessionKey(n,y);try{e.onSuccess?.(o)}catch{}return c.info("[SessionRekey] A1 self-rekey successful",{sessionId:n,attempt:o,entriesTriedToDecrypt:l.length}),y}c.warn("[SessionRekey] A1 found entries but all decrypt-failed, will retry",{sessionId:n,attempt:o,entriesTried:l.length,errors:p})}try{e.onTimeout?.(r)}catch{}return c.warn("[SessionRekey] A1 self-rekey exhausted maxAttempts",{sessionId:n,maxAttempts:r}),null}P();async function ge(n,e){try{let t=await g.getDeviceId(),r=await g.getDevicePublicKey(),i=g.getDevicePlatform(),s=g.getDeviceName();e.info("Registering device encryption key",{deviceId:t,platform:i,deviceName:s}),await n.registerDeviceKey(t,r,i,s),g.setIsRegistered(!0),e.info("Device encryption key registered successfully",{deviceId:t})}catch(t){e.warn("Failed to register device encryption key (E2E encryption may not work):",t)}}async function Ae(n,e,t){try{let r=await e.listUserDeviceKeys();if(r.length===0)return t.info("No device keys found, session will not be encrypted"),null;t.info("Preparing session encryption",{sessionId:n,deviceCount:r.length});let i=Ee(n),{sessionKey:s,encryptedKeys:o,skippedDeviceIds:a}=g.createSessionKey(r,{onDeviceSkipped:d=>{Et({skipped_count_bucket:re(d),session_hash:i}).catch(()=>{})}});return a.length>0&&It({session_hash:i,encrypted_count_bucket:re(o.length),skipped_count_bucket:re(a.length)}).catch(()=>{}),t.info("Session encryption prepared",{sessionId:n,deviceCount:o.length,skippedCount:a.length}),{sessionKey:s,encryptedKeys:o,skippedDeviceIds:a}}catch(r){return t.warn("Failed to prepare session encryption:",r),null}}async function je(n,e,t){let{sessionId:r,userId:i,agentType:s,projectPath:o,metadata:a}=n,d=null;try{d=await e.getSession(r)}catch(m){t.warn("Failed to get session (will attempt to create new)",{sessionId:r,error:m})}if(d){t.info("Session exists in backend - reactivating",{sessionId:r,previousStatus:d.status});try{await e.updateSession({sessionId:r,status:"ACTIVE"})}catch(f){t.warn("Failed to reactivate existing session, will continue",{sessionId:r,error:f})}let m=null,W=d.encryptedKeys??[];if(d.isEncrypted){if(W.length>0){try{let f=await g.getSessionKey(r,W);f&&(m=f,g.cacheSessionKey(r,f),t.info("Session key retrieved for resumed session",{sessionId:r}))}catch(f){t.warn("Failed to retrieve session key for resumed session",{sessionId:r,error:f})}if(!m){let f=Ee(r);t.info("Self-rekey: re-registering device key + awaiting grant",{sessionId:r,otherDeviceCount:W.length}),Tt({session_hash:f,other_device_count_bucket:re(W.length)}).catch(()=>{});try{await ge(e,t),m=await $t(r,{appSyncClient:e,onSuccess:S=>{Ct({session_hash:f,attempt_count:S}).catch(()=>{})},onTimeout:S=>{xt({session_hash:f,attempt_count:S}).catch(()=>{})}})}catch(S){t.warn("Self-rekey path failed",{sessionId:r,error:S instanceof Error?S.message:String(S)})}}}else t.warn("Encrypted session has empty encryptedKeys; cannot self-rekey",{sessionId:r});if(!m){let f=new Error(`Cannot resume encrypted session ${r}: `+(W.length===0?"session is marked encrypted but session.encryptedKeys is empty (corrupt state). Cannot self-rekey without a peer device. Start a new session.":"this device's key is not in session.encryptedKeys and self-rekey did not complete within 30s. This typically means the device key was rotated and mobile has not yet granted access to this device. Open the mobile app to refresh device keys, then retry."));throw f.code="ENCRYPTED_SESSION_NO_KEY",f}}if(m)try{let f=await Y(r,m,e);f>0&&(t.info("Session re-keyed for newly registered devices on resume",{sessionId:r,newDeviceCount:f}),At({session_hash:Ee(r),granted_count_bucket:re(f)}).catch(()=>{}))}catch(f){t.warn("Session re-key on resume failed (non-fatal)",{sessionId:r,error:f instanceof Error?f.message:String(f)})}return{resumed:!0,sessionKey:m}}let l=await Ae(r,e,t),y=o,p=a;l&&(y=E.encryptContent(o,l.sessionKey),p&&Object.keys(p).length>0&&(p={encrypted:E.encryptMetadata(p,l.sessionKey)}),t.info("Session data encrypted",{sessionId:r})),t.info("Creating new session in backend",{sessionId:r,userId:i,agentType:s,isEncrypted:!!l}),await e.createSession({sessionId:r,userId:i,agentType:s,projectPath:y,status:"ACTIVE",metadata:p,isEncrypted:l?!0:void 0,creatorDeviceId:l?await g.getDeviceId():void 0,encryptionVersion:l?1:void 0,encryptedKeys:l?.encryptedKeys});let h=l?.sessionKey||null;return l&&g.cacheSessionKey(r,l.sessionKey),t.info("Session created",{sessionId:r,userId:i,isEncrypted:!!l}),{resumed:!1,sessionKey:h}}P();function ze(n,e){let t=n.getCurrentUserId(),r=async(s,o)=>{let a=g.getCachedSessionIds();if(a.length===0){e.info("[DeviceKeyWatcher] No active sessions to re-key",{reason:s});return}e.info("[DeviceKeyWatcher] Running re-key pass",{reason:s,activeSessionCount:a.length,forceDeviceCount:o?.size??0});for(let d of a){let l=g.getCachedSessionKey(d);if(l)try{let y=await Y(d,l,n,o?{forceDeviceIds:o}:void 0);y>0&&e.info("[DeviceKeyWatcher] Session re-keyed",{sessionId:d,newDeviceCount:y,reason:s})}catch(y){e.warn("[DeviceKeyWatcher] Re-key failed for session (non-fatal)",{sessionId:d,reason:s,error:y instanceof Error?y.message:String(y)})}}},i=n.subscribeToDeviceKeyRegistered(t,s=>{e.info("[DeviceKeyWatcher] New device observed, triggering re-key",{userId:t,newDeviceId:s.deviceId,platform:s.platform,deviceName:s.deviceName}),r(`new-device:${s.deviceId}`,new Set([s.deviceId]))},()=>{r("watcher-reconnect")},s=>{e.warn("[DeviceKeyWatcher] Subscription error (will retry)",{error:s instanceof Error?s.message:String(s)})});return e.info("[DeviceKeyWatcher] Started",{userId:t}),i}var M=new Map;function Ye(n){let e=Date.now(),t=n.agentClock?Date.parse(n.agentClock):NaN,r=Number.isNaN(t)?e:t,i=M.get(n.orderingKey)??0,s=typeof n.notBeforeMs=="number"&&Number.isFinite(n.notBeforeMs)?n.notBeforeMs+1:0,o=Math.max(r,i+1,s);if(M.has(n.orderingKey)&&M.delete(n.orderingKey),M.set(n.orderingKey,o),M.size>1024){let a=M.keys().next().value;a!==void 0&&M.delete(a)}return new Date(o).toISOString()}function Xe(){M.clear()}0&&(module.exports={AgentType,AppSyncClient,AuthService,CryptoError,CryptoService,DeliveryStatus,ENCRYPTION_VERSION,EventSource,EventType,KeychainError,KeychainManager,Logger,PORT_RANGE_SIZE,PRIMARY_PORT,SessionStatus,_resetPrepareEventTimestampForTesting,authService,bindOAuthServer,createLogger,cryptoService,errorWasBeaconed,fireAuthCompletedBeacon,fireAuthFailedBeacon,getConfig,getEnvironment,getErrorReason,keychainManager,loadConfig,logger,markErrorBeaconed,mutations,normalizeSnapshot,parseInteractivePrompt,prepareEventTimestamp,prepareSessionEncryption,queries,registerDeviceEncryptionKey,rekeySessionForNewDevices,resumeOrCreateSession,runAuthCli,startDeviceKeyWatcher,subscriptions});
|
|
254
|
+
`).trim()}function Or(n){let e=n.split(`
|
|
255
|
+
`).map(y=>y.trim()),t=Nr(e,y=>/\[(?:y\/n|Y\/n|y\/N)\]/.test(y)),r=t>=0?e[t]:null;if(!r)return null;let i=zt(e,t),s=i.length>0?i.join(`
|
|
256
|
+
`):r,o=s.toLowerCase(),a=o.includes("what to change")||o.includes("what should")||o.includes("provide")||o.includes("instructions");return{kind:"yes_no",promptText:s,options:a?[{number:"1",text:"Yes"},{number:"2",text:"No, provide instructions"}]:[{number:"1",text:"Yes"},{number:"2",text:"No"}],submitMap:{1:"y",2:"n"},requiresFollowUpText:a}}function Pr(n){let e=n.split(`
|
|
257
|
+
`).map(d=>d.trim()),t=$r(e);if(t.length<2)return null;let r=t.map(({line:d})=>Ze(d)).filter(d=>!!d),i={};for(let d of r)i[d.number]=d.number;let s=t[0]?.index??-1,o=zt(e,s-1);return{kind:"numbered",promptText:o.length>0?o.join(`
|
|
258
|
+
`):"Select an option",options:r,submitMap:i}}function Nr(n,e){for(let t=n.length-1;t>=0;t-=1)if(e(n[t]))return t;return-1}function Ze(n){let e=n.match(/^(?:[>›❯▸▶➜➤*●]\s*)?(\d+)\.\s+(.*)$/);return e?{number:e[1],text:e[2]}:null}function Ur(n){return Ze(n)!==null}function $r(n){let e=[];for(let r=0;r<n.length;r+=1){let i=Ze(n[r]);i&&e.push({index:r,number:Number(i.number)})}if(e.length===0)return[];let t=[e[e.length-1]];for(let r=e.length-2;r>=0;r-=1){let i=e[r],s=t[0];if(Lr(n,i.index,s.index))break;i.number===s.number-1&&t.unshift(i)}return t.map((r,i)=>{let s=i+1<t.length?t[i+1].index-1:Mr(n,r.index);return{index:r.index,line:Wr(n,r.index,s)}})}function Lr(n,e,t){for(let r=e+1;r<t;r+=1)if(!n[r])return!0;return!1}var jt=/\((?:[a-z0-9]|esc|escape)\)\s*$/i;function Mr(n,e){let t=jt.test(n[e])?e:-1;for(let r=e+1;r<n.length&&!(!n[r]||Ur(n[r]));r+=1)jt.test(n[r])&&(t=r);return t>=0?t:e}function Wr(n,e,t){let r=n[e];for(let i=e+1;i<=t;i+=1)r+=n[i];return r}function zt(n,e){if(e<0)return[];let t=Je(n,e);if(t<0)return[];let{start:r,end:i}=Ye(n,t),s=n.slice(r,i+1).filter(Boolean);if(Fr(s)){let p=Br(n,r-1);return p.length>0?p:s}if(r<=1)return s;let o=r-1;if(o=Je(n,o),o<0||o===r-1)return s;let{start:a,end:d}=Ye(n,o),l=n.slice(a,d+1).filter(Boolean);return l.some(Jt)?[...l,...s]:s}function Jt(n){return/^(?:would you like to|do you want to|the model would like to|action required|confirm)\b/i.test(n)}function Je(n,e){let t=e;for(;t>=0&&!n[t];)t-=1;return t}function Ye(n,e){let t=e;for(;t>=0&&n[t];)t-=1;return{start:t+1,end:e}}function Br(n,e){let t=[],r=e;for(;r>=0&&t.length<2&&(r=Je(n,r),!(r<0));){let{start:s,end:o}=Ye(n,r),a=n.slice(s,o+1).filter(Boolean);a.length>0&&t.unshift(a),r=s-1}if(t.length===0)return[];let i=t.findIndex(s=>s.some(Jt));return i>=0?t.slice(i).flat():t[t.length-1]}function Fr(n){return n.length===0?!1:n.filter(Hr).length>=Math.max(2,Math.ceil(n.length/2))}function Hr(n){return/^\d+\s/.test(n)}ge();U();ge();U();q();async function Z(n,e,t,r={}){let i;try{i=await t.getSession(n)}catch(p){return c.warn("[SessionRekey] Failed to fetch session state for re-key",{sessionId:n,error:p instanceof Error?p.message:String(p)}),0}if(!i)return c.warn("[SessionRekey] Session not found, skipping re-key",{sessionId:n}),0;if(!i.isEncrypted)return 0;let s=i.encryptedKeys||[],o=new Set(s.map(p=>p.deviceId)),a=r.forceDeviceIds??new Set,d;try{d=await t.listUserDeviceKeys()}catch(p){return c.warn("[SessionRekey] Failed to fetch user device keys",{sessionId:n,error:p instanceof Error?p.message:String(p)}),0}let l=d.filter(p=>!o.has(p.deviceId)||a.has(p.deviceId));if(l.length===0)return 0;c.info("[SessionRekey] Granting session key to devices",{sessionId:n,existingDeviceCount:s.length,grantCount:l.length,grantDeviceIds:l.map(p=>p.deviceId),forceCount:a.size});let y=0;for(let p of l)try{let h=T.encryptSessionKey(e,p.publicKey);await t.grantSessionKey({sessionId:n,deviceId:p.deviceId,encryptedKey:h.encryptedKey,ephemeralPublicKey:h.ephemeralPublicKey}),y++,c.info("[SessionRekey] Granted session key to device",{sessionId:n,deviceId:p.deviceId,platform:p.platform})}catch(h){c.warn("[SessionRekey] Failed to grant session key to device",{sessionId:n,deviceId:p.deviceId,error:h instanceof Error?h.message:String(h)})}return y>0&&c.info("[SessionRekey] Re-key complete",{sessionId:n,grantedCount:y,requestedCount:l.length}),y}async function Yt(n,e){let t=e.pollIntervalMs??5e3,r=e.maxAttempts??6,i,s;try{i=await g.getDeviceId(),s=await g.getDevicePrivateKey()}catch(o){c.warn("[SessionRekey] A1 pre-loop keychain read failed",{sessionId:n,error:o instanceof Error?o.message:String(o)});try{e.onTimeout?.(0)}catch{}return null}for(let o=1;o<=r;o++){o>1&&await new Promise(h=>setTimeout(h,t));let a;try{a=await e.appSyncClient.getSession(n)}catch(h){c.warn("[SessionRekey] A1 getSession failed during poll, will retry",{sessionId:n,attempt:o,error:h instanceof Error?h.message:String(h)});continue}let d=a?.encryptedKeys??[],l=d.filter(h=>h.deviceId===i);if(l.length===0){c.info("[SessionRekey] A1 our deviceId still not in encryptedKeys",{sessionId:n,attempt:o,freshDeviceCount:d.length});continue}let y=null,p=[];for(let h=l.length-1;h>=0;h--)try{y=T.decryptSessionKey(l[h],s);break}catch(f){p.push(f instanceof Error?f.message:String(f))}if(y){g.cacheSessionKey(n,y);try{e.onSuccess?.(o)}catch{}return c.info("[SessionRekey] A1 self-rekey successful",{sessionId:n,attempt:o,entriesTriedToDecrypt:l.length}),y}c.warn("[SessionRekey] A1 found entries but all decrypt-failed, will retry",{sessionId:n,attempt:o,entriesTried:l.length,errors:p})}try{e.onTimeout?.(r)}catch{}return c.warn("[SessionRekey] A1 self-rekey exhausted maxAttempts",{sessionId:n,maxAttempts:r}),null}U();async function Se(n,e){try{let t=await g.getDeviceId(),r=await g.getDevicePublicKey(),i=g.getDevicePlatform(),s=g.getDeviceName();e.info("Registering device encryption key",{deviceId:t,platform:i,deviceName:s}),await n.registerDeviceKey(t,r,i,s),g.setIsRegistered(!0),e.info("Device encryption key registered successfully",{deviceId:t})}catch(t){e.warn("Failed to register device encryption key (E2E encryption may not work):",t)}}re();async function De(n,e,t){try{let r=await e.listUserDeviceKeys();if(r.length===0)return t.info("No device keys found, session will not be encrypted"),null;t.info("Preparing session encryption",{sessionId:n,deviceCount:r.length});let i=Ee(n),{sessionKey:s,encryptedKeys:o,skippedDeviceIds:a}=g.createSessionKey(r,{onDeviceSkipped:d=>{gt({skipped_count_bucket:te(d),session_hash:i}).catch(()=>{})}});return a.length>0&&yt({session_hash:i,encrypted_count_bucket:te(o.length),skipped_count_bucket:te(a.length)}).catch(()=>{}),t.info("Session encryption prepared",{sessionId:n,deviceCount:o.length,skippedCount:a.length}),{sessionKey:s,encryptedKeys:o,skippedDeviceIds:a}}catch(r){return t.warn("Failed to prepare session encryption:",r),null}}async function Qe(n,e,t){let{sessionId:r,userId:i,agentType:s,projectPath:o,metadata:a}=n,d=null;try{d=await e.getSession(r)}catch(f){t.warn("Failed to get session (will attempt to create new)",{sessionId:r,error:f})}if(d){t.info("Session exists in backend - reactivating",{sessionId:r,previousStatus:d.status});try{await e.updateSession({sessionId:r,status:"ACTIVE"})}catch(m){t.warn("Failed to reactivate existing session, will continue",{sessionId:r,error:m})}let f=null,B=d.encryptedKeys??[];if(d.isEncrypted){if(B.length>0){try{let m=await g.getSessionKey(r,B);m&&(f=m,g.cacheSessionKey(r,m),t.info("Session key retrieved for resumed session",{sessionId:r}))}catch(m){t.warn("Failed to retrieve session key for resumed session",{sessionId:r,error:m})}if(!f){let m=Ee(r);t.info("Self-rekey: re-registering device key + awaiting grant",{sessionId:r,otherDeviceCount:B.length}),mt({session_hash:m,other_device_count_bucket:te(B.length)}).catch(()=>{});try{await Se(e,t),f=await Yt(r,{appSyncClient:e,onSuccess:S=>{vt({session_hash:m,attempt_count:S}).catch(()=>{})},onTimeout:S=>{St({session_hash:m,attempt_count:S}).catch(()=>{})}})}catch(S){t.warn("Self-rekey path failed",{sessionId:r,error:S instanceof Error?S.message:String(S)})}}}else t.warn("Encrypted session has empty encryptedKeys; cannot self-rekey",{sessionId:r});if(!f){let m=new Error(`Cannot resume encrypted session ${r}: `+(B.length===0?"session is marked encrypted but session.encryptedKeys is empty (corrupt state). Cannot self-rekey without a peer device. Start a new session.":"this device's key is not in session.encryptedKeys and self-rekey did not complete within 30s. This typically means the device key was rotated and mobile has not yet granted access to this device. Open the mobile app to refresh device keys, then retry."));throw m.code="ENCRYPTED_SESSION_NO_KEY",m}}if(f)try{let m=await Z(r,f,e);m>0&&(t.info("Session re-keyed for newly registered devices on resume",{sessionId:r,newDeviceCount:m}),ft({session_hash:Ee(r),granted_count_bucket:te(m)}).catch(()=>{}))}catch(m){t.warn("Session re-key on resume failed (non-fatal)",{sessionId:r,error:m instanceof Error?m.message:String(m)})}return{resumed:!0,sessionKey:f}}let l=await De(r,e,t),y=o,p=a;l&&(y=T.encryptContent(o,l.sessionKey),p&&Object.keys(p).length>0&&(p={encrypted:T.encryptMetadata(p,l.sessionKey)}),t.info("Session data encrypted",{sessionId:r})),t.info("Creating new session in backend",{sessionId:r,userId:i,agentType:s,isEncrypted:!!l}),await e.createSession({sessionId:r,userId:i,agentType:s,projectPath:y,status:"ACTIVE",metadata:p,isEncrypted:l?!0:void 0,creatorDeviceId:l?await g.getDeviceId():void 0,encryptionVersion:l?1:void 0,encryptedKeys:l?.encryptedKeys});let h=l?.sessionKey||null;return l&&g.cacheSessionKey(r,l.sessionKey),t.info("Session created",{sessionId:r,userId:i,isEncrypted:!!l}),{resumed:!1,sessionKey:h}}U();function et(n,e){let t=n.getCurrentUserId(),r=async(s,o)=>{let a=g.getCachedSessionIds();if(a.length===0){e.info("[DeviceKeyWatcher] No active sessions to re-key",{reason:s});return}e.info("[DeviceKeyWatcher] Running re-key pass",{reason:s,activeSessionCount:a.length,forceDeviceCount:o?.size??0});for(let d of a){let l=g.getCachedSessionKey(d);if(l)try{let y=await Z(d,l,n,o?{forceDeviceIds:o}:void 0);y>0&&e.info("[DeviceKeyWatcher] Session re-keyed",{sessionId:d,newDeviceCount:y,reason:s})}catch(y){e.warn("[DeviceKeyWatcher] Re-key failed for session (non-fatal)",{sessionId:d,reason:s,error:y instanceof Error?y.message:String(y)})}}},i=n.subscribeToDeviceKeyRegistered(t,s=>{e.info("[DeviceKeyWatcher] New device observed, triggering re-key",{userId:t,newDeviceId:s.deviceId,platform:s.platform,deviceName:s.deviceName}),r(`new-device:${s.deviceId}`,new Set([s.deviceId]))},()=>{r("watcher-reconnect")},s=>{e.warn("[DeviceKeyWatcher] Subscription error (will retry)",{error:s instanceof Error?s.message:String(s)})});return e.info("[DeviceKeyWatcher] Started",{userId:t}),i}var W=new Map;function tt(n){let e=Date.now(),t=n.agentClock?Date.parse(n.agentClock):NaN,r=Number.isNaN(t)?e:t,i=W.get(n.orderingKey)??0,s=typeof n.notBeforeMs=="number"&&Number.isFinite(n.notBeforeMs)?n.notBeforeMs+1:0,o=Math.max(r,i+1,s);if(W.has(n.orderingKey)&&W.delete(n.orderingKey),W.set(n.orderingKey,o),W.size>1024){let a=W.keys().next().value;a!==void 0&&W.delete(a)}return new Date(o).toISOString()}function rt(){W.clear()}0&&(module.exports={AgentType,AppSyncClient,AuthService,CryptoError,CryptoService,DeliveryStatus,ENCRYPTION_VERSION,EventSource,EventType,KeychainError,KeychainManager,Logger,PORT_RANGE_SIZE,PRIMARY_PORT,SessionStatus,_resetPrepareEventTimestampForTesting,authService,bindOAuthServer,createLogger,cryptoService,errorWasBeaconed,fireAuthCompletedBeacon,fireAuthFailedBeacon,getConfig,getEnvironment,getErrorReason,keychainManager,loadConfig,logger,markErrorBeaconed,mutations,normalizeSnapshot,parseInteractivePrompt,prepareEventTimestamp,prepareSessionEncryption,queries,registerDeviceEncryptionKey,rekeySessionForNewDevices,resumeOrCreateSession,runAuthCli,startDeviceKeyWatcher,subscriptions});
|
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
type BackendKind = 'keytar' | 'file';
|
|
2
|
+
/**
|
|
3
|
+
* Is the OS-native keyring service STRUCTURALLY UNREACHABLE in THIS process?
|
|
4
|
+
*
|
|
5
|
+
* - macOS / Windows: the OS keychain (Keychain / Credential Manager) is always
|
|
6
|
+
* present, so this is always false.
|
|
7
|
+
* - Linux: keytar/libsecret reaches the Secret Service over the D-Bus *session*
|
|
8
|
+
* bus, which it discovers via several mechanisms. On a truly headless box
|
|
9
|
+
* (Docker, CI, `ssh` without a GUI/forwarded bus) NONE of them resolve, so
|
|
10
|
+
* keytar's native addon LOADS but every operation throws ("Cannot autolaunch
|
|
11
|
+
* D-Bus without X11 $DISPLAY" / "no Secret Service"). We return true only when
|
|
12
|
+
* EVERY discovery signal is absent — being conservative so we never downgrade
|
|
13
|
+
* a machine where the keyring could actually work:
|
|
14
|
+
* 1. a GUI display (`DISPLAY` / `WAYLAND_DISPLAY`) — D-Bus can X11/Wayland-
|
|
15
|
+
* autolaunch a session bus + keyring from these even with no bus env set;
|
|
16
|
+
* 2. an explicit `DBUS_SESSION_BUS_ADDRESS`;
|
|
17
|
+
* 3. the per-user bus socket — canonically `$XDG_RUNTIME_DIR/bus`, usually
|
|
18
|
+
* `/run/user/<uid>/bus` under systemd; we check both.
|
|
19
|
+
*
|
|
20
|
+
* IMPORTANT (Stage 2 Codex): this returning true means "no OS keyring is
|
|
21
|
+
* REACHABLE in this process", NOT "no keytar identity has ever existed on this
|
|
22
|
+
* machine". The same machine could have a keytar identity from a prior desktop
|
|
23
|
+
* session (e.g. GUI login, then SSH in with no forwarded bus). Auto-selecting
|
|
24
|
+
* the file backend in that case would fork the device identity — so the caller
|
|
25
|
+
* gates the auto-fallback on a "keytar was used here" marker (see
|
|
26
|
+
* `autoFileOrRefuse`), refusing to silently downgrade a machine that has a real
|
|
27
|
+
* keyring identity. A transiently-locked desktop keyring still has a bus/display,
|
|
28
|
+
* so it never reaches the fallback at all.
|
|
29
|
+
*/
|
|
30
|
+
export declare function osKeyringStructurallyAbsent(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Do durable file-backend credentials already exist on this machine? True when
|
|
33
|
+
* ~/.codevibe holds any `<service>.json` credential file (written by a prior
|
|
34
|
+
* file-backend session — a headless auto-fallback OR an explicit opt-in run).
|
|
35
|
+
* The marker dotfiles (`.keyring-used`) are excluded by the `.json` test, and
|
|
36
|
+
* `<service>.json` is the ONLY `.json` we ever write into ~/.codevibe
|
|
37
|
+
* (writeFileStore → fileStorePath).
|
|
38
|
+
*
|
|
39
|
+
* This is the signal that closes the REVERSE fork: a machine that fell back to
|
|
40
|
+
* file storage and wrote its device identity there, now running in a session
|
|
41
|
+
* where the OS keyring is available again. Selecting keytar at that point would
|
|
42
|
+
* read an EMPTY keyring and mint a brand-new device identity — orphaning the
|
|
43
|
+
* existing file identity and breaking the user's E2E sessions. So when durable
|
|
44
|
+
* file creds exist we keep using the file backend instead of switching to keytar.
|
|
45
|
+
* Checked on every platform (the opt-in path can create file creds anywhere) and
|
|
46
|
+
* checked in initBackends right AFTER the explicit `CODEVIBE_ALLOW_FILE_KEYCHAIN=1`
|
|
47
|
+
* opt-in (which always routes to file) but BEFORE the keytar probe, so the no-fork
|
|
48
|
+
* guarantee holds whether or not keytar loads in this session.
|
|
49
|
+
*
|
|
50
|
+
* Exported for direct unit testing — the keytar load itself goes through an
|
|
51
|
+
* `eval('require')` that escapes the test mock registry, so the predicate is
|
|
52
|
+
* pinned down here rather than only through a (non-deterministic) init path.
|
|
53
|
+
*/
|
|
54
|
+
export declare function fileStoreHasCredentials(): boolean;
|
|
55
|
+
/** True if the file backend was auto-selected (no OS keyring service present). */
|
|
56
|
+
export declare function wasAutoFileFallback(): boolean;
|
|
57
|
+
/** The active backend kind, or null if not yet initialized. For tests / diagnostics. */
|
|
58
|
+
export declare function getActiveBackendKind(): BackendKind | null;
|
|
1
59
|
export declare function getPassword(service: string, account: string): Promise<string | null>;
|
|
2
60
|
export declare function setPassword(service: string, account: string, password: string): Promise<void>;
|
|
3
61
|
export declare function deletePassword(service: string, account: string): Promise<boolean>;
|
|
62
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-claude-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"node": ">=18.0.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@quantiya/codevibe-core": "^1.0.
|
|
50
|
+
"@quantiya/codevibe-core": "^1.0.32",
|
|
51
51
|
"dotenv": "^16.6.1",
|
|
52
52
|
"express": "^5.1.0",
|
|
53
53
|
"graphql": "^16.12.0",
|