@quantiya/codevibe-core 1.0.29 → 1.0.30

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.
@@ -304,6 +304,30 @@ export declare class AppSyncClient {
304
304
  * Send a single heartbeat update.
305
305
  */
306
306
  private sendHeartbeat;
307
+ /**
308
+ * Tear down the mobile-event subscription for a SINGLE session.
309
+ *
310
+ * Use this when a session ends or is superseded by a /resume while the
311
+ * daemon keeps running — cleanupSubscriptions() tears down ALL
312
+ * subscriptions and is only appropriate on full daemon shutdown.
313
+ *
314
+ * Without per-session teardown, an ended session's WebSocket survives in
315
+ * the still-running daemon. When AppSync idle-closes it (code 1000) the
316
+ * reconnect path in the start_ack handler re-runs startHeartbeat() and
317
+ * re-asserts status: ACTIVE — resurrecting the ended session as a
318
+ * duplicate row that the mobile app shows as a second live session.
319
+ * cleanupSubscriptionState() sets state.destroyed = true, which is exactly
320
+ * the flag the start_ack and 'close' handlers check to bail out, so this
321
+ * closes the resurrection path.
322
+ *
323
+ * (Confirmed in production logs 2026-06-04: a resumed-over Claude session
324
+ * was marked INACTIVE at 00:23:34 and resurrected to ACTIVE at 03:02:05
325
+ * when its leaked subscription reconnected after an AppSync code-1000
326
+ * close.)
327
+ *
328
+ * Idempotent: a no-op if no subscription is tracked for sessionId.
329
+ */
330
+ cleanupSubscription(sessionId: string): void;
307
331
  /**
308
332
  * Cleanup all subscriptions and heartbeats
309
333
  */
package/dist/index.js CHANGED
@@ -202,7 +202,7 @@ ${r.stack}`)):typeof r=="object"?o+=` ${JSON.stringify(r,qt)}`:o+=` ${r}`),o}log
202
202
  updatedAt
203
203
  }
204
204
  }
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})}}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(`
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(`
206
206
  <!DOCTYPE html>
207
207
  <html>
208
208
  <head><title>Success</title></head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "Core library for CodeVibe plugins - shared keychain, crypto, AppSync, and auth functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",