{"version":3,"sources":["jsdelivr-header.js","/npm/antigravity-manager@2.0.4/node_modules/tsup/assets/esm_shims.js","/npm/antigravity-manager@2.0.4/src/utils/errors.ts","/npm/antigravity-manager@2.0.4/src/protocols/gemini/converter.ts","/npm/antigravity-manager@2.0.4/src/protocols/gemini/types.ts","/npm/antigravity-manager@2.0.4/src/protocols/gemini/client.ts","/npm/antigravity-manager@2.0.4/src/index.ts","/npm/antigravity-manager@2.0.4/src/modules/account/manager.ts","/npm/antigravity-manager@2.0.4/src/database/repositories/account.ts","/npm/antigravity-manager@2.0.4/src/database/repositories/session.ts","/npm/antigravity-manager@2.0.4/src/modules/account/validator.ts","/npm/antigravity-manager@2.0.4/src/utils/crypto.ts","/npm/antigravity-manager@2.0.4/src/utils/logger.ts","/npm/antigravity-manager@2.0.4/src/modules/config/manager.ts","/npm/antigravity-manager@2.0.4/src/database/repositories/config.ts","/npm/antigravity-manager@2.0.4/src/modules/config/types.ts","/npm/antigravity-manager@2.0.4/src/modules/config/validator.ts","/npm/antigravity-manager@2.0.4/src/modules/token/manager.ts","/npm/antigravity-manager@2.0.4/src/modules/token/types.ts","/npm/antigravity-manager@2.0.4/src/modules/token/rate-limit.ts","/npm/antigravity-manager@2.0.4/src/modules/quota/monitor.ts","/npm/antigravity-manager@2.0.4/src/modules/quota/fetcher.ts","/npm/antigravity-manager@2.0.4/src/protocols/openai/handler.ts","/npm/antigravity-manager@2.0.4/src/protocols/openai/mapping.ts","/npm/antigravity-manager@2.0.4/src/server/index.ts","/npm/antigravity-manager@2.0.4/src/server/routes/openai.ts","/npm/antigravity-manager@2.0.4/src/server/routes/metrics.ts","/npm/antigravity-manager@2.0.4/src/modules/monitoring/metrics.ts","/npm/antigravity-manager@2.0.4/src/database/index.ts","/npm/antigravity-manager@2.0.4/src/database/schema.ts"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,+HCLO,OAAU,+BACR,OAAqB,MAF9B,ICGa,EA0BA,GAUA,EAUA,EAUA,EAUA,EAUA,EAUA,GAUA,GAUA,GAUA,GDvHb,EAAA,GAAA,SCAA,EAAA,GAAA,kBAAA,IAGa,EAAN,cAA+B,MACpC,WAAA,CACE,EACgB,EACA,EAAqB,IACrB,GAEhB,MAAM,GAJU,KAAA,OACA,KAAA,aACA,KAAA,UAGhB,KAAK,KAAO,mBACZ,MAAM,kBAAkB,KAAM,KAAK,YACrC,CAEA,MAAA,GACE,MAAO,CACL,KAAM,KAAK,KACX,QAAS,KAAK,QACd,KAAM,KAAK,KACX,WAAY,KAAK,WACjB,QAAS,KAAK,QAElB,GAMW,GAAN,cAAyB,EAC9B,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,cAAe,IAAK,GACnC,KAAK,KAAO,YACd,GAMW,EAAN,cAA2B,EAChC,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,gBAAiB,IAAK,GACrC,KAAK,KAAO,cACd,GAMW,EAAN,cAA4B,EACjC,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,YAAa,IAAK,GACjC,KAAK,KAAO,eACd,GAMW,EAAN,cAA8B,EACnC,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,mBAAoB,IAAK,GACxC,KAAK,KAAO,iBACd,GAMW,EAAN,cAA4B,EACjC,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,iBAAkB,IAAK,GACtC,KAAK,KAAO,eACd,GAMW,EAAN,cAA0B,EAC/B,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,eAAgB,IAAK,GACpC,KAAK,KAAO,aACd,GAMW,GAAN,cAAyB,EAC9B,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,cAAe,IAAK,GACnC,KAAK,KAAO,YACd,GAMW,GAAN,cAAyB,EAC9B,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,cAAe,IAAK,GACnC,KAAK,KAAO,YACd,GAMW,GAAN,cAAyB,EAC9B,WAAA,CAAY,EAAiB,EAAqB,IAAK,GACrD,MAAM,EAAS,cAAe,EAAY,GAC1C,KAAK,KAAO,YACd,GAMW,GAAN,cAA6B,EAClC,WAAA,CAAY,EAAiB,GAC3B,MAAM,EAAS,mBAAoB,IAAK,GACxC,KAAK,KAAO,gBACd,EACF,IC5HA,GAAA,GAAA,GAAA,GAAA,CAAA,0BAAA,IAAA,GAAA,2BAAA,IAAA,GAAA,uBAAA,IAAA,0BAKS,OAAmB,SAiBrB,SAAS,EACd,GAEA,IAAM,EAAW,EAAQ,SAGnB,EAA2B,GAC7B,EAAoB,EAExB,QAAS,EAAI,EAAG,EAAI,EAAS,QACD,WAAtB,EAAS,GAAI,KADkB,IACC,CAClC,IAAM,EAAU,EAAS,GAAI,QAC7B,EAAe,KAAwB,iBAAZ,EAAuB,EAAU,IAC5D,EAAoB,EAAI,CAC1B,CAMF,IAIM,EAA8C,CAClD,SAJqB,GADM,EAAS,MAAM,KAS5C,OAAI,EAAe,OAAS,IAC1B,EAAc,kBAAoB,CAChC,MAAO,EAAe,KAAK,KAAY,aAK3C,EAAc,iBAAmB,CAC/B,YAAa,EAAQ,YACrB,KAAM,EAAQ,MACd,gBAAiB,EAAQ,WACzB,cAAe,MAAM,QAAQ,EAAQ,MACjC,EAAQ,KACR,EAAQ,KACN,CAAC,EAAQ,WACT,GAGD,CACT,CAMA,SAAS,GAAgB,GACvB,IAAM,EAAkC,GAExC,QAAW,KAAW,EAAU,CAC9B,IAAM,EAAO,GAAQ,EAAQ,MACvB,EAAQ,GAAsB,GAG9B,EAAc,EAAe,EAAe,OAAS,GACvD,GAAe,EAAY,OAAS,EAEtC,EAAY,MAAM,QAAQ,GAG1B,EAAe,KAAK,CAAE,OAAM,SAEhC,CAEA,OAAO,CACT,CAKA,SAAS,GAAQ,GACf,MAAa,cAAT,EACK,QAGF,MACT,CAKA,SAAS,GAAsB,GAC7B,IAAM,EAAsB,GAU5B,GAP+B,iBAApB,EAAQ,SACb,EAAQ,SACV,EAAM,KAAK,CAAE,KAAM,EAAQ,UAKV,cAAjB,EAAQ,MAAwB,eAAgB,GAAW,EAAQ,WACrE,QAAW,KAAY,EAAQ,WACP,aAAlB,EAAS,MACX,EAAM,KAAK,CACT,KAAM,kBAAkB,EAAS,SAAS,QAAQ,EAAS,SAAS,eAO5E,GAAqB,SAAjB,EAAQ,MAAmB,iBAAkB,EAAS,CACxD,IAAM,EAAqC,iBAApB,EAAQ,QAAuB,EAAQ,QAAU,GACxE,EAAM,KAAK,CACT,KAAM,oBAAoB,KAE9B,CAGA,OAAqB,IAAjB,EAAM,QACR,EAAM,KAAK,CAAE,KAAM,KAGd,CACT,CAKO,SAAS,GACd,EACA,EACA,GAEA,IAAM,EAAY,EAAe,WAAW,GAE5C,IAAK,EACH,MAAM,IAAI,MAAM,oCAIlB,IAAM,EAAU,GAAmB,EAAU,QAAQ,OAG/C,EAAe,GAAgB,EAAU,cAK/C,MAAO,CACL,GAHS,GAAa,YAAY,GAAY,IAAI,SAAS,SAI3D,OAAQ,kBACR,QAAS,KAAK,MAAM,KAAK,MAAQ,KACjC,QACA,QAAS,CACP,CACE,MAAO,EACP,QAAS,CACP,KAAM,YACN,WAEF,cAAe,IAGnB,MAAO,CACL,cAAe,EAAe,eAAe,kBAAoB,EACjE,kBAAmB,EAAe,eAAe,sBAAwB,EACzE,aAAc,EAAe,eAAe,iBAAmB,GAGrE,CAKA,SAAS,GAAmB,GAC1B,OAAO,EAAM,KAAK,GAAS,EAAK,MAAQ,KAAI,KAAK,GACnD,CAKA,SAAS,GACP,GAEA,OAAQ,GACN,IAAK,OAOL,IAAK,QACH,MAAO,OANT,IAAK,aACH,MAAO,SACT,IAAK,SACL,IAAK,aACH,MAAO,iBAGT,QACE,OAAO,KAEb,CAKO,SAAS,GACd,EACA,EACA,EACA,GAEA,IAAM,EAAY,EAAY,WAAW,GAEnC,EAA6B,CACjC,KACA,OAAQ,wBACR,QAAS,KAAK,MAAM,KAAK,MAAQ,KACjC,QACA,QAAS,CACP,CACE,MAAO,EACP,MAAO,CAAC,EACR,cAAe,QAKrB,IAAK,EACH,OAAO,EAST,GALI,IACF,EAAM,QAAQ,GAAI,MAAM,KAAO,aAI7B,EAAU,SAAS,OAAS,EAAU,QAAQ,MAAM,OAAS,EAAG,CAClE,IAAM,EAAU,GAAmB,EAAU,QAAQ,OACjD,IACF,EAAM,QAAQ,GAAI,MAAM,QAAU,EAEtC,CAGA,OAAI,EAAU,eACZ,EAAM,QAAQ,GAAI,cAAgB,GAAgB,EAAU,eAGvD,CACT,CA9QA,IC4Ea,ED5Eb,EAAA,GAAA,kBAAA,OCAA,GAAA,GAAA,kBAAA,IA4Ea,EAAmB,CAC9B,KAAM,mDACN,MAAO,oDACT,IC/EA,GAAA,GAAA,GAAA,GAAA,CAAA,aAAA,IAAA,IAAA,IASa,EATb,EAAA,GAAA,kBAAA,IAAA,IACA,KAQa,EAAN,MACL,WAAA,CAAoB,GAAA,KAAA,SAA+B,CAEnD,qBAAM,CAAgB,GAEpB,IAAM,EAAM,GAD+B,UAA1B,KAAK,QAAQ,SAAuB,EAAiB,MAAQ,EAAiB,eAC7D,KAAK,QAAQ,wBACzC,QAAiB,MAAM,EAAK,CAChC,OAAQ,OACR,QAAS,CAAE,cAAiB,UAAU,KAAK,QAAQ,cAAe,eAAgB,oBAClF,KAAM,KAAK,UAAU,KAEvB,IAAK,EAAS,GACZ,MAAM,IAAI,EAAiB,qBAAqB,EAAS,SAAU,oBAErE,aAAc,EAAS,MACzB,CAEA,2BAAO,CAAsB,GAE3B,IAAM,EAAM,GAD+B,UAA1B,KAAK,QAAQ,SAAuB,EAAiB,MAAQ,EAAiB,eAC7D,KAAK,QAAQ,sCACzC,QAAY,MAAM,EAAK,CAC3B,OAAQ,OACR,QAAS,CAAE,cAAiB,UAAU,KAAK,QAAQ,cAAe,eAAgB,oBAClF,KAAM,KAAK,UAAU,KAEvB,IAAK,EAAI,GAAI,MAAM,IAAI,EAAiB,wBAAwB,EAAI,SAAU,uBAC9E,IAAK,EAAI,KAAM,MAAM,IAAI,EAAiB,mBAAoB,kBAE9D,IAAM,EAAS,EAAI,KAAK,YAClB,EAAU,IAAI,YAChB,EAAS,GAEb,OAAa,CACX,IAAQ,OAAM,eAAgB,EAAO,OACrC,GAAI,EAAM,MACV,GAAU,EAAQ,OAAO,EAAO,CAAE,QAAQ,IAC1C,IAAM,EAAQ,EAAO,MAAM,MAC3B,EAAS,EAAM,OAAS,GACxB,QAAW,KAAQ,EACjB,GAAI,EAAK,WAAW,UAAW,CAC7B,IAAM,EAAO,EAAK,MAAM,GACxB,GAAI,IAAS,EAAK,WAAW,MAC3B,UACgB,KAAK,MAAM,EAE3B,OAEA,CAEJ,CAEJ,CACF,EACF,IC9DA,ICAA,0BAAS,OAAmB,SCA5B,IAEA,IAmBO,IAAM,EAAN,MACL,WAAA,CAAoB,GAAA,KAAA,IAAS,CAK7B,QAAA,CAAS,GACP,IAEE,OADa,KAAK,GAAG,QAAQ,uCACjB,IAAI,EAClB,OAAS,GACP,MAAM,IAAI,EAAc,+BAAgC,CAAE,QAAO,MACnE,CACF,CAKA,WAAA,CAAY,EAAe,GACzB,IAIE,OAHa,KAAK,GAAG,QACnB,2DAEU,IAAI,EAAO,EACzB,OAAS,GACP,MAAM,IAAI,EAAc,kCAAmC,CAAE,QAAO,QAAO,YAC7E,CACF,CAKA,OAAA,GACE,IAEE,OADa,KAAK,GAAG,QAAQ,mDACjB,KACd,OAAS,GACP,MAAM,IAAI,EAAc,8BAA+B,CAAE,SAC3D,CACF,CAKA,cAAA,CAAe,GACb,IAIE,OAHa,KAAK,GAAG,QACnB,sEAEU,IAAI,EAClB,OAAS,GACP,MAAM,IAAI,EAAc,sCAAuC,CAAE,QAAO,YAC1E,CACF,CAKA,MAAA,CAAO,GACL,IACE,IAAM,EAAM,KAAK,MACJ,KAAK,GAAG,QAAQ,8JAKxB,IACH,EAAQ,GACR,EAAQ,SACR,EAAQ,MACR,EAAQ,OACR,EAAQ,OAAS,KACjB,EACA,GAGF,IAAM,EAAU,KAAK,SAAS,EAAQ,IACtC,IAAK,EACH,MAAM,IAAI,EAAc,qCAAsC,CAAE,GAAI,EAAQ,KAE9E,OAAO,CACT,OAAS,GACP,MAAM,IAAI,EAAc,2BAA4B,CAAE,QAAO,WAC/D,CACF,CAKA,MAAA,CAAO,EAAY,GACjB,IACE,IAAM,EAAoB,GACpB,EAAgB,GAmBtB,QAjBoB,IAAhB,EAAK,SACP,EAAQ,KAAK,cACb,EAAO,KAAK,EAAK,cAEA,IAAf,EAAK,QACP,EAAQ,KAAK,aACb,EAAO,KAAK,EAAK,aAEI,IAAnB,EAAK,YACP,EAAQ,KAAK,iBACb,EAAO,KAAK,EAAK,YAGnB,EAAQ,KAAK,kBACb,EAAO,KAAK,KAAK,OACjB,EAAO,KAAK,GAEW,IAAnB,EAAQ,OAAc,CAExB,IAAM,EAAW,KAAK,SAAS,GAC/B,IAAK,EACH,MAAM,IAAI,EAAc,oBAAqB,CAAE,OAEjD,OAAO,CACT,CAOA,GAAuB,IALV,KAAK,GAAG,QACnB,uBAAuB,EAAQ,KAAK,sBAElB,OAAO,GAEhB,QACT,MAAM,IAAI,EAAc,oBAAqB,CAAE,OAGjD,IAAM,EAAU,KAAK,SAAS,GAC9B,IAAK,EACH,MAAM,IAAI,EAAc,qCAAsC,CAAE,OAElE,OAAO,CACT,OAAS,GACP,MAAI,aAAiB,EAAqB,EACpC,IAAI,EAAc,2BAA4B,CAAE,QAAO,KAAI,QACnE,CACF,CAKA,OAAO,GACL,IAIE,GAAuB,IAHV,KAAK,GAAG,QAAQ,qCACT,IAAI,GAEb,QACT,MAAM,IAAI,EAAc,oBAAqB,CAAE,MAEnD,OAAS,GACP,MAAI,aAAiB,EAAqB,EACpC,IAAI,EAAc,2BAA4B,CAAE,QAAO,MAC/D,CACF,CAKA,KAAA,GACE,IAGE,OAFa,KAAK,GAAG,QAAQ,0CACT,MACN,KAChB,OAAS,GACP,MAAM,IAAI,EAAc,2BAA4B,CAAE,SACxD,CACF,CAKA,cAAA,CAAe,GACb,IACe,KAAK,GAAG,QAAQ,kDACxB,IAAI,KAAK,MAAO,EACvB,OAAS,GACP,MAAM,IAAI,EAAc,6BAA8B,CAAE,QAAO,MACjE,CACF,GCzMF,IAEA,IAYO,IAAM,EAAN,MACL,WAAA,CAAoB,GAAA,KAAA,IAAS,CAK7B,QAAA,CAAS,GACP,IAEE,OADa,KAAK,GAAG,QAAQ,uCACjB,IAAI,EAClB,OAAS,GACP,MAAM,IAAI,EAAc,+BAAgC,CAAE,QAAO,MACnE,CACF,CAKA,iBAAA,CAAkB,GAChB,IAIE,OAHa,KAAK,GAAG,QACnB,oGAEU,IAAI,EAAa,KAAK,MACpC,OAAS,GACP,MAAM,IAAI,EAAc,wCAAyC,CAAE,QAAO,eAC5E,CACF,CAKA,eAAA,CAAgB,GACd,IAIE,OAHa,KAAK,GAAG,QACnB,wEAEU,IAAI,EAClB,OAAS,GACP,MAAM,IAAI,EAAc,wCAAyC,CAAE,QAAO,aAC5E,CACF,CAKA,MAAA,CAAO,GACL,IACE,IAAM,EAAM,KAAK,MACJ,KAAK,GAAG,QAAQ,gIAKxB,IACH,EAAQ,GACR,EAAQ,YACR,EAAQ,WACR,EACA,EAAQ,YAGV,IAAM,EAAU,KAAK,SAAS,EAAQ,IACtC,IAAK,EACH,MAAM,IAAI,EAAc,qCAAsC,CAAE,GAAI,EAAQ,KAE9E,OAAO,CACT,OAAS,GACP,MAAM,IAAI,EAAc,2BAA4B,CAAE,QAAO,WAC/D,CACF,CAKA,OAAO,GACL,IAIE,GAAuB,IAHV,KAAK,GAAG,QAAQ,qCACT,IAAI,GAEb,QACT,MAAM,IAAI,EAAc,oBAAqB,CAAE,MAEnD,OAAS,GACP,MAAI,aAAiB,EAAqB,EACpC,IAAI,EAAc,2BAA4B,CAAE,QAAO,MAC/D,CACF,CAKA,iBAAA,CAAkB,GAChB,IACe,KAAK,GAAG,QAAQ,6CACxB,IAAI,EACX,OAAS,GACP,MAAM,IAAI,EAAc,0CAA2C,CAAE,QAAO,aAC9E,CACF,CAKA,aAAA,GACE,IAGE,OAFa,KAAK,GAAG,QAAQ,8CACT,IAAI,KAAK,OACf,OAChB,OAAS,GACP,MAAM,IAAI,EAAc,oCAAqC,CAAE,SACjE,CACF,CAKA,KAAA,GACE,IAGE,OAFa,KAAK,GAAG,QAAQ,+DACT,IAAI,KAAK,OACf,KAChB,OAAS,GACP,MAAM,IAAI,EAAc,2BAA4B,CAAE,SACxD,CACF,GC3IF,IAEA,gBAFS,MAAS,MAOlB,IAAM,GAAoB,EAAE,OAAO,CACjC,aAAc,EAAE,SAAS,IAAI,EAAG,4BAChC,cAAe,EAAE,SAAS,WAC1B,WAAY,EAAE,SAAS,WACvB,WAAY,EAAE,SAAS,WACvB,MAAO,EAAE,SAAS,aAMd,GAAqB,EAAE,OAAO,CAClC,eAAgB,EAAE,SAAS,WAC3B,eAAgB,EAAE,SAAS,WAC3B,qBAAsB,EAAE,SAAS,WACjC,iBAAkB,EAAE,SAAS,WAC7B,iBAAkB,EAAE,SAAS,WAC7B,uBAAwB,EAAE,SAAS,WACnC,wBAAyB,EAAE,SAAS,WACpC,0BAA2B,EAAE,SAAS,WACtC,iBAAkB,EAAE,SAAS,WAC7B,eAAgB,EAAE,SAAS,aAMvB,GAAsB,EAAE,OAAO,CACnC,SAAU,EAAE,KAAK,CAAC,SAAU,cAC5B,MAAO,EAAE,SAAS,MAAM,yBACxB,OAAQ,GACR,MAAO,GAAmB,aAMtB,GAAsB,EAAE,OAAO,CACnC,OAAQ,GAAkB,WAC1B,MAAO,GAAmB,aAiErB,SAAS,GAAsB,GACpC,IACE,GAAoB,MAAM,EAC5B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,uBAAwB,CAAE,UACtD,CACA,MAAM,CACR,CACF,CAKO,SAAS,GAAsB,GACpC,IACE,GAAoB,MAAM,EAC5B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,sBAAuB,CAAE,UACrD,CACA,MAAM,CACR,CACF,CAKO,SAAS,GAAa,GAC3B,IAAK,EAAQ,OAAO,WAElB,OAAO,EAKT,OAAO,EAAQ,OAAO,WAAa,KAAK,MADzB,GAEjB,CAKO,SAAS,GAAS,EAAY,EAA0B,GAE7D,IAAK,EAAO,OAAO,EAGnB,IAAI,GAAmB,EACnB,GAAoB,EAGxB,YAAsC,IAAlC,EAAM,0BACR,GAAmB,EACf,EAAM,wBAA0B,GAAK,EAAM,yBAA2B,IACxE,GAAoB,SAKW,IAA/B,EAAM,uBACR,GAAmB,EACf,EAAM,qBAAuB,IAC/B,GAAoB,SAKa,IAAjC,EAAM,yBACR,GAAmB,EACf,EAAM,uBAAyB,IACjC,GAAoB,KAMjB,GAAmB,CAC5B,CC9LA,6BAAS,uBAAgB,kBAAkB,iBAAa,OAAkB,SAE1E,IAAM,GAAY,cACZ,GAAY,GACZ,GAAc,GAKb,SAAS,EAAQ,EAAc,GACpC,IAAM,EAAO,GAAY,IACnB,EAAa,GAAU,EAAK,GAC5B,EAAK,GAAY,IACjB,EAAS,GAAe,GAAW,EAAY,GAEjD,EAAY,EAAO,OAAO,EAAM,OAAQ,OAC5C,GAAa,EAAO,MAAM,OAE1B,IAAM,EAAU,EAAO,aAGvB,MAAO,CACL,EAAK,SAAS,OACd,EAAG,SAAS,OACZ,EAAQ,SAAS,OACjB,GACA,KAAK,IACT,CAKO,SAAS,GAAQ,EAAuB,GAC7C,IAAM,EAAQ,EAAc,MAAM,KAClC,GAAqB,IAAjB,EAAM,OACR,MAAM,IAAI,MAAM,iCAGlB,IAAO,EAAS,EAAO,EAAY,GAAa,EAChD,KAAK,GAAY,GAAU,GAAe,GACxC,MAAM,IAAI,MAAM,iCAGlB,IAAM,EAAO,OAAO,KAAK,EAAS,OAC5B,EAAK,OAAO,KAAK,EAAO,OACxB,EAAU,OAAO,KAAK,EAAY,OAClC,EAAa,GAAU,EAAK,GAE5B,EAAW,GAAiB,GAAW,EAAY,GACzD,EAAS,WAAW,GAEpB,IAAI,EAAY,EAAS,OAAO,EAAW,MAAO,QAClD,OAAA,GAAa,EAAS,MAAM,QAErB,CACT,CAKA,SAAS,GAAU,EAAkB,GACnC,OAAO,GAAW,UACf,OAAO,GACP,OAAO,GACP,QACL,CJ9CA,IKnBA,WAAO,MAAoC,OAapC,SAAS,GAAa,EAAuB,CAAC,GACnD,IACE,QAAS,QAAQ,IAAI,WAA0B,OAC/C,SAAkC,eAAzB,QAAQ,IAAI,SACrB,OAAO,eACL,EAEE,EAA8B,CAClC,OACA,QACA,UAAW,EAAK,iBAAiB,QACjC,WAAY,CACV,MAAQ,KAAa,MAAO,MAIhC,OAAI,EACK,EACL,EACA,EAAK,UAAU,CACb,OAAQ,cACR,QAAS,CACP,UAAU,EACV,cAAe,wBACf,OAAQ,eACR,YAAY,MAMb,EAAK,EACd,CAKA,IAAI,EAAkC,KAK/B,SAAS,EAAU,GACxB,OAAK,IACH,EAAe,GAAa,IAEvB,CACT,CAKO,SAAS,GAAU,GACxB,EAAe,CACjB,CAKO,SAAS,GACd,EACA,GAEA,OAAO,EAAO,MAAM,EACtB,CLhDO,IAAM,GAAN,MAKL,WAAA,CACE,EACQ,GAAA,KAAA,SAER,KAAK,YAAc,IAAI,EAAkB,GACzC,KAAK,YAAc,IAAI,EAAkB,EAC3C,CAVQ,YACA,YACA,OAAS,IAAY,MAAM,CAAE,OAAQ,mBAa7C,mBAAM,CAAc,GAMlB,GAJA,GAAsB,GAGL,KAAK,YAAY,YAAY,EAAM,MAAO,EAAM,UAE/D,MAAM,IAAI,EAAa,yBAA0B,CAC/C,MAAO,EAAM,MACb,SAAU,EAAM,WAKpB,IAAM,EAAK,GAAY,IAAI,SAAS,OAG9B,EAAkB,EACtB,KAAK,UAAU,EAAM,QACrB,KAAK,OAAO,eAIR,EAAY,KAAK,YAAY,OAAO,CACxC,KACA,SAAU,EAAM,SAChB,MAAO,EAAM,MACb,OAAQ,EACR,MAAO,KAAK,UAAU,EAAM,OAAS,CAAC,KAGxC,OAAA,KAAK,OAAO,KAAK,CAAE,UAAW,EAAI,SAAU,EAAM,SAAU,MAAO,EAAM,OAAS,mBAE3E,KAAK,mBAAmB,EACjC,CAKA,cAAA,CAAe,GACb,IAAM,EAAY,KAAK,YAAY,SAAS,GAC5C,OAAK,EAEE,KAAK,mBAAmB,GAFR,IAGzB,CAKA,iBAAA,CAAkB,EAAe,GAC/B,IAAM,EAAY,KAAK,YAAY,YAAY,EAAO,GACtD,OAAK,EAEE,KAAK,mBAAmB,GAFR,IAGzB,CAKA,cAAA,GAEE,OADmB,KAAK,YAAY,UAClB,KAAI,GAAO,KAAK,mBAAmB,IACvD,CAKA,qBAAA,CAAsB,GAEpB,OADmB,KAAK,YAAY,eAAe,GACjC,KAAI,GAAO,KAAK,mBAAmB,IACvD,CAKA,oBAAA,GAEE,OADiB,KAAK,iBACN,QAAO,IACrB,IAAM,EAAa,GAAa,GAC1B,EAAiB,GAAS,EAAI,OACpC,OAAO,GAAc,IAEzB,CAKA,mBAAM,CAAc,EAAY,GAM9B,GAJA,GAAsB,IAGL,KAAK,YAAY,SAAS,GAEzC,MAAM,IAAI,EAAc,oBAAqB,CAAE,OAGjD,IAAM,EAAkB,CAAC,EAGrB,EAAM,SACR,EAAW,OAAS,EAClB,KAAK,UAAU,EAAM,QACrB,KAAK,OAAO,gBAKZ,EAAM,QACR,EAAW,MAAQ,KAAK,UAAU,EAAM,QAI1C,IAAM,EAAU,KAAK,YAAY,OAAO,EAAI,GAE5C,OAAA,KAAK,OAAO,KAAK,CAAE,UAAW,GAAM,mBAE7B,KAAK,mBAAmB,EACjC,CAKA,kBAAM,CAAa,EAAY,GAC7B,OAAO,KAAK,cAAc,EAAI,CAAE,UAClC,CAKA,iBAAM,CAAY,EAAY,GAC5B,OAAO,KAAK,cAAc,EAAI,CAAE,SAClC,CAKA,eAAA,CAAgB,GACd,KAAK,YAAY,eAAe,EAClC,CAKA,mBAAM,CAAc,GAElB,KAAK,YAAY,kBAAkB,GAGnC,KAAK,YAAY,OAAO,GAExB,KAAK,OAAO,KAAK,CAAE,UAAW,GAAM,kBACtC,CAKA,eAAA,GACE,IAAM,EAAQ,KAAK,YAAY,QACzB,EAAiB,KAAK,YAAY,eAAe,UACjD,EAAoB,KAAK,YAAY,eAAe,aACpD,EAAiB,KAAK,YAAY,QAIlC,EADc,KAAK,YAAY,UACN,QAAO,CAAC,EAAK,IACrC,EAAI,UACF,KAAK,IAAI,EAAK,EAAI,WADE,GAE1B,GAEH,MAAO,CACL,QACA,YAAa,CACX,OAAQ,EAAe,OACvB,UAAW,EAAkB,QAE/B,gBAAiB,EACjB,aAAc,GAAc,KAEhC,CAKA,cAAA,GACE,OAAO,KAAK,gBACd,CAKA,oBAAM,CAAe,GACnB,IAAI,EAAW,EAEf,QAAW,KAAW,EACpB,IAGE,GADiB,KAAK,YAAY,YAAY,EAAQ,MAAO,EAAQ,UACvD,CACZ,KAAK,OAAO,KACV,CAAE,MAAO,EAAQ,MAAO,SAAU,EAAQ,UAC1C,oCAEF,QACF,OAEM,KAAK,cAAc,GACzB,GACF,OAAS,GACP,KAAK,OAAO,MAAM,CAAE,QAAO,QAAS,EAAQ,OAAS,2BACvD,CAGF,OAAA,KAAK,OAAO,KAAK,CAAE,WAAU,MAAO,EAAS,QAAU,qBAChD,CACT,CAKQ,kBAAA,CAAmB,GACzB,IAAI,EASA,EARJ,IACE,IAAM,EAAkB,GAAQ,EAAU,OAAQ,KAAK,OAAO,eAC9D,EAAS,KAAK,MAAM,EACtB,OAAS,GACP,MAAA,KAAK,OAAO,MAAM,CAAE,QAAO,UAAW,EAAU,IAAM,4BAChD,IAAI,EAAa,mCAAoC,CAAE,UAAW,EAAU,IACpF,CAGA,IACE,EAAQ,KAAK,MAAM,EAAU,OAAS,KACxC,OAAS,GACP,KAAK,OAAO,MAAM,CAAE,QAAO,UAAW,EAAU,IAAM,yBACtD,EAAQ,CAAC,CACX,CAEA,MAAO,CACL,GAAI,EAAU,GACd,SAAU,EAAU,SACpB,MAAO,EAAU,MACjB,SACA,QACA,WAAY,EAAU,WACtB,WAAY,EAAU,WACtB,UAAW,EAAU,UAEzB,GMrSF,yBAAS,mBAAY,oBAAc,gBAAe,OAAiB,oBAC1D,cAAM,OAAe,yBACrB,OAAe,KCFxB,IAEA,IAKO,IAAM,EAAN,MACL,WAAA,CAAoB,GAAA,KAAA,IAAS,CAK7B,GAAA,CAAI,GACF,IAGE,OAFa,KAAK,GAAG,QAAQ,0CACT,IAAI,IACT,OAAS,IAC1B,OAAS,GACP,MAAM,IAAI,EAAc,uBAAwB,CAAE,QAAO,OAC3D,CACF,CAKA,OAAA,CAAW,GACT,IAAM,EAAQ,KAAK,IAAI,GACvB,IAAK,EAAO,OAAO,KACnB,IACE,OAAO,KAAK,MAAM,EACpB,OAAS,GACP,MAAM,IAAI,EAAc,8BAA+B,CAAE,QAAO,OAClE,CACF,CAKA,GAAA,CAAI,EAAa,GACf,IACe,KAAK,GAAG,QAAQ,kNAOxB,IAAI,EAAK,EAAO,KAAK,MAC5B,OAAS,GACP,MAAM,IAAI,EAAc,uBAAwB,CAAE,QAAO,OAC3D,CACF,CAKA,OAAA,CAAQ,EAAa,GACnB,IACE,KAAK,IAAI,EAAK,KAAK,UAAU,GAC/B,OAAS,GACP,MAAM,IAAI,EAAc,4BAA6B,CAAE,QAAO,OAChE,CACF,CAKA,OAAO,GACL,IAIE,GAAuB,IAHV,KAAK,GAAG,QAAQ,oCACT,IAAI,GAEb,QACT,MAAM,IAAI,EAAc,uBAAwB,CAAE,OAEtD,OAAS,GACP,MAAI,aAAiB,EAAqB,EACpC,IAAI,EAAc,0BAA2B,CAAE,QAAO,OAC9D,CACF,CAKA,MAAA,GACE,IAEE,OADa,KAAK,GAAG,QAAQ,qCACjB,KACd,OAAS,GACP,MAAM,IAAI,EAAc,2BAA4B,CAAE,SACxD,CACF,CAKA,GAAA,CAAI,GACF,IAEE,YAAyB,IADZ,KAAK,GAAG,QAAQ,sCACjB,IAAI,EAClB,OAAS,GACP,MAAM,IAAI,EAAc,6BAA8B,CAAE,QAAO,OACjE,CACF,CAKA,KAAA,GACE,IACE,KAAK,GAAG,KAAK,qBACf,OAAS,GACP,MAAM,IAAI,EAAc,yBAA0B,CAAE,SACtD,CACF,GCnHF,IAuCO,IAAM,EAA4B,CACvC,OAAQ,CACN,KAAM,KACN,KAAM,YACN,KAAM,CACJ,QAAS,CAAC,qBAAsB,sBAChC,aAAa,IAGjB,MAAO,CACL,sBAAuB,IACvB,eAAgB,IAChB,cAAe,EACf,eAAe,GAEjB,MAAO,CACL,iBAAkB,uBAClB,oBAAqB,IACrB,gBAAiB,MAEnB,MAAO,CACL,cAAe,IACf,iBAAkB,GAClB,kBAAkB,IC9DtB,IACA,gBADS,MAAS,MAMlB,IAAM,GAAqB,EAAE,OAAO,CAClC,KAAM,EAAE,SAAS,MAAM,IAAI,GAAG,IAAI,OAClC,KAAM,EAAE,SAAS,IAAI,GACrB,KAAM,EACH,OAAO,CACN,QAAS,EAAE,MAAM,EAAE,UACnB,YAAa,EAAE,YAEhB,aAMC,GAAoB,EAAE,OAAO,CACjC,sBAAuB,EAAE,SAAS,MAAM,IAAI,GAC5C,eAAgB,EAAE,SAAS,MAAM,IAAI,KACrC,cAAe,EAAE,SAAS,MAAM,IAAI,GAAG,IAAI,IAC3C,cAAe,EAAE,YAMb,GAAoB,EAAE,OAAO,CACjC,iBAAkB,EAAE,KAAK,CAAC,cAAe,uBAAwB,WACjE,oBAAqB,EAAE,SAAS,MAAM,IAAI,KAC1C,gBAAiB,EAAE,SAAS,MAAM,IAAI,OAMlC,GAAoB,EAAE,OAAO,CACjC,cAAe,EAAE,SAAS,MAAM,IAAI,KACpC,iBAAkB,EAAE,SAAS,IAAI,GAAG,IAAI,GACxC,iBAAkB,EAAE,YAMhB,GAAkB,EAAE,OAAO,CAC/B,OAAQ,GACR,MAAO,GACP,MAAO,GACP,MAAO,KAMF,SAAS,GAAqB,GACnC,IACE,GAAmB,MAAM,EAC3B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,+BAAgC,CAAE,UAC9D,CACA,MAAM,CACR,CACF,CAKO,SAAS,GAAoB,GAClC,IACE,GAAkB,MAAM,EAC1B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,8BAA+B,CAAE,UAC7D,CACA,MAAM,CACR,CACF,CAKO,SAAS,GAAoB,GAClC,IACE,GAAkB,MAAM,EAC1B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,8BAA+B,CAAE,UAC7D,CACA,MAAM,CACR,CACF,CAKO,SAAS,GAAoB,GAClC,IACE,GAAkB,MAAM,EAC1B,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,8BAA+B,CAAE,UAC7D,CACA,MAAM,CACR,CACF,CAKO,SAAS,EAAkB,GAChC,IACE,GAAgB,MAAM,EACxB,OAAS,GACP,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAS,EAAM,OAAO,KAAI,GAAK,GAAG,EAAE,KAAK,KAAK,SAAS,EAAE,YAC/D,MAAM,IAAI,EAAgB,oCAAqC,CAAE,UACnE,CACA,MAAM,CACR,CACF,CHnHA,IAWO,IAAM,GAAN,MACG,WACA,OAAS,IAAY,MAAM,CAAE,OAAQ,kBACrC,WACA,YACA,aAAiC,KAEzC,WAAA,CAAY,EAAQ,EAAgC,CAAC,GACnD,KAAK,WAAa,IAAI,EAAiB,GACvC,KAAK,WAAa,EAAQ,YAAc,GAAK,KAAW,eAAgB,eACxE,KAAK,YAAc,EAAQ,cAAe,CAC5C,CAKA,UAAA,GACE,GAAI,KAAK,aACP,OAAO,KAAK,aAGd,IAAI,EAEJ,OACE,EADE,KAAK,YACE,KAAK,mBAEL,KAAK,eAIhB,EAAkB,GAElB,KAAK,aAAe,EACpB,KAAK,OAAO,KAAK,wBACV,CACT,CAKA,UAAA,CAAW,GAET,EAAkB,GAEd,KAAK,aACP,KAAK,eAAe,GAGtB,KAAK,WAAW,GAChB,KAAK,aAAe,EACpB,KAAK,OAAO,KAAK,sBACnB,CAKA,SAAA,GACE,OAAK,KAAK,aAGH,KAAK,aAFH,KAAK,YAGhB,CAKA,kBAAA,CAAmB,GACjB,IAAM,EAAU,KAAK,YACf,EAAU,IAAK,EAAS,OAAQ,IAAK,EAAQ,UAAW,IAC9D,GAAqB,EAAQ,QAC7B,KAAK,WAAW,EAClB,CAKA,iBAAA,CAAkB,GAChB,IAAM,EAAU,KAAK,YACf,EAAU,IAAK,EAAS,MAAO,IAAK,EAAQ,SAAU,IAC5D,GAAoB,EAAQ,OAC5B,KAAK,WAAW,EAClB,CAKA,iBAAA,CAAkB,GAChB,IAAM,EAAU,KAAK,YACf,EAAU,IAAK,EAAS,MAAO,IAAK,EAAQ,SAAU,IAC5D,GAAoB,EAAQ,OAC5B,KAAK,WAAW,EAClB,CAKA,iBAAA,CAAkB,GAChB,IAAM,EAAU,KAAK,YACf,EAAU,IAAK,EAAS,MAAO,IAAK,EAAQ,SAAU,IAC5D,GAAoB,EAAQ,OAC5B,KAAK,WAAW,EAClB,CAKA,GAAA,CAAa,GACX,IAAM,EAAS,KAAK,YACd,EAAO,EAAI,MAAM,KACnB,EAAa,EAEjB,QAAW,KAAK,EACd,KAAI,GAA0B,iBAAV,KAAsB,KAAK,GAG7C,OAAO,KAFP,EAAQ,EAAM,EAEP,CAIX,OAAO,CACT,CAKA,GAAA,CAAI,EAAa,GACf,IAAM,EAAS,KAAK,YACd,EAAO,EAAI,MAAM,KACnB,EAAe,EAEnB,QAAS,EAAI,EAAG,EAAI,EAAK,OAAS,EAAG,IAAK,CACxC,IAAM,EAAI,EAAK,GACX,KAAO,EAAQ,IAA4B,iBAAf,EAAQ,MACtC,EAAQ,GAAK,CAAC,GAEZ,IACF,EAAU,EAAQ,GAEtB,CAEA,IAAM,EAAU,EAAK,EAAK,OAAS,GAC/B,IACF,EAAQ,GAAW,GAErB,KAAK,WAAW,EAClB,CAKA,eAAA,GACE,KAAK,WAAW,GAChB,KAAK,OAAO,KAAK,kCACnB,CAKA,YAAA,GACE,IAAM,EAAS,KAAK,YACpB,OAAO,KAAK,UAAU,EAAQ,KAAM,EACtC,CAKA,YAAA,CAAa,GACX,IACE,IAAM,EAAS,KAAK,MAAM,GAC1B,EAAkB,GAClB,KAAK,WAAW,GAChB,KAAK,OAAO,KAAK,yBACnB,OAAS,GACP,MAAM,IAAI,EAAY,iCAAkC,CAAE,SAC5D,CACF,CAKQ,YAAA,GACN,IACE,GAAI,GAAW,KAAK,YAAa,CAC/B,IAAM,EAAU,GAAa,KAAK,WAAY,SACxC,EAAS,KAAK,MAAM,GAC1B,MAAO,IAAK,KAAmB,EACjC,CAGA,OAAA,KAAK,OAAO,KAAK,yCACV,CACT,OAAS,GACP,OAAA,KAAK,OAAO,MAAM,CAAE,SAAS,mDACtB,CACT,CACF,CAKQ,UAAA,CAAW,GACjB,IACE,IAAM,EAAM,GAAQ,KAAK,YACpB,GAAW,IACd,GAAU,EAAK,CAAE,WAAW,IAG9B,GAAc,KAAK,WAAY,KAAK,UAAU,EAAQ,KAAM,GAAI,QAClE,OAAS,GACP,MAAM,IAAI,EAAY,gCAAiC,CAAE,SAC3D,CACF,CAKQ,gBAAA,GACN,IACE,IAAM,EAAW,KAAK,WAAW,QAAmB,cACpD,OAAI,EACK,IAAK,KAAmB,IAIjC,KAAK,OAAO,KAAK,gDACV,EACT,OAAS,GACP,OAAA,KAAK,OAAO,MAAM,CAAE,SAAS,uDACtB,CACT,CACF,CAKQ,cAAA,CAAe,GACrB,IACE,KAAK,WAAW,QAAQ,aAAc,EACxC,OAAS,GACP,MAAM,IAAI,EAAY,oCAAqC,CAAE,SAC/D,CACF,CAKA,UAAA,GACE,KAAK,aAAe,IACtB,GIhRF,ICAA,ICAA,IAGA,IAAM,EAAS,IAAY,MAAM,CAAE,OAAQ,qBAQ9B,EAAN,MACG,OAAqC,IAAI,IAKjD,gBAAA,CAAiB,GACf,IAAM,EAAO,KAAK,OAAO,IAAI,GAC7B,IAAK,EAAM,OAAO,EAElB,IAAM,EAAM,IAAI,KAChB,OAAI,EAAK,UAAY,EACZ,KAAK,MAAM,EAAK,UAAU,UAAY,EAAI,WAAa,KAGzD,CACT,CAWA,cAAA,CACE,EACA,EACA,EACA,GAGA,GAAe,MAAX,GAA6B,MAAX,GAA6B,MAAX,GAA6B,MAAX,EACxD,OAAO,KAIT,IAkBI,EAlBE,EAAoB,MAAX,EAAiB,KAAK,qBAAqB,GAAI,eAE1D,EAA+B,KAGnC,GAAI,EAAkB,CACpB,IAAM,EAAS,SAAS,EAAkB,IACrC,MAAM,KACT,EAAgB,EAEpB,CASA,GANsB,OAAlB,IACF,EAAgB,KAAK,uBAAuB,IAKxB,OAAlB,EAEF,EAAW,EAAgB,EAAI,EAAI,OAGnC,OAAQ,GACN,sBACE,EAAO,KAAK,0DACZ,EAAW,KACX,MACF,0BACE,EAAO,MAAM,mDACb,EAAW,GACX,MACF,mBACE,EAAO,KAAK,gBAAgB,4CAC5B,EAAW,GACX,MACF,QACE,EAAO,MAAM,gDACb,EAAW,GAIjB,IAAM,EAAsB,CAC1B,UAAW,IAAI,KAAK,KAAK,MAAmB,IAAX,GACjC,cAAe,EACf,WAAY,IAAI,KAChB,UAGF,OAAA,KAAK,OAAO,IAAI,EAAW,GAE3B,EAAO,KACL,CAAE,YAAW,SAAQ,SAAQ,YAC7B,yBAAyB,eAAoB,MAGxC,CACT,CAKQ,oBAAA,CAAqB,GAC3B,IAAM,EAAU,EAAK,OAGrB,GAAI,EAAQ,WAAW,MAAQ,EAAQ,WAAW,KAChD,IAEE,IAAM,EADO,KAAK,MAAM,GACD,OAAO,UAAU,IAAI,OAE5C,GAAkB,oBAAd,EACF,wBACK,GAAkB,wBAAd,EACT,2BAEJ,OAEA,CAIF,IAAM,EAAY,EAAK,cACvB,OAAI,EAAU,SAAS,cAAgB,EAAU,SAAS,SAAO,kBAEtD,EAAU,SAAS,eAAiB,EAAU,SAAS,qBAAmB,+BAKvF,CAKQ,mBAAA,CAAoB,GAC1B,EAAO,MAAM,CAAE,SAAU,GAAK,2BAG9B,IACM,EAAQ,EAAE,MADA,2DAGhB,IAAK,EACH,OAAA,EAAO,KAAK,CAAE,SAAU,GAAK,oCACtB,KAGT,IAAM,EAAQ,SAAS,EAAM,IAAM,IAAK,IAClC,EAAU,SAAS,EAAM,IAAM,IAAK,IACpC,EAAU,WAAW,EAAM,IAAM,KACjC,EAAe,SAAS,EAAM,IAAM,IAAK,IAE/C,EAAO,MAAM,CAAE,QAAO,UAAS,UAAS,gBAAgB,8BAExD,IAAM,EAAuB,KAAR,EAAyB,GAAV,EAAe,KAAK,KAAK,GAAW,KAAK,KAAK,EAAe,KAEjG,OAAqB,IAAjB,GACF,EAAO,KAAK,CAAE,SAAU,GAAK,gCACtB,OAGT,EAAO,KAAK,CAAE,SAAU,EAAG,gBAAgB,gCACpC,EACT,CAKQ,sBAAA,CAAuB,GAC7B,IAAM,EAAU,EAAK,OAGrB,GAAI,EAAQ,WAAW,MAAQ,EAAQ,WAAW,KAChD,IACE,IAAM,EAAO,KAAK,MAAM,GAGlB,EAAkB,EAAK,OAAO,UAAU,IAAI,UAAU,gBAC5D,GAAI,GAA8C,iBAApB,EAA8B,CAC1D,EAAO,MAAM,CAAE,mBAAmB,iCAClC,IAAM,EAAS,KAAK,oBAAoB,GACxC,GAAe,OAAX,EAAiB,OAAO,CAC9B,CAGA,IAAM,EAAa,EAAK,OAAO,YAC/B,GAA0B,iBAAf,EACT,OAAA,EAAO,MAAM,CAAE,cAAc,6BACtB,CAEX,OAEA,CAIF,IAAM,EAAW,CAEf,CAAE,MAAO,gCAAiC,QAAU,GAA8C,GAAtB,SAAS,EAAE,GAAK,IAAW,SAAS,EAAE,GAAK,KAGvH,CAAE,MAAO,8CAA+C,QAAU,GAAwB,SAAS,EAAE,GAAK,KAG1G,CAAE,MAAO,oCAAqC,QAAU,GAAwB,SAAS,EAAE,GAAK,KAGhG,CAAE,MAAO,4BAA6B,QAAU,GAAwB,SAAS,EAAE,GAAK,KAGxF,CAAE,MAAO,mBAAoB,QAAU,GAAwB,SAAS,EAAE,GAAK,MAGjF,QAAa,QAAO,aAAa,EAAU,CACzC,IAAM,EAAQ,EAAK,MAAM,GACzB,GAAI,EAAO,CACT,IAAM,EAAS,EAAQ,GACvB,OAAA,EAAO,MAAM,CAAE,QAAS,EAAM,OAAQ,UAAU,8BACzC,CACT,CACF,CAEA,OAAO,IACT,CAKA,aAAA,CAAc,GACZ,IAAM,EAAO,KAAK,OAAO,IAAI,GAC7B,QAAK,GAEE,EAAK,UAAY,IAAI,IAC9B,CAKA,eAAA,CAAgB,GACd,IAAM,EAAO,KAAK,OAAO,IAAI,GAC7B,IAAK,EAAM,OAAO,KAElB,IAAM,EAAM,IAAI,KACV,EAAO,EAAK,UAAU,UAAY,EAAI,UAC5C,OAAO,EAAO,EAAI,KAAK,KAAK,EAAO,KAAQ,IAC7C,CAKA,GAAA,CAAI,GACF,OAAO,KAAK,OAAO,IAAI,IAAc,IACvC,CAKA,KAAA,CAAM,GACJ,OAAO,KAAK,OAAO,OAAO,EAC5B,CAKA,QAAA,GACE,IAAM,EAAQ,KAAK,OAAO,KAC1B,KAAK,OAAO,QACZ,EAAO,MAAM,CAAE,SAAS,iCAC1B,CAKA,OAAA,GACE,IAAM,EAAM,IAAI,KACZ,EAAQ,EAEZ,QAAY,EAAW,KAAS,KAAK,OAAO,UACtC,EAAK,WAAa,IACpB,KAAK,OAAO,OAAO,GACnB,KAIJ,OAAI,EAAQ,GACV,EAAO,MAAM,CAAE,SAAS,yCAGnB,CACT,CAKA,IAAA,GACE,OAAO,KAAK,OAAO,IACrB,GCnTF,ICAA,IASA,IAEA,IAAM,EAAS,IAAY,MAAM,CAAE,OAAQ,iBAErC,GAAgB,sEAChB,GAAmB,gEACnB,GAAa,+BAON,EAAN,MAIL,sBAAM,CAAiB,EAAqB,GAC1C,IACE,EAAO,MAAM,CAAE,SAAS,yBAExB,IAAM,QAAiB,MAAM,GAAkB,CAC7C,OAAQ,OACR,QAAS,CACP,cAAiB,UAAU,IAC3B,eAAgB,mBAChB,aAAc,IAEhB,KAAM,KAAK,UAAU,CACnB,SAAU,CAAE,QAAS,mBAIzB,IAAK,EAAS,GACZ,OAAA,EAAO,KAAK,CAAE,QAAO,OAAQ,EAAS,QAAU,yBACzC,CAAE,UAAW,KAAM,iBAAkB,MAG9C,IAAM,QAAa,EAAS,OAEtB,EAAY,EAAK,yBAA2B,KAG5C,EACJ,EAAK,UAAU,IACf,EAAK,aAAa,IAClB,KAEF,OAAI,GACF,EAAO,KAAK,CAAE,QAAO,oBAAoB,8BAGpC,CAAE,YAAW,mBACtB,OAAS,GACP,OAAA,EAAO,MAAM,CAAE,QAAO,SAAS,gCACxB,CAAE,UAAW,KAAM,iBAAkB,KAC9C,CACF,CAKA,gBAAM,CAAW,EAAqB,GACpC,EAAO,MAAM,CAAE,SAAS,wBAGxB,IAAQ,YAAW,0BAA2B,KAAK,iBAAiB,EAAa,GAE3E,EAAiB,GAAa,uBAIhC,EAA0B,KAE9B,QAAS,EAAU,EAAG,GAHH,EAG0B,IAC3C,IACE,IAAM,QAAiB,MAAM,GAAe,CAC1C,OAAQ,OACR,QAAS,CACP,cAAiB,UAAU,IAC3B,eAAgB,mBAChB,aAAc,IAEhB,KAAM,KAAK,UAAU,CACnB,QAAS,MAKb,GAAwB,MAApB,EAAS,OACX,OAAA,EAAO,KAAK,CAAE,SAAS,iDAOhB,CAAE,MANgB,CACvB,OAAQ,GACR,YAAa,KAAK,MAAM,KAAK,MAAQ,KACrC,aAAa,EACb,iBAAkB,QAAoB,GAExB,aAIlB,IAAK,EAAS,GAAI,CAChB,IAAM,QAAkB,EAAS,OAMjC,GALA,EAAO,KACL,CAAE,QAAO,OAAQ,EAAS,OAAQ,UAAS,WAjChC,GAkCX,cAAc,KAGZ,EArCS,EAqCa,OAClB,KAAK,MAAM,KACjB,QACF,CAEA,MAAM,IAAI,EACR,oBAAoB,EAAS,YAAY,IACzC,kBACA,EAAS,OAEb,CAGA,IAAM,QAAa,EAAS,OAEtB,EAAQ,KAAK,mBAAmB,EAAM,GAE5C,OAAA,EAAO,MAAM,CAAE,QAAO,WAAY,EAAM,OAAO,QAAU,0BAElD,CAAE,QAAO,YAClB,OAAS,GACP,EAAO,KAAK,CAAE,QAAO,UAAS,WA1Df,EA0D2B,SAAS,8BACnD,EAAY,EAER,EA7DW,SA8DP,KAAK,MAAM,IAErB,CAGF,MAAM,GAAa,IAAI,EAAiB,mCAAoC,qBAC9E,CAKQ,kBAAA,CAAmB,EAA2B,GACpD,IAAM,EAAuB,GAE7B,EAAO,MAAM,CAAE,WAAY,OAAO,KAAK,EAAK,QAAU,CAAC,GAAG,QAAU,0BAEpE,QAAY,EAAM,KAAS,OAAO,QAAQ,EAAK,QAAU,CAAC,GAAI,CAC5D,IAAK,EAAK,UAAW,SAErB,IAAM,EAAa,EAAK,UAAU,kBAC9B,KAAK,MAAyC,IAAnC,EAAK,UAAU,mBAC1B,EAEE,EAAY,EAAK,UAAU,WAAa,IAG1C,EAAK,SAAS,WAAa,EAAK,SAAS,YAC3C,EAAO,KAAK,CAAE,OAAM,aAAY,aAEpC,CAEA,MAAO,CACL,SACA,YAAa,KAAK,MAAM,KAAK,MAAQ,KACrC,aAAa,EACb,iBAAkB,QAAoB,EAE1C,CAKQ,KAAA,CAAM,GACZ,OAAO,IAAI,SAAQ,GAAW,WAAW,EAAS,IACpD,GDtLI,GAAS,IAAY,MAAM,CAAE,OAAQ,iBAO9B,EAAN,MACG,QACA,MAAgC,IAAI,IACpC,SAER,WAAA,CAAY,EAAmB,KAC7B,KAAK,QAAU,IAAI,EACnB,KAAK,SAAW,CAClB,CAKA,gBAAM,CAAW,EAAqB,GAEpC,IAAM,EAAS,KAAK,MAAM,IAAI,GAC9B,GAAI,GAAU,KAAK,aAAa,GAC9B,OAAA,GAAO,MAAM,CAAE,SAAS,2BACjB,CAAE,MAAO,EAAQ,UAAW,MAIrC,IAAM,QAAe,KAAK,QAAQ,WAAW,EAAa,GAG1D,OAAA,KAAK,MAAM,IAAI,EAAO,EAAO,OAEtB,CACT,CAKA,sBAAM,CAAiB,EAAqB,GAC1C,OAAO,KAAK,QAAQ,iBAAiB,EAAa,EACpD,CAKA,QAAA,CAAS,EAAe,EAAe,EAAoB,IACzD,IAAM,EAAS,KAAK,MAAM,IAAI,GAC9B,IAAK,EAAQ,OAAO,EAEpB,GAAI,EAAO,YAAa,OAAO,EAE/B,IAAM,EAAa,EAAO,OAAO,MAAK,GAAK,EAAE,KAAK,SAAS,KAC3D,OAAK,GAEE,EAAW,YAAc,CAClC,CAKA,cAAA,CAAe,GACb,OAAO,KAAK,MAAM,IAAI,IAAU,IAClC,CAKA,UAAA,CAAW,GACT,KAAK,MAAM,OAAO,EACpB,CAKA,aAAA,GACE,KAAK,MAAM,OACb,CAKQ,YAAA,CAAa,GAGnB,OAFY,KAAK,MAAM,KAAK,MAAQ,KAClB,EAAM,YACX,KAAK,QACpB,GH/EF,IAEA,IAAM,EAAS,IAAY,MAAM,CAAE,OAAQ,iBAO9B,GAAN,MACG,eACA,iBACA,aACA,cAEA,eACA,eACA,kBACA,wBAGA,gBAAuC,IAAI,IAG3C,gBAAmE,KAGnE,aAAuB,EAE/B,WAAA,CACE,EACA,EACA,EAA6B,CAAC,GAE9B,KAAK,eAAiB,EACtB,KAAK,cAAgB,EACrB,KAAK,iBAAmB,IAAI,EAC5B,KAAK,aAAe,IAAI,EAExB,KAAK,eAAiB,EAAO,gBAAkB,UAC/C,KAAK,eAAiB,EAAO,gBAAkB,GAC/C,KAAK,kBAAoB,EAAO,mBAAqB,GACrD,KAAK,wBAA0B,EAAO,yBAA2B,GACnE,CAKA,cAAM,CAAS,GACb,IAAQ,aAAY,eAAc,EAAO,aAAc,EAGjD,EAAc,KAAK,eAAe,iBACxC,GAA2B,IAAvB,EAAY,OACd,MAAM,IAAI,EAAiB,wBAAyB,eAGtD,IAAM,EAAiB,KAAK,mBAAmB,GACzC,EAAY,IAAI,IAClB,EAA2B,KAE/B,QAAS,EAAU,EAAG,EAAU,EAAe,OAAQ,IAAW,CAChE,IAAM,EAAS,GAAe,EAAU,EAGpC,QAAwB,KAAK,cAC/B,EACA,EACA,EACA,EACA,GAGF,IAAK,EAAiB,CAEpB,IAAM,EAAU,KAAK,mBAAmB,GACxC,MAAM,IAAI,EACR,gEAAgE,MAChE,uBACA,IAEJ,CAGA,UACQ,KAAK,iBAAiB,EAC9B,OAAS,GACP,EAAO,MAAM,CAAE,MAAO,EAAgB,MAAO,SAAS,wBAGtD,EAAU,IAAI,EAAgB,IAG1B,KAAK,iBAAiB,YAAc,EAAgB,KACtD,KAAK,gBAAkB,MAGzB,EAAY,yBAAyB,IACrC,QACF,CAGA,UACQ,KAAK,gBAAgB,EAC7B,OAAS,GACP,EAAO,MAAM,CAAE,MAAO,EAAgB,MAAO,SAAS,8BAEtD,EAAU,IAAI,EAAgB,IAE1B,KAAK,iBAAiB,YAAc,EAAgB,KACtD,KAAK,gBAAkB,MAGzB,EAAY,+BAA+B,IAC3C,QACF,CAGA,MAAO,CACL,YAAa,EAAgB,OAAO,aACpC,UAAW,EAAgB,WAAa,uBACxC,MAAO,EAAgB,MACvB,UAAW,EAAgB,GAE/B,CAEA,MAAM,IAAI,EAAiB,GAAa,sBAAuB,yBACjE,CAKA,mBAAc,CACZ,EACA,EACA,EACA,EACA,GAGA,IAAK,GAAU,GAAqC,sBAAxB,KAAK,eAAoD,CACnF,IAAM,EAAiB,KAAK,gBAAgB,IAAI,GAEhD,GAAI,IAAmB,EAAU,IAAI,GAAiB,CACpD,IAAM,EAAe,EAAS,MAAK,GAAK,EAAE,KAAO,IAEjD,GAAI,EAAc,CAChB,IAAM,EAAW,KAAK,iBAAiB,iBAAiB,GAExD,KAAI,EAAW,GAuBb,OAAA,EAAO,MAAM,CAAE,YAAW,MAAO,EAAa,OAAS,yBAChD,EAtBP,GAA4B,gBAAxB,KAAK,gBAAgD,GAAY,KAAK,eAExE,OAAA,EAAO,KACL,CAAE,YAAW,MAAO,EAAa,MAAO,YACxC,kEAGI,KAAK,MAAiB,IAAX,GAEjB,EAAO,MAAM,CAAE,YAAW,MAAO,EAAa,OAAS,wCAChD,EAGP,EAAO,KACL,CAAE,YAAW,MAAO,EAAa,MAAO,WAAU,QAAS,KAAK,gBAChE,qDAEF,KAAK,gBAAgB,OAAO,EAOlC,CACF,CACF,CAGA,IAAK,GAAyB,cAAf,GACT,KAAK,iBACK,KAAK,MAAQ,KAAK,gBAAgB,UAEX,IAAzB,KAAK,oBAA6B,EAAU,IAAI,KAAK,gBAAgB,WAAY,CACzF,IAAM,EAAgB,EAAS,MAAK,GAAK,EAAE,KAAO,KAAK,gBAAiB,YAExE,GAAI,IAAkB,KAAK,iBAAiB,cAAc,EAAc,IACtE,OAAA,EAAO,MAAM,CAAE,MAAO,EAAc,OAAS,0CACtC,CAEX,CAKJ,IAAM,EAAW,KAAK,aAEtB,QAAS,EAAS,EAAG,EAAS,EAAS,OAAQ,IAAU,CACvD,IAAM,GAAO,EAAW,GAAU,EAAS,OACrC,EAAY,EAAS,GAE3B,GAAK,IAED,EAAU,IAAI,EAAU,MAKxB,KAAK,iBAAiB,cAAc,EAAU,IAKlD,OAAA,KAAK,cAAgB,EAAM,GAAK,EAAS,QAGpC,GAAyB,cAAf,IACb,KAAK,gBAAkB,CACrB,UAAW,EAAU,GACrB,UAAW,KAAK,QAKhB,GAAqC,sBAAxB,KAAK,iBACpB,KAAK,gBAAgB,IAAI,EAAW,EAAU,IAC9C,EAAO,MAAM,CAAE,YAAW,MAAO,EAAU,OAAS,iCAGlD,GACF,EAAO,MAAM,CAAE,MAAO,EAAU,OAAS,uCAGpC,CACT,CAEA,OAAO,IACT,CAKA,sBAAc,CAAiB,GAC7B,IAAM,EAAY,EAAQ,OAAO,WACjC,IAAK,EAAW,OAEhB,IACM,GAAa,EADP,KAAK,OACqB,IAGtC,GAAI,EAAY,KAAK,wBAAyB,CAC5C,EAAO,MAAM,CAAE,MAAO,EAAQ,MAAO,aAAa,mCAElD,IACE,IAAM,QAAkB,KAAK,cAAc,aAAa,EAAQ,OAAO,qBAGjE,KAAK,eAAe,cAAc,EAAQ,GAAI,CAClD,OAAQ,IAIV,EAAQ,OAAS,EAEjB,EAAO,KAAK,CAAE,MAAO,EAAQ,OAAS,+BACxC,OAAS,GAGP,MADqB,OAAO,GACX,SAAS,kBACxB,EAAO,MAAM,CAAE,MAAO,EAAQ,OAAS,iEAGjC,KAAK,eAAe,cAAc,EAAQ,GAAI,CAClD,UAAU,IAIN,IAAI,EAAiB,wCAAyC,gBAAiB,MAGjF,CACR,CACF,CACF,CAKA,qBAAc,CAAgB,GAC5B,GAAI,EAAQ,UAAW,OAEvB,EAAO,MAAM,CAAE,MAAO,EAAQ,OAAS,wCAEvC,IAAQ,YAAW,0BAA2B,KAAK,aAAa,iBAC9D,EAAQ,OAAO,aACf,EAAQ,OAGN,SAEI,KAAK,eAAe,cAAc,EAAQ,GAAI,CAClD,YACA,MAAO,IACF,EAAQ,MACX,kBAAmB,QAAoB,KAK3C,EAAQ,UAAY,EAChB,QAAwD,IAApC,EAAQ,MAAM,oBACpC,EAAQ,MAAM,kBAAoB,GAGpC,EAAO,KAAK,CAAE,MAAO,EAAQ,MAAO,aAAa,iCAEjD,EAAO,KAAK,CAAE,MAAO,EAAQ,OAAS,4CAE1C,CAKQ,kBAAA,CAAmB,GACzB,IAAM,EAAgB,GACP,UAAT,EAAyB,EAChB,QAAT,EAAuB,EACd,SAAT,EAAwB,EACrB,EAGT,MAAO,IAAI,GAAU,MAAK,CAAC,EAAG,KAC5B,IAAM,EAAQ,EAAE,MAAM,kBAChB,EAAQ,EAAE,MAAM,kBACtB,OAAO,EAAa,GAAS,EAAa,EAAK,GAEnD,CAKQ,kBAAA,CAAmB,GACzB,IAAM,EAAY,EACf,KAAI,GAAK,KAAK,iBAAiB,iBAAiB,EAAE,MAClD,QAAO,GAAK,EAAI,IAEnB,OAAO,EAAU,OAAS,EAAI,KAAK,OAAO,GAAa,EACzD,CAKA,eAAA,CACE,EACA,EACA,EACA,GAEA,KAAK,iBAAiB,eAAe,EAAW,EAAQ,EAAY,EACtE,CAKA,mBAAA,CAAoB,GAClB,KAAK,gBAAgB,OAAO,EAC9B,CAKA,gBAAA,GACE,KAAK,gBAAgB,OACvB,CAKA,iBAAA,GACE,OAAO,KAAK,cACd,CAKA,iBAAA,CAAkB,GAChB,KAAK,eAAiB,EACtB,EAAO,KAAK,CAAE,QAAQ,0BACxB,CAKA,mBAAA,GACE,OAAO,KAAK,gBACd,CAKA,eAAA,GACE,OAAO,KAAK,YACd,CAKQ,KAAA,CAAM,GACZ,OAAO,IAAI,SAAQ,GAAW,WAAW,EAAS,IACpD,GKtaF,IAQA,ICRA,IAWO,IAAM,GAA0C,CAErD,QAAS,CACP,SAAU,SACV,MAAO,qCACP,SAAU,QAEZ,cAAe,CACb,SAAU,SACV,MAAO,uBACP,SAAU,QAEZ,SAAU,CACR,SAAU,SACV,MAAO,kBACP,SAAU,SAEZ,gBAAiB,CACf,SAAU,SACV,MAAO,mBACP,SAAU,QAIZ,6BAA8B,CAC5B,SAAU,YACV,MAAO,8BAET,4BAA6B,CAC3B,SAAU,YACV,MAAO,6BAET,yBAA0B,CACxB,SAAU,YACV,MAAO,0BAIT,oBAAqB,CACnB,SAAU,YACV,MAAO,8BAET,mBAAoB,CAClB,SAAU,YACV,MAAO,8BAOJ,SAAS,EAAgB,GAC9B,OAAO,GAAU,IAAc,IACjC,CAKO,SAAS,GAAY,GAE1B,OADgB,EAAgB,IAChB,UAAY,IAC9B,CD9DA,IACA,IAYO,IAAM,EAAN,MACL,WAAA,CAAoB,GAAA,KAAA,SAAgC,CAKpD,0BAAM,CACJ,GAGA,IAAM,EAAU,EAAgB,EAAQ,OACxC,IAAK,EACH,MAAM,IAAI,EACR,kBAAkB,EAAQ,QAC1B,iBAKJ,IAAM,EAAkC,WAArB,EAAQ,SAAwB,SAAW,SACxD,QAAsB,KAAK,QAAQ,aAAa,SAAS,CAC7D,aACA,gBAAiB,EAAQ,YAAc,MAIzC,GAAyB,WAArB,EAAQ,SACV,aAAa,KAAK,oBAAoB,EAAS,EAAc,YAAa,GAE1E,MAAM,IAAI,EAAiB,qCAAsC,kBAErE,CAKA,yBAAc,CACZ,EACA,EACA,GAGA,IAAM,EAAgB,EAAuB,GAa7C,OAAO,SAVQ,IAAI,EAAa,CAC9B,cACA,MAAO,EAAQ,MACf,SAAU,EAAQ,WAIgB,gBAAgB,GAGH,EAAQ,MAC3D,GhBjEF,IACA,IkBfA,WAKO,OAAuC,iBACvC,OAAU,gBCNjB,IAcA,IAAM,GAAS,IAYR,SAAS,GAAoB,EAAyB,GAC3D,IAAM,EAAU,IAAI,EAAc,GAElC,EAAO,KAAK,wBAAwB,MAAO,EAAS,KAClD,IAAM,EAAM,EAAQ,KAEpB,IAEE,GAAI,EAAI,OAAQ,CACd,IAAM,EAAgB,EAAQ,aAAa,cAC3C,IAAK,EACH,OAAO,EAAM,OAAO,KAAK,KAAK,CAC5B,MAAO,CAAE,QAAS,wBAAyB,KAAM,yBAIrD,IAAQ,sBAAuB,QAAA,UAAA,MAAA,KAAA,IAAA,OACvB,yBAAwB,oCAAqC,QAAA,UAAA,MAAA,KAAA,IAAA,OAC7D,qBAAsB,OAAO,UAErC,EAAM,IAAI,UAAU,IAAK,CACvB,eAAgB,oBAChB,gBAAiB,WACjB,WAAc,eAGhB,IACE,IAAM,EAAgB,EAAuB,GAEvC,EADe,IAAI,EAAa,CAAE,YAAa,EAAc,cACjC,sBAAsB,GAElD,EAAK,YAAc,EAAY,IAAI,SAAS,OAC9C,GAAU,EAEd,cAAiB,KAAe,EAAc,CAC5C,IAAM,EAAc,EAA2B,EAAa,EAAI,MAAO,EAAI,GAG3E,GAFA,EAAM,IAAI,MAAM,SAAS,KAAK,UAAU,UACpC,IAAS,GAAU,GACnB,EAAY,QAAQ,IAAI,cAAe,KAC7C,CAIA,EAAM,IAAI,MAAM,UAEhB,EAAM,IAAI,MADO,aAEnB,OAAS,GACP,GAAO,MAAM,CAAE,IAAK,GAAa,kBACnC,SACE,EAAM,IAAI,KACZ,CACA,OAAO,CACT,CAGA,IAAM,QAAiB,EAAQ,qBAAqB,GACpD,OAAO,EAAM,KAAK,EACpB,OAAS,GACP,OAAA,GAAO,MAAM,CAAE,IAAK,EAAO,MAAO,EAAI,OAAS,yBACxC,EAAM,OAAO,KAAK,KAAK,CAC5B,MAAO,CACL,QAAS,aAAiB,MAAQ,EAAM,QAAU,gBAClD,KAAM,iBAGZ,IAEJ,CC1FA,eAAsB,GACpB,EACA,GAEA,EAAO,IAAI,YAAY,MAAO,EAAyB,KACrD,IAAM,EAAiB,EAAQ,sBAE/B,EACG,KAAK,4CACL,KAAK,EAAc,GAE1B,CAdA,ICAA,IAGO,IAAM,EAAN,MACG,cAAkD,IAAI,IACtD,iBAA0C,IAAI,IAC9C,wBAAoC,GACpC,UAAoB,KAAK,MAKjC,gBAAA,CAAiB,EAAe,GACzB,KAAK,cAAc,IAAI,IAC1B,KAAK,cAAc,IAAI,EAAO,IAAI,KAGpC,IAAM,EAAc,KAAK,cAAc,IAAI,GACrC,EAAe,EAAY,IAAI,IAAe,EACpD,EAAY,IAAI,EAAY,EAAe,EAC7C,CAKA,cAAA,CAAe,EAAkB,GAC1B,KAAK,iBAAiB,IAAI,IAC7B,KAAK,iBAAiB,IAAI,EAAU,IAGtC,IAAM,EAAY,KAAK,iBAAiB,IAAI,GAC5C,EAAU,KAAK,GAGX,EAAU,OAAS,KACrB,EAAU,OAEd,CAKA,oBAAA,CAAqB,GACnB,KAAK,wBAAwB,KAAK,GAG9B,KAAK,wBAAwB,OAAS,KACxC,KAAK,wBAAwB,OAEjC,CAKA,mBAAA,GACE,IAAM,EAAkB,GAGlB,EAAgB,KAAK,OAAO,KAAK,MAAQ,KAAK,WAAa,KACjE,EAAM,KAAK,2DACX,EAAM,KAAK,uCACX,EAAM,KAAK,0BAA0B,KACrC,EAAM,KAAK,IAGX,EAAM,KAAK,kDACX,EAAM,KAAK,sCACX,QAAY,EAAO,KAAiB,KAAK,cAAc,UACrD,QAAY,EAAQ,KAAU,EAAa,UACzC,EAAM,KAAK,8BAA8B,cAAkB,OAAY,KAM3E,GAHA,EAAM,KAAK,IAGP,KAAK,iBAAiB,KAAO,EAAG,CAClC,EAAM,KAAK,8EACX,EAAM,KAAK,qDAEX,QAAY,EAAU,KAAc,KAAK,iBAAiB,UACxD,GAAI,EAAU,OAAS,EAAG,CACxB,IAAM,EAAS,IAAI,GAAW,MAAK,CAAC,EAAG,IAAM,EAAI,IAC3C,EAAQ,EAAO,OACf,EAAM,EAAO,QAAO,CAAC,EAAG,IAAM,EAAI,GAAG,GACrC,EAAM,EAAO,KAAK,MAAc,GAAR,KAAiB,EACzC,EAAM,EAAO,KAAK,MAAc,IAAR,KAAkB,EAC1C,EAAM,EAAO,KAAK,MAAc,IAAR,KAAkB,EAEhD,EAAM,KAAK,gDAAgD,sBAA6B,EAAI,QAAQ,MACpG,EAAM,KAAK,gDAAgD,uBAA8B,EAAI,QAAQ,MACrG,EAAM,KAAK,gDAAgD,uBAA8B,EAAI,QAAQ,MACrG,EAAM,KAAK,oDAAoD,OAAc,EAAI,QAAQ,MACzF,EAAM,KAAK,sDAAsD,OAAc,IACjF,CAEF,EAAM,KAAK,GACb,CAGA,GAAI,KAAK,wBAAwB,OAAS,EAAG,CAC3C,IAAM,EAAS,IAAI,KAAK,yBAAyB,MAAK,CAAC,EAAG,IAAM,EAAI,IAC9D,EAAQ,EAAO,OACf,EAAM,EAAO,QAAO,CAAC,EAAG,IAAM,EAAI,GAAG,GACrC,EAAM,EAAO,KAAK,MAAc,GAAR,KAAiB,EACzC,EAAM,EAAO,KAAK,MAAc,IAAR,KAAkB,EAC1C,EAAM,EAAO,KAAK,MAAc,IAAR,KAAkB,EAEhD,EAAM,KAAK,yFACX,EAAM,KAAK,wDACX,EAAM,KAAK,yDAAyD,EAAI,QAAQ,MAChF,EAAM,KAAK,0DAA0D,EAAI,QAAQ,MACjF,EAAM,KAAK,0DAA0D,EAAI,QAAQ,MACjF,EAAM,KAAK,6CAA6C,EAAI,QAAQ,MACpE,EAAM,KAAK,+CAA+C,KAC1D,EAAM,KAAK,GACb,CAGA,IAAM,EAAW,QAAQ,cACzB,OAAA,EAAM,KAAK,6DACX,EAAM,KAAK,qCACX,EAAM,KAAK,oCAAoC,EAAS,OACxD,EAAM,KAAK,2CAA2C,EAAS,aAC/D,EAAM,KAAK,0CAA0C,EAAS,YAC9D,EAAM,KAAK,yCAAyC,EAAS,YAC7D,EAAM,KAAK,IAEJ,EAAM,KAAK,KACpB,CAKA,KAAA,GACE,KAAK,cAAc,QACnB,KAAK,iBAAiB,QACtB,KAAK,wBAA0B,GAC/B,KAAK,UAAY,KAAK,KACxB,CAKA,eAAA,CAAgB,EAAe,GAC7B,OAAO,KAAK,cAAc,IAAI,IAAQ,IAAI,IAAe,CAC3D,CAKA,gBAAA,GACE,IAAI,EAAQ,EACZ,QAAW,KAAgB,KAAK,cAAc,SAC5C,QAAW,KAAS,EAAa,SAC/B,GAAS,EAGb,OAAO,CACT,GH/II,EAAS,IAWF,GAAN,MACG,OACA,QACA,QAER,WAAA,CAAY,GACV,KAAK,QAAU,EACf,KAAK,QAAU,EAAQ,SAAW,IAAI,EACtC,KAAK,OAAS,GAAQ,CACpB,QAAQ,EACR,kBAAmB,QACnB,uBAAuB,IAGzB,KAAK,kBACL,KAAK,gBACP,CAKA,qBAAc,GAEZ,KAAK,OAAO,QAAQ,aAAa,UAC9B,EAAgB,UAAY,KAAK,KAAI,IAGxC,KAAK,OAAO,QAAQ,cAAc,UAChC,IAAM,EAAa,EAAgB,UACnC,GAAI,EAAW,CACb,IAAM,EAAW,KAAK,MAAQ,EAC9B,KAAK,QAAQ,eAAe,EAAQ,aAAa,KAAO,EAAQ,IAAK,EACvE,WAII,KAAK,OAAO,SAAS,GAAM,CAC/B,QAAQ,EACR,aAAa,IAIf,KAAK,OAAO,iBAAgB,CAAC,EAAO,EAAS,KAC3C,EAAO,MAAM,CAAE,IAAK,EAAO,MAAO,EAAQ,IAAM,iBAChD,EAAM,OAAO,EAAM,YAAc,KAAK,KAAK,CACzC,MAAO,CACL,QAAS,EAAM,QACf,KAAM,iBAET,GAEL,CAKQ,cAAA,GAEN,KAAK,OAAO,IAAI,WAAW,UAAA,CAChB,OAAQ,KAAM,WAAW,IAAI,MAAO,kBAI/C,KAAK,OAAO,IAAI,KAAK,UAAA,CAEjB,KAAM,sBACN,QAAS,QACT,UAAW,CACT,uBACA,UACA,gBAMN,GAAqB,KAAK,OAAQ,KAAK,SAGvC,GAAoB,KAAK,OAAQ,CAC/B,eAAgB,KAAK,QAAQ,eAC7B,aAAc,KAAK,QAAQ,aAC3B,iBAAkB,KAAK,QAAQ,iBAC/B,QAAS,KAAK,SAElB,CAKA,WAAM,GACJ,IAAM,EAAO,KAAK,QAAQ,MAAQ,UAC5B,EAAO,KAAK,QAAQ,MAAQ,IAElC,UACQ,KAAK,OAAO,OAAO,CAAE,OAAM,SACjC,EAAO,KAAK,uBAAuB,KAAQ,IAC7C,OAAS,GACP,MAAA,EAAO,MAAM,CAAE,IAAK,GAAS,0BACvB,CACR,CACF,CAKA,UAAM,GACJ,UACQ,KAAK,OAAO,QAClB,EAAO,KAAK,iBACd,OAAS,GACP,MAAA,EAAO,MAAM,CAAE,IAAK,GAAS,yBACvB,CACR,CACF,CAKA,WAAA,GACE,OAAO,KAAK,MACd,GInJF,IAGA,WAHO,OAAc,gCACZ,OAAY,4BACZ,gBAAY,OAAiB,KCFtC,IAIO,IAAM,EAAS,CACpB,SAAU,skBAgBV,SAAU,giBAcV,OAAQ,oJAQR,KAAM,4VAgBD,SAAS,GAAiB,GAC/B,EAAG,KAAK,EAAO,UACf,EAAG,KAAK,EAAO,UACf,EAAG,KAAK,EAAO,QACf,EAAG,KAAK,EAAO,KACjB,CDhDO,IAAM,GAAN,MACG,GACA,OAAS,IAEjB,WAAA,CAAY,EAAyB,CAAC,GACpC,IACE,OAAO,QAAQ,IAAI,SAAW,GAAK,QAAQ,MAAO,OAAQ,kBAC1D,YAAW,EACX,WAAU,GACR,EAEJ,IAEE,IAAM,EAAM,GAAK,EAAM,MAClB,GAAW,IACd,GAAU,EAAK,CAAE,WAAW,IAG9B,KAAK,GAAK,IAAI,GAAS,EAAM,CAC3B,WACA,QAAS,EAAW,GAAQ,KAAK,OAAO,MAAM,QAAO,IAGvD,KAAK,YACL,KAAK,aAEL,KAAK,OAAO,KAAK,CAAE,QAAQ,uBAC7B,OAAS,GACP,MAAM,IAAI,EAAc,gCAAiC,CAAE,SAC7D,CACF,CAKQ,SAAA,GAEN,KAAK,GAAG,OAAO,sBAEf,KAAK,GAAG,OAAO,wBAEf,KAAK,GAAG,OAAO,uBAEf,KAAK,GAAG,OAAO,qBAEf,KAAK,GAAG,OAAO,sBACjB,CAKQ,UAAA,GACN,GAAiB,KAAK,GACxB,CAKA,WAAA,GACE,OAAO,KAAK,EACd,CAKA,OAAA,CAAQ,GACN,OAAO,KAAK,GAAG,QAAQ,EACzB,CAKA,IAAA,CAAK,GACH,KAAK,GAAG,KAAK,EACf,CAKA,WAAA,CAAe,GAEb,OADY,KAAK,GAAG,YAAY,EAApB,EAEd,CAKA,KAAA,GACE,IACE,KAAK,GAAG,QACR,KAAK,OAAO,KAAK,6BACnB,OAAS,GACP,MAAA,KAAK,OAAO,MAAM,CAAE,SAAS,4BACvB,IAAI,EAAc,2BAA4B,CAAE,SACxD,CACF,CAKA,MAAA,CAAO,GACL,IACE,KAAK,GAAG,OAAO,GACf,KAAK,OAAO,KAAK,CAAE,mBAAmB,qBACxC,OAAS,GACP,MAAM,IAAI,EAAc,4BAA6B,CAAE,SACzD,CACF,CAKA,QAAA,GACE,IACE,KAAK,GAAG,KAAK,UACb,KAAK,OAAO,KAAK,qBACnB,OAAS,GACP,MAAM,IAAI,EAAc,8BAA+B,CAAE,SAC3D,CACF,GAME,EAAsB,KAKnB,SAAS,GAAY,GAC1B,OAAK,IACH,EAAW,IAAI,GAAG,IAEb,CACT,CAKO,SAAS,GAAY,GAC1B,EAAW,CACb,CAKO,SAAS,KACV,IACF,EAAS,QACT,EAAW,KAEf,CtB/IA","file":"/npm/antigravity-manager@2.0.4/dist/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/antigravity-manager@2.0.4/dist/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * Base error class for Antigravity application errors\n */\nexport class AntigravityError extends Error {\n  constructor(\n    message: string,\n    public readonly code: string,\n    public readonly statusCode: number = 500,\n    public readonly details?: unknown\n  ) {\n    super(message);\n    this.name = 'AntigravityError';\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  toJSON() {\n    return {\n      name: this.name,\n      message: this.message,\n      code: this.code,\n      statusCode: this.statusCode,\n      details: this.details,\n    };\n  }\n}\n\n/**\n * OAuth-related errors\n */\nexport class OAuthError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'OAUTH_ERROR', 401, details);\n    this.name = 'OAuthError';\n  }\n}\n\n/**\n * Account-related errors\n */\nexport class AccountError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'ACCOUNT_ERROR', 400, details);\n    this.name = 'AccountError';\n  }\n}\n\n/**\n * Not found errors\n */\nexport class NotFoundError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'NOT_FOUND', 404, details);\n    this.name = 'NotFoundError';\n  }\n}\n\n/**\n * Validation errors\n */\nexport class ValidationError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'VALIDATION_ERROR', 400, details);\n    this.name = 'ValidationError';\n  }\n}\n\n/**\n * Database errors\n */\nexport class DatabaseError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'DATABASE_ERROR', 500, details);\n    this.name = 'DatabaseError';\n  }\n}\n\n/**\n * Configuration errors\n */\nexport class ConfigError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'CONFIG_ERROR', 500, details);\n    this.name = 'ConfigError';\n  }\n}\n\n/**\n * Token/authentication errors\n */\nexport class TokenError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'TOKEN_ERROR', 401, details);\n    this.name = 'TokenError';\n  }\n}\n\n/**\n * Quota-related errors\n */\nexport class QuotaError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'QUOTA_ERROR', 429, details);\n    this.name = 'QuotaError';\n  }\n}\n\n/**\n * Proxy/upstream errors\n */\nexport class ProxyError extends AntigravityError {\n  constructor(message: string, statusCode: number = 502, details?: unknown) {\n    super(message, 'PROXY_ERROR', statusCode, details);\n    this.name = 'ProxyError';\n  }\n}\n\n/**\n * Rate limit errors\n */\nexport class RateLimitError extends AntigravityError {\n  constructor(message: string, details?: unknown) {\n    super(message, 'RATE_LIMIT_ERROR', 429, details);\n    this.name = 'RateLimitError';\n  }\n}\n","/**\n * Gemini Protocol Converter\n * Converts between OpenAI and Gemini API formats\n */\n\nimport { randomBytes } from 'crypto';\nimport type {\n  ChatMessage,\n  ChatCompletionRequest,\n  ChatCompletionResponse,\n  ChatCompletionChunk,\n} from '../openai/types.js';\nimport type {\n  GeminiContent,\n  GeminiPart,\n  GeminiGenerateContentRequest,\n  GeminiGenerateContentResponse,\n} from './types.js';\n\n/**\n * Convert OpenAI messages to Gemini format\n */\nexport function convertToGeminiRequest(\n  request: ChatCompletionRequest\n): GeminiGenerateContentRequest {\n  const messages = request.messages;\n\n  // Extract system messages from the beginning\n  const systemMessages: string[] = [];\n  let firstNonSystemIdx = 0;\n\n  for (let i = 0; i < messages.length; i++) {\n    if (messages[i]!.role === 'system') {\n      const content = messages[i]!.content;\n      systemMessages.push(typeof content === 'string' ? content : '');\n      firstNonSystemIdx = i + 1;\n    } else {\n      break;\n    }\n  }\n\n  // Convert remaining messages\n  const conversationMessages = messages.slice(firstNonSystemIdx);\n  const geminiContents = convertMessages(conversationMessages);\n\n  // Build Gemini request\n  const geminiRequest: GeminiGenerateContentRequest = {\n    contents: geminiContents,\n  };\n\n  // Add system instruction if present\n  if (systemMessages.length > 0) {\n    geminiRequest.systemInstruction = {\n      parts: systemMessages.map((text) => ({ text })),\n    };\n  }\n\n  // Add generation config\n  geminiRequest.generationConfig = {\n    temperature: request.temperature,\n    topP: request.top_p,\n    maxOutputTokens: request.max_tokens,\n    stopSequences: Array.isArray(request.stop)\n      ? request.stop\n      : request.stop\n        ? [request.stop]\n        : undefined,\n  };\n\n  return geminiRequest;\n}\n\n/**\n * Convert OpenAI messages to Gemini contents\n * Handles role mapping and message merging for alternation requirement\n */\nfunction convertMessages(messages: ChatMessage[]): GeminiContent[] {\n  const geminiContents: GeminiContent[] = [];\n\n  for (const message of messages) {\n    const role = mapRole(message.role);\n    const parts = convertMessageContent(message);\n\n    // Check if we can merge with previous message (same role)\n    const lastContent = geminiContents[geminiContents.length - 1];\n    if (lastContent && lastContent.role === role) {\n      // Merge parts into existing message\n      lastContent.parts.push(...parts);\n    } else {\n      // Create new message\n      geminiContents.push({ role, parts });\n    }\n  }\n\n  return geminiContents;\n}\n\n/**\n * Map OpenAI role to Gemini role\n */\nfunction mapRole(role: string): 'user' | 'model' {\n  if (role === 'assistant') {\n    return 'model';\n  }\n  // user, tool, function, system all map to user\n  return 'user';\n}\n\n/**\n * Convert message content to Gemini parts\n */\nfunction convertMessageContent(message: ChatMessage): GeminiPart[] {\n  const parts: GeminiPart[] = [];\n\n  // Handle string content\n  if (typeof message.content === 'string') {\n    if (message.content) {\n      parts.push({ text: message.content });\n    }\n  }\n\n  // Handle tool calls (for assistant messages)\n  if (message.role === 'assistant' && 'tool_calls' in message && message.tool_calls) {\n    for (const toolCall of message.tool_calls) {\n      if (toolCall.type === 'function') {\n        parts.push({\n          text: `Function call: ${toolCall.function.name}(${toolCall.function.arguments})`,\n        });\n      }\n    }\n  }\n\n  // Handle tool results (for tool messages)\n  if (message.role === 'tool' && 'tool_call_id' in message) {\n    const content = typeof message.content === 'string' ? message.content : '';\n    parts.push({\n      text: `Function result: ${content}`,\n    });\n  }\n\n  // If no parts were created, add empty text part\n  if (parts.length === 0) {\n    parts.push({ text: '' });\n  }\n\n  return parts;\n}\n\n/**\n * Convert Gemini response to OpenAI format\n */\nexport function convertFromGeminiResponse(\n  geminiResponse: GeminiGenerateContentResponse,\n  model: string,\n  requestId?: string\n): ChatCompletionResponse {\n  const candidate = geminiResponse.candidates[0];\n\n  if (!candidate) {\n    throw new Error('No candidates in Gemini response');\n  }\n\n  // Extract text content from parts\n  const content = extractTextContent(candidate.content.parts);\n\n  // Map finish reason\n  const finishReason = mapFinishReason(candidate.finishReason);\n\n  // Generate response ID\n  const id = requestId || `chatcmpl-${randomBytes(12).toString('hex')}`;\n\n  return {\n    id,\n    object: 'chat.completion',\n    created: Math.floor(Date.now() / 1000),\n    model,\n    choices: [\n      {\n        index: 0,\n        message: {\n          role: 'assistant',\n          content,\n        },\n        finish_reason: finishReason,\n      },\n    ],\n    usage: {\n      prompt_tokens: geminiResponse.usageMetadata?.promptTokenCount || 0,\n      completion_tokens: geminiResponse.usageMetadata?.candidatesTokenCount || 0,\n      total_tokens: geminiResponse.usageMetadata?.totalTokenCount || 0,\n    },\n  };\n}\n\n/**\n * Extract text content from Gemini parts\n */\nfunction extractTextContent(parts: GeminiPart[]): string {\n  return parts.map((part) => part.text || '').join('');\n}\n\n/**\n * Map Gemini finish reason to OpenAI finish reason\n */\nfunction mapFinishReason(\n  reason?: 'STOP' | 'MAX_TOKENS' | 'SAFETY' | 'RECITATION' | 'OTHER'\n): 'stop' | 'length' | 'content_filter' | null {\n  switch (reason) {\n    case 'STOP':\n      return 'stop';\n    case 'MAX_TOKENS':\n      return 'length';\n    case 'SAFETY':\n    case 'RECITATION':\n      return 'content_filter';\n    case 'OTHER':\n      return 'stop';\n    default:\n      return null;\n  }\n}\n\n/**\n * Convert Gemini streaming chunk to OpenAI chunk format\n */\nexport function convertGeminiChunkToOpenAI(\n  geminiChunk: GeminiGenerateContentResponse,\n  model: string,\n  id: string,\n  isFirst: boolean\n): ChatCompletionChunk {\n  const candidate = geminiChunk.candidates[0];\n\n  const chunk: ChatCompletionChunk = {\n    id,\n    object: 'chat.completion.chunk',\n    created: Math.floor(Date.now() / 1000),\n    model,\n    choices: [\n      {\n        index: 0,\n        delta: {},\n        finish_reason: null,\n      },\n    ],\n  };\n\n  if (!candidate) {\n    return chunk;\n  }\n\n  // First chunk includes role\n  if (isFirst) {\n    chunk.choices[0]!.delta.role = 'assistant';\n  }\n\n  // Extract content from parts\n  if (candidate.content?.parts && candidate.content.parts.length > 0) {\n    const content = extractTextContent(candidate.content.parts);\n    if (content) {\n      chunk.choices[0]!.delta.content = content;\n    }\n  }\n\n  // Add finish reason if present\n  if (candidate.finishReason) {\n    chunk.choices[0]!.finish_reason = mapFinishReason(candidate.finishReason);\n  }\n\n  return chunk;\n}\n\n","/**\n * Gemini Protocol Types\n */\n\n/**\n * Gemini content part\n */\nexport interface GeminiPart {\n  text: string;\n}\n\n/**\n * Gemini content\n */\nexport interface GeminiContent {\n  role: 'user' | 'model';\n  parts: GeminiPart[];\n}\n\n/**\n * Gemini generation config\n */\nexport interface GeminiGenerationConfig {\n  temperature?: number;\n  topP?: number;\n  topK?: number;\n  maxOutputTokens?: number;\n  stopSequences?: string[];\n}\n\n/**\n * Gemini generate content request\n */\nexport interface GeminiGenerateContentRequest {\n  contents: GeminiContent[];\n  generationConfig?: GeminiGenerationConfig;\n  systemInstruction?: {\n    parts: GeminiPart[];\n  };\n}\n\n/**\n * Gemini candidate\n */\nexport interface GeminiCandidate {\n  content: GeminiContent;\n  finishReason?: 'STOP' | 'MAX_TOKENS' | 'SAFETY' | 'RECITATION' | 'OTHER';\n  index: number;\n  safetyRatings?: Array<{\n    category: string;\n    probability: string;\n  }>;\n}\n\n/**\n * Gemini generate content response\n */\nexport interface GeminiGenerateContentResponse {\n  candidates: GeminiCandidate[];\n  promptFeedback?: {\n    blockReason?: string;\n    safetyRatings?: Array<{\n      category: string;\n      probability: string;\n    }>;\n  };\n  usageMetadata?: {\n    promptTokenCount: number;\n    candidatesTokenCount: number;\n    totalTokenCount: number;\n  };\n}\n\n/**\n * Gemini API endpoints\n */\nexport const GEMINI_ENDPOINTS = {\n  prod: 'https://generativelanguage.googleapis.com/v1beta',\n  daily: 'https://generativelanguage.googleapis.com/v1alpha',\n} as const;\n","import { AntigravityError } from '../../utils/errors.js';\nimport { GEMINI_ENDPOINTS, type GeminiGenerateContentRequest, type GeminiGenerateContentResponse } from './types.js';\n\nexport interface GeminiClientOptions {\n  accessToken: string;\n  model: string;\n  endpoint?: 'prod' | 'daily';\n}\n\nexport class GeminiClient {\n  constructor(private options: GeminiClientOptions) {}\n\n  async generateContent(request: GeminiGenerateContentRequest): Promise<GeminiGenerateContentResponse> {\n    const endpoint = this.options.endpoint === 'daily' ? GEMINI_ENDPOINTS.daily : GEMINI_ENDPOINTS.prod;\n    const url = `${endpoint}/models/${this.options.model}:generateContent`;\n    const response = await fetch(url, {\n      method: 'POST',\n      headers: { 'Authorization': `Bearer ${this.options.accessToken}`, 'Content-Type': 'application/json' },\n      body: JSON.stringify(request),\n    });\n    if (!response.ok) {\n      throw new AntigravityError(`Gemini API error: ${response.status}`, 'GEMINI_API_ERROR');\n    }\n    return (await response.json()) as GeminiGenerateContentResponse;\n  }\n\n  async *streamGenerateContent(req: GeminiGenerateContentRequest): AsyncGenerator<GeminiGenerateContentResponse> {\n    const endpoint = this.options.endpoint === 'daily' ? GEMINI_ENDPOINTS.daily : GEMINI_ENDPOINTS.prod;\n    const url = `${endpoint}/models/${this.options.model}:streamGenerateContent?alt=sse`;\n    const res = await fetch(url, {\n      method: 'POST',\n      headers: { 'Authorization': `Bearer ${this.options.accessToken}`, 'Content-Type': 'application/json' },\n      body: JSON.stringify(req),\n    });\n    if (!res.ok) throw new AntigravityError(`Gemini stream error: ${res.status}`, 'GEMINI_STREAM_ERROR');\n    if (!res.body) throw new AntigravityError('No response body', 'GEMINI_NO_BODY');\n\n    const reader = res.body.getReader();\n    const decoder = new TextDecoder();\n    let buffer = '';\n\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      buffer += decoder.decode(value, { stream: true });\n      const lines = buffer.split('\\n');\n      buffer = lines.pop() || '';\n      for (const line of lines) {\n        if (line.startsWith('data: ')) {\n          const data = line.slice(6);\n          if (data && !data.startsWith('</')) {\n            try {\n              const chunk = JSON.parse(data) as GeminiGenerateContentResponse;\n              yield chunk;\n            } catch (e) {\n              // Skip invalid JSON\n            }\n          }\n        }\n      }\n    }\n  }\n}\n","/**\n * Antigravity Manager - Main Entry Point\n * Export all public APIs\n */\n\n// Core modules\nexport * from './modules/account/manager.js';\nexport * from './modules/config/manager.js';\nexport * from './modules/token/manager.js';\nexport * from './modules/quota/monitor.js';\n\n// Protocol handlers\nexport * from './protocols/openai/handler.js';\nexport * from './protocols/openai/mapping.js';\nexport * from './protocols/gemini/converter.js';\nexport * from './protocols/gemini/client.js';\n\n// Server\nexport * from './server/index.js';\n\n// Database\nexport * from './database/index.js';\n\n// Utilities\nexport * from './utils/logger.js';\nexport * from './utils/errors.js';\n","import { randomBytes } from 'crypto';\nimport { DB } from '../../database/index.js';\nimport { AccountRepository } from '../../database/repositories/account.js';\nimport { SessionRepository } from '../../database/repositories/session.js';\nimport {\n  Account,\n  CreateAccountInput,\n  UpdateAccountInput,\n  AccountStats,\n  AccountQuota,\n  OAuthTokens,\n} from './types.js';\nimport {\n  validateCreateAccount,\n  validateUpdateAccount,\n  isTokenValid,\n  hasQuota,\n} from './validator.js';\nimport { encrypt, decrypt } from '../../utils/crypto.js';\nimport { AccountError, NotFoundError } from '../../utils/errors.js';\nimport { getLogger } from '../../utils/logger.js';\n\nexport interface AccountManagerConfig {\n  encryptionKey: string;\n}\n\n/**\n * Account Manager - handles account CRUD with encryption\n */\nexport class AccountManager {\n  private accountRepo: AccountRepository;\n  private sessionRepo: SessionRepository;\n  private logger = getLogger().child({ module: 'AccountManager' });\n\n  constructor(\n    db: DB,\n    private config: AccountManagerConfig\n  ) {\n    this.accountRepo = new AccountRepository(db);\n    this.sessionRepo = new SessionRepository(db);\n  }\n\n  /**\n   * Creates a new account\n   */\n  async createAccount(input: CreateAccountInput): Promise<Account> {\n    // Validate input\n    validateCreateAccount(input);\n\n    // Check if account already exists\n    const existing = this.accountRepo.findByEmail(input.email, input.provider);\n    if (existing) {\n      throw new AccountError('Account already exists', {\n        email: input.email,\n        provider: input.provider,\n      });\n    }\n\n    // Generate account ID\n    const id = randomBytes(16).toString('hex');\n\n    // Encrypt tokens\n    const encryptedTokens = encrypt(\n      JSON.stringify(input.tokens),\n      this.config.encryptionKey\n    );\n\n    // Create account in database\n    const dbAccount = this.accountRepo.create({\n      id,\n      provider: input.provider,\n      email: input.email,\n      tokens: encryptedTokens,\n      quota: JSON.stringify(input.quota || {}),\n    });\n\n    this.logger.info({ accountId: id, provider: input.provider, email: input.email }, 'Account created');\n\n    return this.deserializeAccount(dbAccount);\n  }\n\n  /**\n   * Gets an account by ID\n   */\n  getAccountById(id: string): Account | null {\n    const dbAccount = this.accountRepo.findById(id);\n    if (!dbAccount) return null;\n\n    return this.deserializeAccount(dbAccount);\n  }\n\n  /**\n   * Gets an account by email and provider\n   */\n  getAccountByEmail(email: string, provider: 'google' | 'anthropic'): Account | null {\n    const dbAccount = this.accountRepo.findByEmail(email, provider);\n    if (!dbAccount) return null;\n\n    return this.deserializeAccount(dbAccount);\n  }\n\n  /**\n   * Gets all accounts\n   */\n  getAllAccounts(): Account[] {\n    const dbAccounts = this.accountRepo.findAll();\n    return dbAccounts.map(acc => this.deserializeAccount(acc));\n  }\n\n  /**\n   * Gets accounts by provider\n   */\n  getAccountsByProvider(provider: 'google' | 'anthropic'): Account[] {\n    const dbAccounts = this.accountRepo.findByProvider(provider);\n    return dbAccounts.map(acc => this.deserializeAccount(acc));\n  }\n\n  /**\n   * Gets accounts with valid tokens and quota\n   */\n  getAvailableAccounts(): Account[] {\n    const accounts = this.getAllAccounts();\n    return accounts.filter(acc => {\n      const tokenValid = isTokenValid(acc);\n      const quotaAvailable = hasQuota(acc.quota);\n      return tokenValid && quotaAvailable;\n    });\n  }\n\n  /**\n   * Updates an account\n   */\n  async updateAccount(id: string, input: UpdateAccountInput): Promise<Account> {\n    // Validate input\n    validateUpdateAccount(input);\n\n    // Check if account exists\n    const existing = this.accountRepo.findById(id);\n    if (!existing) {\n      throw new NotFoundError('Account not found', { id });\n    }\n\n    const updateData: any = {};\n\n    // Encrypt tokens if provided\n    if (input.tokens) {\n      updateData.tokens = encrypt(\n        JSON.stringify(input.tokens),\n        this.config.encryptionKey\n      );\n    }\n\n    // Update quota if provided\n    if (input.quota) {\n      updateData.quota = JSON.stringify(input.quota);\n    }\n\n    // Update in database\n    const updated = this.accountRepo.update(id, updateData);\n\n    this.logger.info({ accountId: id }, 'Account updated');\n\n    return this.deserializeAccount(updated);\n  }\n\n  /**\n   * Updates account tokens\n   */\n  async updateTokens(id: string, tokens: OAuthTokens): Promise<Account> {\n    return this.updateAccount(id, { tokens });\n  }\n\n  /**\n   * Updates account quota\n   */\n  async updateQuota(id: string, quota: AccountQuota): Promise<Account> {\n    return this.updateAccount(id, { quota });\n  }\n\n  /**\n   * Marks account as used (updates last_used timestamp)\n   */\n  markAccountUsed(id: string): void {\n    this.accountRepo.updateLastUsed(id);\n  }\n\n  /**\n   * Deletes an account\n   */\n  async deleteAccount(id: string): Promise<void> {\n    // Delete associated sessions first\n    this.sessionRepo.deleteByAccountId(id);\n\n    // Delete account\n    this.accountRepo.delete(id);\n\n    this.logger.info({ accountId: id }, 'Account deleted');\n  }\n\n  /**\n   * Gets account statistics\n   */\n  getAccountStats(): AccountStats {\n    const total = this.accountRepo.count();\n    const googleAccounts = this.accountRepo.findByProvider('google');\n    const anthropicAccounts = this.accountRepo.findByProvider('anthropic');\n    const activeSessions = this.sessionRepo.count();\n\n    // Find most recently used account\n    const allAccounts = this.accountRepo.findAll();\n    const lastUsedAt = allAccounts.reduce((max, acc) => {\n      if (!acc.last_used) return max;\n      return Math.max(max, acc.last_used);\n    }, 0);\n\n    return {\n      total,\n      by_provider: {\n        google: googleAccounts.length,\n        anthropic: anthropicAccounts.length,\n      },\n      active_sessions: activeSessions,\n      last_used_at: lastUsedAt || null,\n    };\n  }\n\n  /**\n   * Exports all accounts (for backup)\n   */\n  exportAccounts(): Account[] {\n    return this.getAllAccounts();\n  }\n\n  /**\n   * Imports accounts (for restore)\n   */\n  async importAccounts(accounts: CreateAccountInput[]): Promise<number> {\n    let imported = 0;\n\n    for (const account of accounts) {\n      try {\n        // Check if account already exists\n        const existing = this.accountRepo.findByEmail(account.email, account.provider);\n        if (existing) {\n          this.logger.warn(\n            { email: account.email, provider: account.provider },\n            'Account already exists, skipping'\n          );\n          continue;\n        }\n\n        await this.createAccount(account);\n        imported++;\n      } catch (error) {\n        this.logger.error({ error, account: account.email }, 'Failed to import account');\n      }\n    }\n\n    this.logger.info({ imported, total: accounts.length }, 'Accounts imported');\n    return imported;\n  }\n\n  /**\n   * Deserializes a database account to domain model\n   */\n  private deserializeAccount(dbAccount: any): Account {\n    let tokens: OAuthTokens;\n    try {\n      const decryptedTokens = decrypt(dbAccount.tokens, this.config.encryptionKey);\n      tokens = JSON.parse(decryptedTokens);\n    } catch (error) {\n      this.logger.error({ error, accountId: dbAccount.id }, 'Failed to decrypt tokens');\n      throw new AccountError('Failed to decrypt account tokens', { accountId: dbAccount.id });\n    }\n\n    let quota: AccountQuota;\n    try {\n      quota = JSON.parse(dbAccount.quota || '{}');\n    } catch (error) {\n      this.logger.error({ error, accountId: dbAccount.id }, 'Failed to parse quota');\n      quota = {};\n    }\n\n    return {\n      id: dbAccount.id,\n      provider: dbAccount.provider,\n      email: dbAccount.email,\n      tokens,\n      quota,\n      created_at: dbAccount.created_at,\n      updated_at: dbAccount.updated_at,\n      last_used: dbAccount.last_used,\n    };\n  }\n}\n","import { DB } from '../index.js';\nimport { Account } from '../types.js';\nimport { NotFoundError, DatabaseError } from '../../utils/errors.js';\n\nexport interface NewAccount {\n  id: string;\n  provider: 'google' | 'anthropic';\n  email: string;\n  tokens: string; // Encrypted JSON\n  quota?: string; // JSON\n}\n\nexport interface UpdateAccount {\n  tokens?: string;\n  quota?: string;\n  last_used?: number;\n}\n\n/**\n * Repository for account data access\n */\nexport class AccountRepository {\n  constructor(private db: DB) {}\n\n  /**\n   * Finds an account by ID\n   */\n  findById(id: string): Account | null {\n    try {\n      const stmt = this.db.prepare('SELECT * FROM accounts WHERE id = ?');\n      return stmt.get(id) as Account | null;\n    } catch (error) {\n      throw new DatabaseError('Failed to find account by ID', { error, id });\n    }\n  }\n\n  /**\n   * Finds an account by email and provider\n   */\n  findByEmail(email: string, provider: 'google' | 'anthropic'): Account | null {\n    try {\n      const stmt = this.db.prepare(\n        'SELECT * FROM accounts WHERE email = ? AND provider = ?'\n      );\n      return stmt.get(email, provider) as Account | null;\n    } catch (error) {\n      throw new DatabaseError('Failed to find account by email', { error, email, provider });\n    }\n  }\n\n  /**\n   * Finds all accounts\n   */\n  findAll(): Account[] {\n    try {\n      const stmt = this.db.prepare('SELECT * FROM accounts ORDER BY created_at DESC');\n      return stmt.all() as Account[];\n    } catch (error) {\n      throw new DatabaseError('Failed to find all accounts', { error });\n    }\n  }\n\n  /**\n   * Finds accounts by provider\n   */\n  findByProvider(provider: 'google' | 'anthropic'): Account[] {\n    try {\n      const stmt = this.db.prepare(\n        'SELECT * FROM accounts WHERE provider = ? ORDER BY created_at DESC'\n      );\n      return stmt.all(provider) as Account[];\n    } catch (error) {\n      throw new DatabaseError('Failed to find accounts by provider', { error, provider });\n    }\n  }\n\n  /**\n   * Creates a new account\n   */\n  create(account: NewAccount): Account {\n    try {\n      const now = Date.now();\n      const stmt = this.db.prepare(`\n        INSERT INTO accounts (id, provider, email, tokens, quota, created_at, updated_at, last_used)\n        VALUES (?, ?, ?, ?, ?, ?, ?, NULL)\n      `);\n\n      stmt.run(\n        account.id,\n        account.provider,\n        account.email,\n        account.tokens,\n        account.quota || '{}',\n        now,\n        now\n      );\n\n      const created = this.findById(account.id);\n      if (!created) {\n        throw new DatabaseError('Failed to retrieve created account', { id: account.id });\n      }\n      return created;\n    } catch (error) {\n      throw new DatabaseError('Failed to create account', { error, account });\n    }\n  }\n\n  /**\n   * Updates an account\n   */\n  update(id: string, data: UpdateAccount): Account {\n    try {\n      const updates: string[] = [];\n      const values: any[] = [];\n\n      if (data.tokens !== undefined) {\n        updates.push('tokens = ?');\n        values.push(data.tokens);\n      }\n      if (data.quota !== undefined) {\n        updates.push('quota = ?');\n        values.push(data.quota);\n      }\n      if (data.last_used !== undefined) {\n        updates.push('last_used = ?');\n        values.push(data.last_used);\n      }\n\n      updates.push('updated_at = ?');\n      values.push(Date.now());\n      values.push(id);\n\n      if (updates.length === 1) {\n        // Only updated_at, nothing to update\n        const existing = this.findById(id);\n        if (!existing) {\n          throw new NotFoundError('Account not found', { id });\n        }\n        return existing;\n      }\n\n      const stmt = this.db.prepare(\n        `UPDATE accounts SET ${updates.join(', ')} WHERE id = ?`\n      );\n      const result = stmt.run(...values);\n\n      if (result.changes === 0) {\n        throw new NotFoundError('Account not found', { id });\n      }\n\n      const updated = this.findById(id);\n      if (!updated) {\n        throw new DatabaseError('Failed to retrieve updated account', { id });\n      }\n      return updated;\n    } catch (error) {\n      if (error instanceof NotFoundError) throw error;\n      throw new DatabaseError('Failed to update account', { error, id, data });\n    }\n  }\n\n  /**\n   * Deletes an account\n   */\n  delete(id: string): void {\n    try {\n      const stmt = this.db.prepare('DELETE FROM accounts WHERE id = ?');\n      const result = stmt.run(id);\n\n      if (result.changes === 0) {\n        throw new NotFoundError('Account not found', { id });\n      }\n    } catch (error) {\n      if (error instanceof NotFoundError) throw error;\n      throw new DatabaseError('Failed to delete account', { error, id });\n    }\n  }\n\n  /**\n   * Counts total accounts\n   */\n  count(): number {\n    try {\n      const stmt = this.db.prepare('SELECT COUNT(*) as count FROM accounts');\n      const result = stmt.get() as { count: number };\n      return result.count;\n    } catch (error) {\n      throw new DatabaseError('Failed to count accounts', { error });\n    }\n  }\n\n  /**\n   * Updates last_used timestamp\n   */\n  updateLastUsed(id: string): void {\n    try {\n      const stmt = this.db.prepare('UPDATE accounts SET last_used = ? WHERE id = ?');\n      stmt.run(Date.now(), id);\n    } catch (error) {\n      throw new DatabaseError('Failed to update last_used', { error, id });\n    }\n  }\n}\n","import { DB } from '../index.js';\nimport { Session } from '../types.js';\nimport { NotFoundError, DatabaseError } from '../../utils/errors.js';\n\nexport interface NewSession {\n  id: string;\n  fingerprint: string;\n  account_id: string;\n  expires_at: number;\n}\n\n/**\n * Repository for session data access\n */\nexport class SessionRepository {\n  constructor(private db: DB) {}\n\n  /**\n   * Finds a session by ID\n   */\n  findById(id: string): Session | null {\n    try {\n      const stmt = this.db.prepare('SELECT * FROM sessions WHERE id = ?');\n      return stmt.get(id) as Session | null;\n    } catch (error) {\n      throw new DatabaseError('Failed to find session by ID', { error, id });\n    }\n  }\n\n  /**\n   * Finds a session by fingerprint\n   */\n  findByFingerprint(fingerprint: string): Session | null {\n    try {\n      const stmt = this.db.prepare(\n        'SELECT * FROM sessions WHERE fingerprint = ? AND expires_at > ? ORDER BY created_at DESC LIMIT 1'\n      );\n      return stmt.get(fingerprint, Date.now()) as Session | null;\n    } catch (error) {\n      throw new DatabaseError('Failed to find session by fingerprint', { error, fingerprint });\n    }\n  }\n\n  /**\n   * Finds all sessions for an account\n   */\n  findByAccountId(accountId: string): Session[] {\n    try {\n      const stmt = this.db.prepare(\n        'SELECT * FROM sessions WHERE account_id = ? ORDER BY created_at DESC'\n      );\n      return stmt.all(accountId) as Session[];\n    } catch (error) {\n      throw new DatabaseError('Failed to find sessions by account ID', { error, accountId });\n    }\n  }\n\n  /**\n   * Creates a new session\n   */\n  create(session: NewSession): Session {\n    try {\n      const now = Date.now();\n      const stmt = this.db.prepare(`\n        INSERT INTO sessions (id, fingerprint, account_id, created_at, expires_at)\n        VALUES (?, ?, ?, ?, ?)\n      `);\n\n      stmt.run(\n        session.id,\n        session.fingerprint,\n        session.account_id,\n        now,\n        session.expires_at\n      );\n\n      const created = this.findById(session.id);\n      if (!created) {\n        throw new DatabaseError('Failed to retrieve created session', { id: session.id });\n      }\n      return created;\n    } catch (error) {\n      throw new DatabaseError('Failed to create session', { error, session });\n    }\n  }\n\n  /**\n   * Deletes a session\n   */\n  delete(id: string): void {\n    try {\n      const stmt = this.db.prepare('DELETE FROM sessions WHERE id = ?');\n      const result = stmt.run(id);\n\n      if (result.changes === 0) {\n        throw new NotFoundError('Session not found', { id });\n      }\n    } catch (error) {\n      if (error instanceof NotFoundError) throw error;\n      throw new DatabaseError('Failed to delete session', { error, id });\n    }\n  }\n\n  /**\n   * Deletes all sessions for an account\n   */\n  deleteByAccountId(accountId: string): void {\n    try {\n      const stmt = this.db.prepare('DELETE FROM sessions WHERE account_id = ?');\n      stmt.run(accountId);\n    } catch (error) {\n      throw new DatabaseError('Failed to delete sessions by account ID', { error, accountId });\n    }\n  }\n\n  /**\n   * Deletes expired sessions\n   */\n  deleteExpired(): number {\n    try {\n      const stmt = this.db.prepare('DELETE FROM sessions WHERE expires_at <= ?');\n      const result = stmt.run(Date.now());\n      return result.changes;\n    } catch (error) {\n      throw new DatabaseError('Failed to delete expired sessions', { error });\n    }\n  }\n\n  /**\n   * Counts total sessions\n   */\n  count(): number {\n    try {\n      const stmt = this.db.prepare('SELECT COUNT(*) as count FROM sessions WHERE expires_at > ?');\n      const result = stmt.get(Date.now()) as { count: number };\n      return result.count;\n    } catch (error) {\n      throw new DatabaseError('Failed to count sessions', { error });\n    }\n  }\n}\n","import { z } from 'zod';\nimport { Provider, CreateAccountInput, UpdateAccountInput, ValidationResult } from './types.js';\nimport { ValidationError } from '../../utils/errors.js';\n\n/**\n * OAuth tokens schema\n */\nconst oauthTokensSchema = z.object({\n  access_token: z.string().min(1, 'Access token is required'),\n  refresh_token: z.string().optional(),\n  expires_at: z.number().optional(),\n  token_type: z.string().optional(),\n  scope: z.string().optional(),\n});\n\n/**\n * Account quota schema\n */\nconst accountQuotaSchema = z.object({\n  gemini_pro_rpm: z.number().optional(),\n  gemini_pro_rpd: z.number().optional(),\n  gemini_pro_remaining: z.number().optional(),\n  gemini_flash_rpm: z.number().optional(),\n  gemini_flash_rpd: z.number().optional(),\n  gemini_flash_remaining: z.number().optional(),\n  claude_tokens_remaining: z.number().optional(),\n  claude_requests_remaining: z.number().optional(),\n  last_quota_check: z.number().optional(),\n  quota_reset_at: z.number().optional(),\n});\n\n/**\n * Create account input schema\n */\nconst createAccountSchema = z.object({\n  provider: z.enum(['google', 'anthropic']),\n  email: z.string().email('Invalid email address'),\n  tokens: oauthTokensSchema,\n  quota: accountQuotaSchema.optional(),\n});\n\n/**\n * Update account input schema\n */\nconst updateAccountSchema = z.object({\n  tokens: oauthTokensSchema.optional(),\n  quota: accountQuotaSchema.optional(),\n});\n\n/**\n * Validates a provider value\n */\nexport function validateProvider(provider: string): provider is Provider {\n  return provider === 'google' || provider === 'anthropic';\n}\n\n/**\n * Validates an email address\n */\nexport function validateEmail(email: string): ValidationResult {\n  const errors: string[] = [];\n\n  if (!email || email.trim().length === 0) {\n    errors.push('Email is required');\n  } else {\n    // Basic email validation\n    const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n    if (!emailRegex.test(email)) {\n      errors.push('Invalid email format');\n    }\n  }\n\n  return {\n    valid: errors.length === 0,\n    errors,\n  };\n}\n\n/**\n * Validates OAuth tokens\n */\nexport function validateTokens(tokens: any): ValidationResult {\n  const errors: string[] = [];\n\n  if (!tokens) {\n    errors.push('Tokens are required');\n    return { valid: false, errors };\n  }\n\n  if (!tokens.access_token || typeof tokens.access_token !== 'string') {\n    errors.push('Access token is required and must be a string');\n  }\n\n  if (tokens.expires_at && typeof tokens.expires_at !== 'number') {\n    errors.push('Expires at must be a number');\n  }\n\n  // Check if token is expired\n  if (tokens.expires_at && tokens.expires_at < Date.now()) {\n    errors.push('Access token is expired');\n  }\n\n  return {\n    valid: errors.length === 0,\n    errors,\n  };\n}\n\n/**\n * Validates create account input\n */\nexport function validateCreateAccount(input: CreateAccountInput): void {\n  try {\n    createAccountSchema.parse(input);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid account data', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Validates update account input\n */\nexport function validateUpdateAccount(input: UpdateAccountInput): void {\n  try {\n    updateAccountSchema.parse(input);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid update data', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Checks if an account token is valid (not expired)\n */\nexport function isTokenValid(account: { tokens: { expires_at?: number } }): boolean {\n  if (!account.tokens.expires_at) {\n    // No expiration, assume valid\n    return true;\n  }\n\n  // Add 5 minute buffer\n  const buffer = 5 * 60 * 1000;\n  return account.tokens.expires_at > Date.now() + buffer;\n}\n\n/**\n * Checks if an account has sufficient quota\n */\nexport function hasQuota(quota: any, estimatedTokens: number = 0): boolean {\n  // If no quota info, assume available\n  if (!quota) return true;\n\n  // Track if we found any quota fields\n  let hasAnyQuotaField = false;\n  let hasAvailableQuota = false;\n\n  // Check Claude quota\n  if (quota.claude_tokens_remaining !== undefined) {\n    hasAnyQuotaField = true;\n    if (quota.claude_tokens_remaining > 0 && quota.claude_tokens_remaining >= estimatedTokens) {\n      hasAvailableQuota = true;\n    }\n  }\n\n  // Check Gemini Pro quota\n  if (quota.gemini_pro_remaining !== undefined) {\n    hasAnyQuotaField = true;\n    if (quota.gemini_pro_remaining > 0) {\n      hasAvailableQuota = true;\n    }\n  }\n\n  // Check Gemini Flash quota\n  if (quota.gemini_flash_remaining !== undefined) {\n    hasAnyQuotaField = true;\n    if (quota.gemini_flash_remaining > 0) {\n      hasAvailableQuota = true;\n    }\n  }\n\n  // If we found quota fields, return whether any have available quota\n  // If no quota fields found, assume available\n  return hasAnyQuotaField ? hasAvailableQuota : true;\n}\n","import { createCipheriv, createDecipheriv, randomBytes, createHash } from 'crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 16;\nconst SALT_LENGTH = 32;\n\n/**\n * Encrypts data using AES-256-GCM\n */\nexport function encrypt(text: string, key: string): string {\n  const salt = randomBytes(SALT_LENGTH);\n  const derivedKey = deriveKey(key, salt);\n  const iv = randomBytes(IV_LENGTH);\n  const cipher = createCipheriv(ALGORITHM, derivedKey, iv);\n\n  let encrypted = cipher.update(text, 'utf8', 'hex');\n  encrypted += cipher.final('hex');\n\n  const authTag = cipher.getAuthTag();\n\n  // Format: salt:iv:authTag:encrypted\n  return [\n    salt.toString('hex'),\n    iv.toString('hex'),\n    authTag.toString('hex'),\n    encrypted,\n  ].join(':');\n}\n\n/**\n * Decrypts data encrypted with AES-256-GCM\n */\nexport function decrypt(encryptedData: string, key: string): string {\n  const parts = encryptedData.split(':');\n  if (parts.length !== 4) {\n    throw new Error('Invalid encrypted data format');\n  }\n\n  const [saltHex, ivHex, authTagHex, encrypted] = parts;\n  if (!saltHex || !ivHex || !authTagHex || !encrypted) {\n    throw new Error('Invalid encrypted data format');\n  }\n\n  const salt = Buffer.from(saltHex, 'hex');\n  const iv = Buffer.from(ivHex, 'hex');\n  const authTag = Buffer.from(authTagHex, 'hex');\n  const derivedKey = deriveKey(key, salt);\n\n  const decipher = createDecipheriv(ALGORITHM, derivedKey, iv);\n  decipher.setAuthTag(authTag);\n\n  let decrypted = decipher.update(encrypted, 'hex', 'utf8');\n  decrypted += decipher.final('utf8');\n\n  return decrypted;\n}\n\n/**\n * Derives a key from password and salt using SHA-256\n */\nfunction deriveKey(password: string, salt: Buffer): Buffer {\n  return createHash('sha256')\n    .update(password)\n    .update(salt)\n    .digest();\n}\n\n/**\n * Generates a SHA-256 hash of the input\n */\nexport function hash(data: string): string {\n  return createHash('sha256').update(data).digest('hex');\n}\n\n/**\n * Generates a random token\n */\nexport function generateToken(length: number = 32): string {\n  return randomBytes(length).toString('hex');\n}\n\n/**\n * Creates a session fingerprint from request metadata\n */\nexport function createFingerprint(metadata: Record<string, string>): string {\n  const sorted = Object.keys(metadata)\n    .sort()\n    .map((key) => `${key}=${metadata[key]}`)\n    .join('|');\n  return hash(sorted);\n}\n","import pino, { Logger as PinoLogger } from 'pino';\n\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport interface LoggerConfig {\n  level?: LogLevel;\n  pretty?: boolean;\n  name?: string;\n}\n\n/**\n * Creates a configured Pino logger instance\n */\nexport function createLogger(config: LoggerConfig = {}): PinoLogger {\n  const {\n    level = (process.env.LOG_LEVEL as LogLevel) || 'info',\n    pretty = process.env.NODE_ENV !== 'production',\n    name = 'antigravity',\n  } = config;\n\n  const options: pino.LoggerOptions = {\n    name,\n    level,\n    timestamp: pino.stdTimeFunctions.isoTime,\n    formatters: {\n      level: (label) => ({ level: label }),\n    },\n  };\n\n  if (pretty) {\n    return pino(\n      options,\n      pino.transport({\n        target: 'pino-pretty',\n        options: {\n          colorize: true,\n          translateTime: 'yyyy-mm-dd HH:MM:ss.l',\n          ignore: 'pid,hostname',\n          singleLine: false,\n        },\n      })\n    );\n  }\n\n  return pino(options);\n}\n\n/**\n * Global logger instance\n */\nlet globalLogger: PinoLogger | null = null;\n\n/**\n * Gets or creates the global logger instance\n */\nexport function getLogger(config?: LoggerConfig): PinoLogger {\n  if (!globalLogger) {\n    globalLogger = createLogger(config);\n  }\n  return globalLogger;\n}\n\n/**\n * Sets a custom global logger instance\n */\nexport function setLogger(logger: PinoLogger): void {\n  globalLogger = logger;\n}\n\n/**\n * Creates a child logger with additional context\n */\nexport function createChildLogger(\n  parent: PinoLogger,\n  bindings: Record<string, unknown>\n): PinoLogger {\n  return parent.child(bindings);\n}\n\nexport type Logger = PinoLogger;\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\nimport { DB } from '../../database/index.js';\nimport { ConfigRepository } from '../../database/repositories/config.js';\nimport { AppConfig, DEFAULT_CONFIG, ServerConfig, ProxyConfig, TokenConfig, QuotaConfig } from './types.js';\nimport {\n  validateAppConfig,\n  validateServerConfig,\n  validateProxyConfig,\n  validateTokenConfig,\n  validateQuotaConfig,\n} from './validator.js';\nimport { ConfigError } from '../../utils/errors.js';\nimport { getLogger } from '../../utils/logger.js';\n\nexport interface ConfigManagerOptions {\n  configPath?: string;\n  useDatabase?: boolean;\n}\n\n/**\n * Configuration Manager - handles app configuration with file and database persistence\n */\nexport class ConfigManager {\n  private configRepo: ConfigRepository;\n  private logger = getLogger().child({ module: 'ConfigManager' });\n  private configPath: string;\n  private useDatabase: boolean;\n  private cachedConfig: AppConfig | null = null;\n\n  constructor(db: DB, options: ConfigManagerOptions = {}) {\n    this.configRepo = new ConfigRepository(db);\n    this.configPath = options.configPath || join(homedir(), '.antigravity', 'config.json');\n    this.useDatabase = options.useDatabase ?? true;\n  }\n\n  /**\n   * Loads configuration from file or database\n   */\n  loadConfig(): AppConfig {\n    if (this.cachedConfig) {\n      return this.cachedConfig;\n    }\n\n    let config: AppConfig;\n\n    if (this.useDatabase) {\n      config = this.loadFromDatabase();\n    } else {\n      config = this.loadFromFile();\n    }\n\n    // Validate loaded config\n    validateAppConfig(config);\n\n    this.cachedConfig = config;\n    this.logger.info('Configuration loaded');\n    return config;\n  }\n\n  /**\n   * Saves configuration to file and/or database\n   */\n  saveConfig(config: AppConfig): void {\n    // Validate before saving\n    validateAppConfig(config);\n\n    if (this.useDatabase) {\n      this.saveToDatabase(config);\n    }\n\n    this.saveToFile(config);\n    this.cachedConfig = config;\n    this.logger.info('Configuration saved');\n  }\n\n  /**\n   * Gets the current configuration\n   */\n  getConfig(): AppConfig {\n    if (!this.cachedConfig) {\n      return this.loadConfig();\n    }\n    return this.cachedConfig;\n  }\n\n  /**\n   * Updates a specific configuration section\n   */\n  updateServerConfig(config: Partial<ServerConfig>): void {\n    const current = this.getConfig();\n    const updated = { ...current, server: { ...current.server, ...config } };\n    validateServerConfig(updated.server);\n    this.saveConfig(updated);\n  }\n\n  /**\n   * Updates proxy configuration\n   */\n  updateProxyConfig(config: Partial<ProxyConfig>): void {\n    const current = this.getConfig();\n    const updated = { ...current, proxy: { ...current.proxy, ...config } };\n    validateProxyConfig(updated.proxy);\n    this.saveConfig(updated);\n  }\n\n  /**\n   * Updates token configuration\n   */\n  updateTokenConfig(config: Partial<TokenConfig>): void {\n    const current = this.getConfig();\n    const updated = { ...current, token: { ...current.token, ...config } };\n    validateTokenConfig(updated.token);\n    this.saveConfig(updated);\n  }\n\n  /**\n   * Updates quota configuration\n   */\n  updateQuotaConfig(config: Partial<QuotaConfig>): void {\n    const current = this.getConfig();\n    const updated = { ...current, quota: { ...current.quota, ...config } };\n    validateQuotaConfig(updated.quota);\n    this.saveConfig(updated);\n  }\n\n  /**\n   * Gets a specific configuration value\n   */\n  get<T = any>(key: string): T | null {\n    const config = this.getConfig();\n    const keys = key.split('.');\n    let value: any = config;\n\n    for (const k of keys) {\n      if (value && typeof value === 'object' && k in value) {\n        value = value[k];\n      } else {\n        return null;\n      }\n    }\n\n    return value as T;\n  }\n\n  /**\n   * Sets a specific configuration value\n   */\n  set(key: string, value: any): void {\n    const config = this.getConfig();\n    const keys = key.split('.');\n    let current: any = config;\n\n    for (let i = 0; i < keys.length - 1; i++) {\n      const k = keys[i];\n      if (k && (!current[k] || typeof current[k] !== 'object')) {\n        current[k] = {};\n      }\n      if (k) {\n        current = current[k];\n      }\n    }\n\n    const lastKey = keys[keys.length - 1];\n    if (lastKey) {\n      current[lastKey] = value;\n    }\n    this.saveConfig(config);\n  }\n\n  /**\n   * Resets configuration to defaults\n   */\n  resetToDefaults(): void {\n    this.saveConfig(DEFAULT_CONFIG);\n    this.logger.info('Configuration reset to defaults');\n  }\n\n  /**\n   * Exports configuration as JSON\n   */\n  exportConfig(): string {\n    const config = this.getConfig();\n    return JSON.stringify(config, null, 2);\n  }\n\n  /**\n   * Imports configuration from JSON\n   */\n  importConfig(json: string): void {\n    try {\n      const config = JSON.parse(json);\n      validateAppConfig(config);\n      this.saveConfig(config);\n      this.logger.info('Configuration imported');\n    } catch (error) {\n      throw new ConfigError('Failed to import configuration', { error });\n    }\n  }\n\n  /**\n   * Loads configuration from file\n   */\n  private loadFromFile(): AppConfig {\n    try {\n      if (existsSync(this.configPath)) {\n        const content = readFileSync(this.configPath, 'utf-8');\n        const config = JSON.parse(content);\n        return { ...DEFAULT_CONFIG, ...config };\n      }\n\n      // File doesn't exist, use defaults\n      this.logger.info('Config file not found, using defaults');\n      return DEFAULT_CONFIG;\n    } catch (error) {\n      this.logger.error({ error }, 'Failed to load config from file, using defaults');\n      return DEFAULT_CONFIG;\n    }\n  }\n\n  /**\n   * Saves configuration to file\n   */\n  private saveToFile(config: AppConfig): void {\n    try {\n      const dir = dirname(this.configPath);\n      if (!existsSync(dir)) {\n        mkdirSync(dir, { recursive: true });\n      }\n\n      writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8');\n    } catch (error) {\n      throw new ConfigError('Failed to save config to file', { error });\n    }\n  }\n\n  /**\n   * Loads configuration from database\n   */\n  private loadFromDatabase(): AppConfig {\n    try {\n      const dbConfig = this.configRepo.getJSON<AppConfig>('app_config');\n      if (dbConfig) {\n        return { ...DEFAULT_CONFIG, ...dbConfig };\n      }\n\n      // Not in database, use defaults\n      this.logger.info('Config not found in database, using defaults');\n      return DEFAULT_CONFIG;\n    } catch (error) {\n      this.logger.error({ error }, 'Failed to load config from database, using defaults');\n      return DEFAULT_CONFIG;\n    }\n  }\n\n  /**\n   * Saves configuration to database\n   */\n  private saveToDatabase(config: AppConfig): void {\n    try {\n      this.configRepo.setJSON('app_config', config);\n    } catch (error) {\n      throw new ConfigError('Failed to save config to database', { error });\n    }\n  }\n\n  /**\n   * Clears cached configuration\n   */\n  clearCache(): void {\n    this.cachedConfig = null;\n  }\n}\n","import { DB } from '../index.js';\nimport { Config } from '../types.js';\nimport { NotFoundError, DatabaseError } from '../../utils/errors.js';\n\n/**\n * Repository for config data access\n */\nexport class ConfigRepository {\n  constructor(private db: DB) {}\n\n  /**\n   * Gets a config value by key\n   */\n  get(key: string): string | null {\n    try {\n      const stmt = this.db.prepare('SELECT value FROM config WHERE key = ?');\n      const result = stmt.get(key) as { value: string } | undefined;\n      return result?.value || null;\n    } catch (error) {\n      throw new DatabaseError('Failed to get config', { error, key });\n    }\n  }\n\n  /**\n   * Gets a parsed JSON config value\n   */\n  getJSON<T>(key: string): T | null {\n    const value = this.get(key);\n    if (!value) return null;\n    try {\n      return JSON.parse(value) as T;\n    } catch (error) {\n      throw new DatabaseError('Failed to parse config JSON', { error, key });\n    }\n  }\n\n  /**\n   * Sets a config value\n   */\n  set(key: string, value: string): void {\n    try {\n      const stmt = this.db.prepare(`\n        INSERT INTO config (key, value, updated_at)\n        VALUES (?, ?, ?)\n        ON CONFLICT(key) DO UPDATE SET\n          value = excluded.value,\n          updated_at = excluded.updated_at\n      `);\n      stmt.run(key, value, Date.now());\n    } catch (error) {\n      throw new DatabaseError('Failed to set config', { error, key });\n    }\n  }\n\n  /**\n   * Sets a JSON config value\n   */\n  setJSON(key: string, value: any): void {\n    try {\n      this.set(key, JSON.stringify(value));\n    } catch (error) {\n      throw new DatabaseError('Failed to set config JSON', { error, key });\n    }\n  }\n\n  /**\n   * Deletes a config value\n   */\n  delete(key: string): void {\n    try {\n      const stmt = this.db.prepare('DELETE FROM config WHERE key = ?');\n      const result = stmt.run(key);\n\n      if (result.changes === 0) {\n        throw new NotFoundError('Config key not found', { key });\n      }\n    } catch (error) {\n      if (error instanceof NotFoundError) throw error;\n      throw new DatabaseError('Failed to delete config', { error, key });\n    }\n  }\n\n  /**\n   * Gets all config entries\n   */\n  getAll(): Config[] {\n    try {\n      const stmt = this.db.prepare('SELECT * FROM config ORDER BY key');\n      return stmt.all() as Config[];\n    } catch (error) {\n      throw new DatabaseError('Failed to get all config', { error });\n    }\n  }\n\n  /**\n   * Checks if a key exists\n   */\n  has(key: string): boolean {\n    try {\n      const stmt = this.db.prepare('SELECT 1 FROM config WHERE key = ?');\n      return stmt.get(key) !== undefined;\n    } catch (error) {\n      throw new DatabaseError('Failed to check config key', { error, key });\n    }\n  }\n\n  /**\n   * Clears all config entries\n   */\n  clear(): void {\n    try {\n      this.db.exec('DELETE FROM config');\n    } catch (error) {\n      throw new DatabaseError('Failed to clear config', { error });\n    }\n  }\n}\n","/**\n * Configuration types and interfaces\n */\n\nexport interface ServerConfig {\n  port: number;\n  host: string;\n  cors?: {\n    origins: string[];\n    credentials: boolean;\n  };\n}\n\nexport interface ProxyConfig {\n  maxConcurrentRequests: number;\n  requestTimeout: number;\n  retryAttempts: number;\n  enableLogging: boolean;\n}\n\nexport interface TokenConfig {\n  rotationStrategy: 'round-robin' | 'weighted-round-robin' | 'random';\n  healthCheckInterval: number;\n  refreshInterval: number;\n}\n\nexport interface QuotaConfig {\n  checkInterval: number;\n  warningThreshold: number;\n  enableMonitoring: boolean;\n}\n\nexport interface AppConfig {\n  server: ServerConfig;\n  proxy: ProxyConfig;\n  token: TokenConfig;\n  quota: QuotaConfig;\n}\n\nexport const DEFAULT_CONFIG: AppConfig = {\n  server: {\n    port: 8080,\n    host: '127.0.0.1',\n    cors: {\n      origins: ['http://localhost:*', 'http://127.0.0.1:*'],\n      credentials: true,\n    },\n  },\n  proxy: {\n    maxConcurrentRequests: 100,\n    requestTimeout: 60000,\n    retryAttempts: 3,\n    enableLogging: true,\n  },\n  token: {\n    rotationStrategy: 'weighted-round-robin',\n    healthCheckInterval: 300000, // 5 minutes\n    refreshInterval: 3600000, // 1 hour\n  },\n  quota: {\n    checkInterval: 60000, // 1 minute\n    warningThreshold: 0.8, // 80%\n    enableMonitoring: true,\n  },\n};\n","import { z } from 'zod';\nimport { ValidationError } from '../../utils/errors.js';\n\n/**\n * Server configuration schema\n */\nconst serverConfigSchema = z.object({\n  port: z.number().int().min(1).max(65535),\n  host: z.string().min(1),\n  cors: z\n    .object({\n      origins: z.array(z.string()),\n      credentials: z.boolean(),\n    })\n    .optional(),\n});\n\n/**\n * Proxy configuration schema\n */\nconst proxyConfigSchema = z.object({\n  maxConcurrentRequests: z.number().int().min(1),\n  requestTimeout: z.number().int().min(1000),\n  retryAttempts: z.number().int().min(0).max(10),\n  enableLogging: z.boolean(),\n});\n\n/**\n * Token configuration schema\n */\nconst tokenConfigSchema = z.object({\n  rotationStrategy: z.enum(['round-robin', 'weighted-round-robin', 'random']),\n  healthCheckInterval: z.number().int().min(1000),\n  refreshInterval: z.number().int().min(1000),\n});\n\n/**\n * Quota configuration schema\n */\nconst quotaConfigSchema = z.object({\n  checkInterval: z.number().int().min(1000),\n  warningThreshold: z.number().min(0).max(1),\n  enableMonitoring: z.boolean(),\n});\n\n/**\n * Full app configuration schema\n */\nconst appConfigSchema = z.object({\n  server: serverConfigSchema,\n  proxy: proxyConfigSchema,\n  token: tokenConfigSchema,\n  quota: quotaConfigSchema,\n});\n\n/**\n * Validates server configuration\n */\nexport function validateServerConfig(config: any): void {\n  try {\n    serverConfigSchema.parse(config);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid server configuration', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Validates proxy configuration\n */\nexport function validateProxyConfig(config: any): void {\n  try {\n    proxyConfigSchema.parse(config);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid proxy configuration', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Validates token configuration\n */\nexport function validateTokenConfig(config: any): void {\n  try {\n    tokenConfigSchema.parse(config);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid token configuration', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Validates quota configuration\n */\nexport function validateQuotaConfig(config: any): void {\n  try {\n    quotaConfigSchema.parse(config);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid quota configuration', { errors });\n    }\n    throw error;\n  }\n}\n\n/**\n * Validates full app configuration\n */\nexport function validateAppConfig(config: any): void {\n  try {\n    appConfigSchema.parse(config);\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      const errors = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);\n      throw new ValidationError('Invalid application configuration', { errors });\n    }\n    throw error;\n  }\n}\n","import {\n  SchedulingMode,\n  TokenSelectionOptions,\n  SelectedToken,\n  TokenManagerConfig,\n} from './types.js';\nimport { RateLimitTracker } from './rate-limit.js';\nimport { QuotaMonitor } from '../quota/monitor.js';\nimport { AccountManager } from '../account/manager.js';\nimport { GoogleOAuthProvider } from '../oauth/google.js';\nimport { Account } from '../account/types.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { AntigravityError } from '../../utils/errors.js';\n\nconst logger = getLogger().child({ module: 'TokenManager' });\n\n/**\n * Token Manager\n *\n * Manages token selection with smart rotation, sticky sessions, and rate limit avoidance.\n */\nexport class TokenManager {\n  private accountManager: AccountManager;\n  private rateLimitTracker: RateLimitTracker;\n  private quotaMonitor: QuotaMonitor;\n  private oauthProvider: GoogleOAuthProvider;\n\n  private schedulingMode: SchedulingMode;\n  private maxWaitSeconds: number;\n  private globalLockSeconds: number;\n  private refreshThresholdSeconds: number;\n\n  // Session binding: sessionId -> accountId\n  private sessionAccounts: Map<string, string> = new Map();\n\n  // Global lock: last used account and timestamp\n  private lastUsedAccount: { accountId: string; timestamp: number } | null = null;\n\n  // Round-robin index\n  private currentIndex: number = 0;\n\n  constructor(\n    accountManager: AccountManager,\n    oauthProvider: GoogleOAuthProvider,\n    config: TokenManagerConfig = {}\n  ) {\n    this.accountManager = accountManager;\n    this.oauthProvider = oauthProvider;\n    this.rateLimitTracker = new RateLimitTracker();\n    this.quotaMonitor = new QuotaMonitor();\n\n    this.schedulingMode = config.schedulingMode || SchedulingMode.Balance;\n    this.maxWaitSeconds = config.maxWaitSeconds || 30;\n    this.globalLockSeconds = config.globalLockSeconds || 60;\n    this.refreshThresholdSeconds = config.refreshThresholdSeconds || 300; // 5 minutes\n  }\n\n  /**\n   * Get token for a request\n   */\n  async getToken(options: TokenSelectionOptions): Promise<SelectedToken> {\n    const { quotaGroup, forceRotate = false, sessionId } = options;\n\n    // Get all accounts and sort by subscription tier\n    const allAccounts = this.accountManager.getAllAccounts();\n    if (allAccounts.length === 0) {\n      throw new AntigravityError('No accounts available', 'NO_ACCOUNTS');\n    }\n\n    const sortedAccounts = this.sortAccountsByTier(allAccounts);\n    const attempted = new Set<string>();\n    let lastError: string | null = null;\n\n    for (let attempt = 0; attempt < sortedAccounts.length; attempt++) {\n      const rotate = forceRotate || attempt > 0;\n\n      // Select account based on strategy\n      let selectedAccount = await this.selectAccount(\n        sortedAccounts,\n        attempted,\n        sessionId,\n        quotaGroup,\n        rotate\n      );\n\n      if (!selectedAccount) {\n        // All accounts are rate-limited or attempted\n        const minWait = this.getMinimumWaitTime(sortedAccounts);\n        throw new AntigravityError(\n          `All accounts are currently limited or unhealthy. Please wait ${minWait}s.`,\n          'ALL_ACCOUNTS_LIMITED',\n          429\n        );\n      }\n\n      // Check token expiry and refresh if needed\n      try {\n        await this.ensureTokenValid(selectedAccount);\n      } catch (error) {\n        logger.error({ email: selectedAccount.email, error }, 'Token refresh failed');\n\n        // Mark as attempted and try next account\n        attempted.add(selectedAccount.id);\n\n        // Clear global lock if this was the locked account\n        if (this.lastUsedAccount?.accountId === selectedAccount.id) {\n          this.lastUsedAccount = null;\n        }\n\n        lastError = `Token refresh failed: ${error}`;\n        continue;\n      }\n\n      // Ensure project ID exists\n      try {\n        await this.ensureProjectId(selectedAccount);\n      } catch (error) {\n        logger.error({ email: selectedAccount.email, error }, 'Failed to fetch project ID');\n\n        attempted.add(selectedAccount.id);\n\n        if (this.lastUsedAccount?.accountId === selectedAccount.id) {\n          this.lastUsedAccount = null;\n        }\n\n        lastError = `Failed to fetch project ID: ${error}`;\n        continue;\n      }\n\n      // Success!\n      return {\n        accessToken: selectedAccount.tokens.access_token,\n        projectId: selectedAccount.projectId || 'bamboo-precept-lgxtn',\n        email: selectedAccount.email,\n        accountId: selectedAccount.id,\n      };\n    }\n\n    throw new AntigravityError(lastError || 'All accounts failed', 'TOKEN_SELECTION_FAILED');\n  }\n\n  /**\n   * Select account based on strategy\n   */\n  private async selectAccount(\n    accounts: Account[],\n    attempted: Set<string>,\n    sessionId: string | undefined,\n    quotaGroup: string,\n    rotate: boolean\n  ): Promise<Account | null> {\n    // Strategy A: Sticky session (if session exists and not rotating and not PerformanceFirst)\n    if (!rotate && sessionId && this.schedulingMode !== SchedulingMode.PerformanceFirst) {\n      const boundAccountId = this.sessionAccounts.get(sessionId);\n\n      if (boundAccountId && !attempted.has(boundAccountId)) {\n        const boundAccount = accounts.find(a => a.id === boundAccountId);\n\n        if (boundAccount) {\n          const resetSec = this.rateLimitTracker.getRemainingWait(boundAccountId);\n\n          if (resetSec > 0) {\n            // Account is rate-limited\n            if (this.schedulingMode === SchedulingMode.CacheFirst && resetSec <= this.maxWaitSeconds) {\n              // Wait for the account to preserve cache\n              logger.warn(\n                { sessionId, email: boundAccount.email, resetSec },\n                'Cache-first: Waiting for bound account to preserve cache'\n              );\n\n              await this.sleep(resetSec * 1000);\n\n              logger.debug({ sessionId, email: boundAccount.email }, 'Successfully recovered bound account');\n              return boundAccount;\n            } else {\n              // Wait too long or Balance mode - unbind\n              logger.warn(\n                { sessionId, email: boundAccount.email, resetSec, maxWait: this.maxWaitSeconds },\n                'Switching from bound account (wait time too long)'\n              );\n              this.sessionAccounts.delete(sessionId);\n            }\n          } else {\n            // Account is available\n            logger.debug({ sessionId, email: boundAccount.email }, 'Reusing bound account');\n            return boundAccount;\n          }\n        }\n      }\n    }\n\n    // Strategy B: Global 60s lock (for non-session requests or no bound account)\n    if (!rotate && quotaGroup !== 'image_gen') {\n      if (this.lastUsedAccount) {\n        const age = Date.now() - this.lastUsedAccount.timestamp;\n\n        if (age < this.globalLockSeconds * 1000 && !attempted.has(this.lastUsedAccount.accountId)) {\n          const lockedAccount = accounts.find(a => a.id === this.lastUsedAccount!.accountId);\n\n          if (lockedAccount && !this.rateLimitTracker.isRateLimited(lockedAccount.id)) {\n            logger.debug({ email: lockedAccount.email }, '60s window: Force reusing last account');\n            return lockedAccount;\n          }\n        }\n      }\n    }\n\n    // Strategy C: Round-robin selection with rate limit avoidance\n    const startIdx = this.currentIndex;\n\n    for (let offset = 0; offset < accounts.length; offset++) {\n      const idx = (startIdx + offset) % accounts.length;\n      const candidate = accounts[idx];\n\n      if (!candidate) continue;\n\n      if (attempted.has(candidate.id)) {\n        continue;\n      }\n\n      // Skip rate-limited accounts\n      if (this.rateLimitTracker.isRateLimited(candidate.id)) {\n        continue;\n      }\n\n      // Found available account\n      this.currentIndex = (idx + 1) % accounts.length;\n\n      // Update global lock\n      if (!rotate && quotaGroup !== 'image_gen') {\n        this.lastUsedAccount = {\n          accountId: candidate.id,\n          timestamp: Date.now(),\n        };\n      }\n\n      // Bind session if needed\n      if (sessionId && this.schedulingMode !== SchedulingMode.PerformanceFirst) {\n        this.sessionAccounts.set(sessionId, candidate.id);\n        logger.debug({ sessionId, email: candidate.email }, 'Bound new account to session');\n      }\n\n      if (rotate) {\n        logger.debug({ email: candidate.email }, 'Force rotation: Switched to account');\n      }\n\n      return candidate;\n    }\n\n    return null;\n  }\n\n  /**\n   * Ensure token is valid (refresh if needed)\n   */\n  private async ensureTokenValid(account: Account): Promise<void> {\n    const expiresAt = account.tokens.expires_at;\n    if (!expiresAt) return;\n\n    const now = Date.now();\n    const expiresIn = (expiresAt - now) / 1000;\n\n    // Refresh if expires in less than threshold\n    if (expiresIn < this.refreshThresholdSeconds) {\n      logger.debug({ email: account.email, expiresIn }, 'Token expiring soon, refreshing');\n\n      try {\n        const newTokens = await this.oauthProvider.refreshToken(account.tokens.refresh_token!);\n\n        // Update account with new tokens\n        await this.accountManager.updateAccount(account.id, {\n          tokens: newTokens,\n        });\n\n        // Update in-memory reference\n        account.tokens = newTokens;\n\n        logger.info({ email: account.email }, 'Token refreshed successfully');\n      } catch (error) {\n        // Check for invalid_grant error\n        const errorMessage = String(error);\n        if (errorMessage.includes('invalid_grant')) {\n          logger.error({ email: account.email }, 'Token refresh failed: invalid_grant - disabling account');\n\n          // Disable account\n          await this.accountManager.updateAccount(account.id, {\n            disabled: true,\n          });\n\n          // Remove from token manager\n          throw new AntigravityError('Account disabled due to invalid_grant', 'INVALID_GRANT', 401);\n        }\n\n        throw error;\n      }\n    }\n  }\n\n  /**\n   * Ensure account has project ID\n   */\n  private async ensureProjectId(account: Account): Promise<void> {\n    if (account.projectId) return;\n\n    logger.debug({ email: account.email }, 'Account missing project ID, fetching');\n\n    const { projectId, subscriptionTier } = await this.quotaMonitor.fetchProjectInfo(\n      account.tokens.access_token,\n      account.email\n    );\n\n    if (projectId) {\n      // Update account\n      await this.accountManager.updateAccount(account.id, {\n        projectId,\n        quota: {\n          ...account.quota,\n          subscription_tier: subscriptionTier || undefined,\n        },\n      });\n\n      // Update in-memory reference\n      account.projectId = projectId;\n      if (subscriptionTier && account.quota.subscription_tier !== undefined) {\n        account.quota.subscription_tier = subscriptionTier;\n      }\n\n      logger.info({ email: account.email, projectId }, 'Project ID fetched and saved');\n    } else {\n      logger.warn({ email: account.email }, 'Failed to fetch project ID, using default');\n    }\n  }\n\n  /**\n   * Sort accounts by subscription tier (ULTRA > PRO > FREE)\n   */\n  private sortAccountsByTier(accounts: Account[]): Account[] {\n    const tierPriority = (tier: string | undefined): number => {\n      if (tier === 'ULTRA') return 0;\n      if (tier === 'PRO') return 1;\n      if (tier === 'FREE') return 2;\n      return 3;\n    };\n\n    return [...accounts].sort((a, b) => {\n      const aTier = a.quota.subscription_tier;\n      const bTier = b.quota.subscription_tier;\n      return tierPriority(aTier) - tierPriority(bTier);\n    });\n  }\n\n  /**\n   * Get minimum wait time across all accounts\n   */\n  private getMinimumWaitTime(accounts: Account[]): number {\n    const waitTimes = accounts\n      .map(a => this.rateLimitTracker.getRemainingWait(a.id))\n      .filter(t => t > 0);\n\n    return waitTimes.length > 0 ? Math.min(...waitTimes) : 60;\n  }\n\n  /**\n   * Mark account as rate-limited\n   */\n  markRateLimited(\n    accountId: string,\n    status: number,\n    retryAfter: string | null,\n    errorBody: string\n  ): void {\n    this.rateLimitTracker.parseFromError(accountId, status, retryAfter, errorBody);\n  }\n\n  /**\n   * Clear session binding\n   */\n  clearSessionBinding(sessionId: string): void {\n    this.sessionAccounts.delete(sessionId);\n  }\n\n  /**\n   * Clear all session bindings\n   */\n  clearAllSessions(): void {\n    this.sessionAccounts.clear();\n  }\n\n  /**\n   * Get scheduling mode\n   */\n  getSchedulingMode(): SchedulingMode {\n    return this.schedulingMode;\n  }\n\n  /**\n   * Update scheduling mode\n   */\n  setSchedulingMode(mode: SchedulingMode): void {\n    this.schedulingMode = mode;\n    logger.info({ mode }, 'Scheduling mode updated');\n  }\n\n  /**\n   * Get rate limit tracker (for external use)\n   */\n  getRateLimitTracker(): RateLimitTracker {\n    return this.rateLimitTracker;\n  }\n\n  /**\n   * Get quota monitor (for external use)\n   */\n  getQuotaMonitor(): QuotaMonitor {\n    return this.quotaMonitor;\n  }\n\n  /**\n   * Sleep utility\n   */\n  private sleep(ms: number): Promise<void> {\n    return new Promise(resolve => setTimeout(resolve, ms));\n  }\n}\n","/**\n * Token Management Types\n */\n\n/**\n * Scheduling mode for token selection\n */\nexport enum SchedulingMode {\n  /**\n   * Cache-first: Wait for rate-limited account to preserve cache (up to max_wait_seconds)\n   */\n  CacheFirst = 'CACHE_FIRST',\n\n  /**\n   * Balance: Switch if wait time exceeds threshold\n   */\n  Balance = 'BALANCE',\n\n  /**\n   * Performance-first: Never wait, always rotate to available account\n   */\n  PerformanceFirst = 'PERFORMANCE_FIRST',\n}\n\n/**\n * Rate limit reason types\n */\nexport enum RateLimitReason {\n  QuotaExhausted = 'QUOTA_EXHAUSTED',\n  RateLimitExceeded = 'RATE_LIMIT_EXCEEDED',\n  ServerError = 'SERVER_ERROR',\n  Unknown = 'UNKNOWN',\n}\n\n/**\n * Rate limit information\n */\nexport interface RateLimitInfo {\n  resetTime: Date;\n  retryAfterSec: number;\n  detectedAt: Date;\n  reason: RateLimitReason;\n}\n\n/**\n * Token selection options\n */\nexport interface TokenSelectionOptions {\n  /**\n   * Quota group for the request ('claude' | 'gemini' | 'image_gen')\n   */\n  quotaGroup: string;\n\n  /**\n   * Force rotation to next account (ignore sticky sessions)\n   */\n  forceRotate?: boolean;\n\n  /**\n   * Session ID for sticky session support\n   */\n  sessionId?: string;\n\n  /**\n   * Estimated tokens for the request (for quota checking)\n   */\n  estimatedTokens?: number;\n}\n\n/**\n * Selected token result\n */\nexport interface SelectedToken {\n  accessToken: string;\n  projectId: string;\n  email: string;\n  accountId: string;\n}\n\n/**\n * Token manager configuration\n */\nexport interface TokenManagerConfig {\n  /**\n   * Scheduling mode\n   */\n  schedulingMode?: SchedulingMode;\n\n  /**\n   * Maximum wait time in seconds for cache-first mode\n   */\n  maxWaitSeconds?: number;\n\n  /**\n   * Global lock duration in seconds (for non-session requests)\n   */\n  globalLockSeconds?: number;\n\n  /**\n   * Token refresh threshold in seconds (refresh if expires in less than this)\n   */\n  refreshThresholdSeconds?: number;\n}\n\n/**\n * Subscription tier for accounts\n */\nexport type SubscriptionTier = 'FREE' | 'PRO' | 'ULTRA';\n","import { RateLimitInfo, RateLimitReason } from './types.js';\nimport { getLogger } from '../../utils/logger.js';\n\nconst logger = getLogger().child({ module: 'RateLimitTracker' });\n\n/**\n * Rate Limit Tracker\n *\n * Tracks rate-limited accounts and provides avoidance logic.\n * Supports detection of 429 (rate limit) and 5xx (server error) responses.\n */\nexport class RateLimitTracker {\n  private limits: Map<string, RateLimitInfo> = new Map();\n\n  /**\n   * Get remaining wait time in seconds for an account\n   */\n  getRemainingWait(accountId: string): number {\n    const info = this.limits.get(accountId);\n    if (!info) return 0;\n\n    const now = new Date();\n    if (info.resetTime > now) {\n      return Math.ceil((info.resetTime.getTime() - now.getTime()) / 1000);\n    }\n\n    return 0;\n  }\n\n  /**\n   * Parse error response and store rate limit information\n   *\n   * @param accountId - Account ID\n   * @param status - HTTP status code\n   * @param retryAfterHeader - Retry-After header value\n   * @param body - Error response body\n   * @returns RateLimitInfo if rate limit detected, null otherwise\n   */\n  parseFromError(\n    accountId: string,\n    status: number,\n    retryAfterHeader: string | null,\n    body: string\n  ): RateLimitInfo | null {\n    // Only handle 429 (rate limit) and 5xx (server errors)\n    if (status !== 429 && status !== 500 && status !== 503 && status !== 529) {\n      return null;\n    }\n\n    // Determine rate limit reason\n    const reason = status === 429 ? this.parseRateLimitReason(body) : RateLimitReason.ServerError;\n\n    let retryAfterSec: number | null = null;\n\n    // 1. Try to parse from Retry-After header\n    if (retryAfterHeader) {\n      const parsed = parseInt(retryAfterHeader, 10);\n      if (!isNaN(parsed)) {\n        retryAfterSec = parsed;\n      }\n    }\n\n    // 2. Try to parse from error body\n    if (retryAfterSec === null) {\n      retryAfterSec = this.parseRetryTimeFromBody(body);\n    }\n\n    // 3. Apply default values with safety buffer\n    let retrySec: number;\n    if (retryAfterSec !== null) {\n      // Minimum 2 seconds safety buffer\n      retrySec = retryAfterSec < 2 ? 2 : retryAfterSec;\n    } else {\n      // Apply defaults based on reason\n      switch (reason) {\n        case RateLimitReason.QuotaExhausted:\n          logger.warn('Quota exhausted detected, using default 3600s (1 hour)');\n          retrySec = 3600;\n          break;\n        case RateLimitReason.RateLimitExceeded:\n          logger.debug('Rate limit exceeded detected, using default 30s');\n          retrySec = 30;\n          break;\n        case RateLimitReason.ServerError:\n          logger.warn(`Server error ${status} detected, executing 20s soft avoidance`);\n          retrySec = 20;\n          break;\n        default:\n          logger.debug('Unknown rate limit reason, using default 60s');\n          retrySec = 60;\n      }\n    }\n\n    const info: RateLimitInfo = {\n      resetTime: new Date(Date.now() + retrySec * 1000),\n      retryAfterSec: retrySec,\n      detectedAt: new Date(),\n      reason,\n    };\n\n    this.limits.set(accountId, info);\n\n    logger.warn(\n      { accountId, status, reason, retrySec },\n      `Account rate limited: ${reason}, reset in ${retrySec}s`\n    );\n\n    return info;\n  }\n\n  /**\n   * Parse rate limit reason from error body\n   */\n  private parseRateLimitReason(body: string): RateLimitReason {\n    const trimmed = body.trim();\n\n    // Try JSON parsing first\n    if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n      try {\n        const json = JSON.parse(trimmed);\n        const reasonStr = json.error?.details?.[0]?.reason;\n\n        if (reasonStr === 'QUOTA_EXHAUSTED') {\n          return RateLimitReason.QuotaExhausted;\n        } else if (reasonStr === 'RATE_LIMIT_EXCEEDED') {\n          return RateLimitReason.RateLimitExceeded;\n        }\n      } catch {\n        // Fall through to text matching\n      }\n    }\n\n    // Text matching fallback\n    const lowerBody = body.toLowerCase();\n    if (lowerBody.includes('exhausted') || lowerBody.includes('quota')) {\n      return RateLimitReason.QuotaExhausted;\n    } else if (lowerBody.includes('rate limit') || lowerBody.includes('too many requests')) {\n      return RateLimitReason.RateLimitExceeded;\n    }\n\n    return RateLimitReason.Unknown;\n  }\n\n  /**\n   * Parse duration string like \"2h1m1s\", \"42s\", \"1h30m\"\n   */\n  private parseDurationString(s: string): number | null {\n    logger.debug({ duration: s }, 'Parsing duration string');\n\n    // Regex to extract hours, minutes, seconds, milliseconds\n    const pattern = /(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+(?:\\.\\d+)?)s)?(?:(\\d+)ms)?/;\n    const match = s.match(pattern);\n\n    if (!match) {\n      logger.warn({ duration: s }, 'Failed to match duration pattern');\n      return null;\n    }\n\n    const hours = parseInt(match[1] || '0', 10);\n    const minutes = parseInt(match[2] || '0', 10);\n    const seconds = parseFloat(match[3] || '0');\n    const milliseconds = parseInt(match[4] || '0', 10);\n\n    logger.debug({ hours, minutes, seconds, milliseconds }, 'Parsed duration components');\n\n    const totalSeconds = hours * 3600 + minutes * 60 + Math.ceil(seconds) + Math.ceil(milliseconds / 1000);\n\n    if (totalSeconds === 0) {\n      logger.warn({ duration: s }, 'Duration parsed to 0 seconds');\n      return null;\n    }\n\n    logger.info({ duration: s, totalSeconds }, 'Successfully parsed duration');\n    return totalSeconds;\n  }\n\n  /**\n   * Parse retry time from error body\n   */\n  private parseRetryTimeFromBody(body: string): number | null {\n    const trimmed = body.trim();\n\n    // A. Try JSON parsing first\n    if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n      try {\n        const json = JSON.parse(trimmed);\n\n        // 1. Google quotaResetDelay format\n        const quotaResetDelay = json.error?.details?.[0]?.metadata?.quotaResetDelay;\n        if (quotaResetDelay && typeof quotaResetDelay === 'string') {\n          logger.debug({ quotaResetDelay }, 'Found quotaResetDelay in JSON');\n          const parsed = this.parseDurationString(quotaResetDelay);\n          if (parsed !== null) return parsed;\n        }\n\n        // 2. OpenAI retry_after format (number)\n        const retryAfter = json.error?.retry_after;\n        if (typeof retryAfter === 'number') {\n          logger.debug({ retryAfter }, 'Found retry_after in JSON');\n          return retryAfter;\n        }\n      } catch {\n        // Fall through to regex\n      }\n    }\n\n    // B. Regex patterns for text matching\n    const patterns = [\n      // \"Try again in 2m 30s\"\n      { regex: /try again in (\\d+)m\\s*(\\d+)s/i, handler: (m: RegExpMatchArray) => parseInt(m[1]!, 10) * 60 + parseInt(m[2]!, 10) },\n\n      // \"Try again in 30s\" or \"backoff for 42s\"\n      { regex: /(?:try again in|backoff for|wait)\\s*(\\d+)s/i, handler: (m: RegExpMatchArray) => parseInt(m[1]!, 10) },\n\n      // \"quota will reset in X seconds\"\n      { regex: /quota will reset in (\\d+) second/i, handler: (m: RegExpMatchArray) => parseInt(m[1]!, 10) },\n\n      // \"Retry after X seconds\"\n      { regex: /retry after (\\d+) second/i, handler: (m: RegExpMatchArray) => parseInt(m[1]!, 10) },\n\n      // \"(wait Xs)\"\n      { regex: /\\(wait (\\d+)s\\)/i, handler: (m: RegExpMatchArray) => parseInt(m[1]!, 10) },\n    ];\n\n    for (const { regex, handler } of patterns) {\n      const match = body.match(regex);\n      if (match) {\n        const result = handler(match);\n        logger.debug({ pattern: regex.source, result }, 'Matched retry time pattern');\n        return result;\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * Check if account is currently rate-limited\n   */\n  isRateLimited(accountId: string): boolean {\n    const info = this.limits.get(accountId);\n    if (!info) return false;\n\n    return info.resetTime > new Date();\n  }\n\n  /**\n   * Get reset time in seconds for an account\n   */\n  getResetSeconds(accountId: string): number | null {\n    const info = this.limits.get(accountId);\n    if (!info) return null;\n\n    const now = new Date();\n    const diff = info.resetTime.getTime() - now.getTime();\n    return diff > 0 ? Math.ceil(diff / 1000) : null;\n  }\n\n  /**\n   * Get rate limit info for an account\n   */\n  get(accountId: string): RateLimitInfo | null {\n    return this.limits.get(accountId) || null;\n  }\n\n  /**\n   * Clear rate limit for an account\n   */\n  clear(accountId: string): boolean {\n    return this.limits.delete(accountId);\n  }\n\n  /**\n   * Clear all rate limits\n   */\n  clearAll(): void {\n    const count = this.limits.size;\n    this.limits.clear();\n    logger.debug({ count }, 'Cleared all rate limit records');\n  }\n\n  /**\n   * Clean up expired rate limit records\n   */\n  cleanup(): number {\n    const now = new Date();\n    let count = 0;\n\n    for (const [accountId, info] of this.limits.entries()) {\n      if (info.resetTime <= now) {\n        this.limits.delete(accountId);\n        count++;\n      }\n    }\n\n    if (count > 0) {\n      logger.debug({ count }, 'Cleaned up expired rate limit records');\n    }\n\n    return count;\n  }\n\n  /**\n   * Get count of tracked rate limits\n   */\n  size(): number {\n    return this.limits.size;\n  }\n}\n","import { QuotaData, QuotaFetchResult, ProjectInfo } from './types.js';\nimport { QuotaFetcher } from './fetcher.js';\nimport { getLogger } from '../../utils/logger.js';\n\nconst logger = getLogger().child({ module: 'QuotaMonitor' });\n\n/**\n * Quota Monitor\n *\n * Monitors account quotas with caching\n */\nexport class QuotaMonitor {\n  private fetcher: QuotaFetcher;\n  private cache: Map<string, QuotaData> = new Map();\n  private cacheTTL: number;\n\n  constructor(cacheTTL: number = 300) {\n    this.fetcher = new QuotaFetcher();\n    this.cacheTTL = cacheTTL; // Default 5 minutes\n  }\n\n  /**\n   * Fetch quota for an account (with caching)\n   */\n  async fetchQuota(accessToken: string, email: string): Promise<QuotaFetchResult> {\n    // Check cache first\n    const cached = this.cache.get(email);\n    if (cached && this.isCacheValid(cached)) {\n      logger.debug({ email }, 'Using cached quota data');\n      return { quota: cached, projectId: null };\n    }\n\n    // Fetch fresh data\n    const result = await this.fetcher.fetchQuota(accessToken, email);\n\n    // Update cache\n    this.cache.set(email, result.quota);\n\n    return result;\n  }\n\n  /**\n   * Fetch project info for an account\n   */\n  async fetchProjectInfo(accessToken: string, email: string): Promise<ProjectInfo> {\n    return this.fetcher.fetchProjectInfo(accessToken, email);\n  }\n\n  /**\n   * Check if account has sufficient quota\n   */\n  hasQuota(email: string, model: string, threshold: number = 10): boolean {\n    const cached = this.cache.get(email);\n    if (!cached) return true; // Assume available if no data\n\n    if (cached.isForbidden) return false;\n\n    const modelQuota = cached.models.find(m => m.name.includes(model));\n    if (!modelQuota) return true; // Assume available if model not found\n\n    return modelQuota.percentage >= threshold;\n  }\n\n  /**\n   * Get cached quota for an account\n   */\n  getCachedQuota(email: string): QuotaData | null {\n    return this.cache.get(email) || null;\n  }\n\n  /**\n   * Clear cached quota for an account\n   */\n  clearCache(email: string): void {\n    this.cache.delete(email);\n  }\n\n  /**\n   * Clear all cached quotas\n   */\n  clearAllCache(): void {\n    this.cache.clear();\n  }\n\n  /**\n   * Check if cache is still valid\n   */\n  private isCacheValid(quota: QuotaData): boolean {\n    const now = Math.floor(Date.now() / 1000);\n    const age = now - quota.lastUpdated;\n    return age < this.cacheTTL;\n  }\n}\n","import {\n  QuotaData,\n  QuotaFetchResult,\n  ProjectInfo,\n  GoogleQuotaResponse,\n  GoogleLoadProjectResponse,\n  ModelQuota,\n} from './types.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { AntigravityError } from '../../utils/errors.js';\n\nconst logger = getLogger().child({ module: 'QuotaFetcher' });\n\nconst QUOTA_API_URL = 'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels';\nconst LOAD_PROJECT_URL = 'https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist';\nconst USER_AGENT = 'antigravity/2.0.0 TypeScript';\n\n/**\n * Quota Fetcher\n *\n * Fetches quota information from Google Cloud APIs\n */\nexport class QuotaFetcher {\n  /**\n   * Fetch project ID and subscription tier\n   */\n  async fetchProjectInfo(accessToken: string, email: string): Promise<ProjectInfo> {\n    try {\n      logger.debug({ email }, 'Fetching project info');\n\n      const response = await fetch(LOAD_PROJECT_URL, {\n        method: 'POST',\n        headers: {\n          'Authorization': `Bearer ${accessToken}`,\n          'Content-Type': 'application/json',\n          'User-Agent': USER_AGENT,\n        },\n        body: JSON.stringify({\n          metadata: { ideType: 'ANTIGRAVITY' },\n        }),\n      });\n\n      if (!response.ok) {\n        logger.warn({ email, status: response.status }, 'loadCodeAssist failed');\n        return { projectId: null, subscriptionTier: null };\n      }\n\n      const data = await response.json() as GoogleLoadProjectResponse;\n\n      const projectId = data.cloudaicompanionProject || null;\n\n      // Prefer paid_tier over current_tier\n      const subscriptionTier =\n        data.paidTier?.id ||\n        data.currentTier?.id ||\n        null;\n\n      if (subscriptionTier) {\n        logger.info({ email, subscriptionTier }, 'Subscription tier detected');\n      }\n\n      return { projectId, subscriptionTier };\n    } catch (error) {\n      logger.error({ email, error }, 'Failed to fetch project info');\n      return { projectId: null, subscriptionTier: null };\n    }\n  }\n\n  /**\n   * Fetch quota data for an account\n   */\n  async fetchQuota(accessToken: string, email: string): Promise<QuotaFetchResult> {\n    logger.debug({ email }, 'Starting quota fetch');\n\n    // 1. Get project ID and subscription tier\n    const { projectId, subscriptionTier } = await this.fetchProjectInfo(accessToken, email);\n\n    const finalProjectId = projectId || 'bamboo-precept-lgxtn';\n\n    // 2. Fetch quota data with retries\n    const maxRetries = 3;\n    let lastError: Error | null = null;\n\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\n      try {\n        const response = await fetch(QUOTA_API_URL, {\n          method: 'POST',\n          headers: {\n            'Authorization': `Bearer ${accessToken}`,\n            'Content-Type': 'application/json',\n            'User-Agent': USER_AGENT,\n          },\n          body: JSON.stringify({\n            project: finalProjectId,\n          }),\n        });\n\n        // Handle 403 Forbidden specially\n        if (response.status === 403) {\n          logger.warn({ email }, 'Account forbidden (403), marking as forbidden');\n          const quota: QuotaData = {\n            models: [],\n            lastUpdated: Math.floor(Date.now() / 1000),\n            isForbidden: true,\n            subscriptionTier: subscriptionTier || undefined,\n          };\n          return { quota, projectId };\n        }\n\n        // Other errors: retry\n        if (!response.ok) {\n          const errorText = await response.text();\n          logger.warn(\n            { email, status: response.status, attempt, maxRetries },\n            `API error: ${errorText}`\n          );\n\n          if (attempt < maxRetries) {\n            await this.sleep(1000);\n            continue;\n          }\n\n          throw new AntigravityError(\n            `Quota API error: ${response.status} - ${errorText}`,\n            'QUOTA_API_ERROR',\n            response.status\n          );\n        }\n\n        // Success - parse response\n        const data = await response.json() as GoogleQuotaResponse;\n\n        const quota = this.parseQuotaResponse(data, subscriptionTier);\n\n        logger.debug({ email, modelCount: quota.models.length }, 'Quota fetch successful');\n\n        return { quota, projectId };\n      } catch (error) {\n        logger.warn({ email, attempt, maxRetries, error }, 'Quota fetch attempt failed');\n        lastError = error as Error;\n\n        if (attempt < maxRetries) {\n          await this.sleep(1000);\n        }\n      }\n    }\n\n    throw lastError || new AntigravityError('Quota fetch failed after retries', 'QUOTA_FETCH_FAILED');\n  }\n\n  /**\n   * Parse quota API response\n   */\n  private parseQuotaResponse(data: GoogleQuotaResponse, subscriptionTier: string | null): QuotaData {\n    const models: ModelQuota[] = [];\n\n    logger.debug({ modelCount: Object.keys(data.models || {}).length }, 'Parsing quota response');\n\n    for (const [name, info] of Object.entries(data.models || {})) {\n      if (!info.quotaInfo) continue;\n\n      const percentage = info.quotaInfo.remainingFraction\n        ? Math.floor(info.quotaInfo.remainingFraction * 100)\n        : 0;\n\n      const resetTime = info.quotaInfo.resetTime || '';\n\n      // Only save models we care about\n      if (name.includes('gemini') || name.includes('claude')) {\n        models.push({ name, percentage, resetTime });\n      }\n    }\n\n    return {\n      models,\n      lastUpdated: Math.floor(Date.now() / 1000),\n      isForbidden: false,\n      subscriptionTier: subscriptionTier || undefined,\n    };\n  }\n\n  /**\n   * Sleep utility\n   */\n  private sleep(ms: number): Promise<void> {\n    return new Promise(resolve => setTimeout(resolve, ms));\n  }\n}\n","/**\n * OpenAI Chat Completions Handler\n * Main orchestrator for handling OpenAI-compatible API requests\n */\n\nimport type { AccountManager } from '../../modules/account/manager.js';\nimport type { TokenManager } from '../../modules/token/manager.js';\nimport type { RateLimitTracker } from '../../modules/token/rate-limit.js';\nimport { AntigravityError } from '../../utils/errors.js';\nimport { getModelMapping } from './mapping.js';\nimport {  convertToGeminiRequest, convertFromGeminiResponse } from '../gemini/converter.js';\nimport { GeminiClient } from '../gemini/client.js';\nimport type {\n  ChatCompletionRequest,\n  ChatCompletionResponse,\n} from './types.js';\n\nexport interface OpenAIHandlerOptions {\n  accountManager: AccountManager;\n  tokenManager: TokenManager;\n  rateLimitTracker: RateLimitTracker;\n}\n\nexport class OpenAIHandler {\n  constructor(private options: OpenAIHandlerOptions) {}\n\n  /**\n   * Handle chat completion request\n   */\n  async handleChatCompletion(\n    request: ChatCompletionRequest\n  ): Promise<ChatCompletionResponse> {\n    // Get model mapping\n    const mapping = getModelMapping(request.model);\n    if (!mapping) {\n      throw new AntigravityError(\n        `Unknown model: ${request.model}`,\n        'UNKNOWN_MODEL'\n      );\n    }\n\n    // Get account token\n    const quotaGroup = mapping.provider === 'google' ? 'gemini' : 'claude';\n    const selectedToken = await this.options.tokenManager.getToken({\n      quotaGroup,\n      estimatedTokens: request.max_tokens || 1000,\n    });\n\n    // Handle based on provider\n    if (mapping.provider === 'google') {\n      return await this.handleGeminiRequest(request, selectedToken.accessToken, mapping);\n    } else {\n      throw new AntigravityError('Claude handler not implemented yet', 'NOT_IMPLEMENTED');\n    }\n  }\n\n  /**\n   * Handle request via Gemini\n   */\n  private async handleGeminiRequest(\n    request: ChatCompletionRequest,\n    accessToken: string,\n    mapping: { model: string; endpoint?: 'prod' | 'daily' }\n  ): Promise<ChatCompletionResponse> {\n    // Convert OpenAI request to Gemini format\n    const geminiRequest = convertToGeminiRequest(request);\n\n    // Create Gemini client\n    const client = new GeminiClient({\n      accessToken,\n      model: mapping.model,\n      endpoint: mapping.endpoint,\n    });\n\n    // Make request\n    const geminiResponse = await client.generateContent(geminiRequest);\n\n    // Convert response back to OpenAI format\n    return convertFromGeminiResponse(geminiResponse, request.model);\n  }\n}\n","/**\n * Model Mapping Configuration\n * Maps OpenAI model names to actual provider models\n */\n\nexport interface ModelMapping {\n  provider: 'google' | 'anthropic';\n  model: string;\n  endpoint?: 'prod' | 'daily';\n}\n\nexport const MODEL_MAP: Record<string, ModelMapping> = {\n  // Google Gemini models (via OpenAI names)\n  'gpt-4': {\n    provider: 'google',\n    model: 'gemini-2.0-flash-thinking-exp-1219',\n    endpoint: 'prod',\n  },\n  'gpt-4-turbo': {\n    provider: 'google',\n    model: 'gemini-2.0-flash-exp',\n    endpoint: 'prod',\n  },\n  'gpt-4o': {\n    provider: 'google',\n    model: 'gemini-exp-1206',\n    endpoint: 'daily',\n  },\n  'gpt-3.5-turbo': {\n    provider: 'google',\n    model: 'gemini-1.5-flash',\n    endpoint: 'prod',\n  },\n\n  // Anthropic Claude models (direct mapping)\n  'claude-3-5-sonnet-20241022': {\n    provider: 'anthropic',\n    model: 'claude-3-5-sonnet-20241022',\n  },\n  'claude-3-5-haiku-20241022': {\n    provider: 'anthropic',\n    model: 'claude-3-5-haiku-20241022',\n  },\n  'claude-3-opus-20240229': {\n    provider: 'anthropic',\n    model: 'claude-3-opus-20240229',\n  },\n\n  // Shortened aliases\n  'claude-3-5-sonnet': {\n    provider: 'anthropic',\n    model: 'claude-3-5-sonnet-20241022',\n  },\n  'claude-3-5-haiku': {\n    provider: 'anthropic',\n    model: 'claude-3-5-haiku-20241022',\n  },\n};\n\n/**\n * Get model mapping for a given model name\n */\nexport function getModelMapping(modelName: string): ModelMapping | null {\n  return MODEL_MAP[modelName] || null;\n}\n\n/**\n * Get provider from model name\n */\nexport function getProvider(modelName: string): 'google' | 'anthropic' | null {\n  const mapping = getModelMapping(modelName);\n  return mapping?.provider || null;\n}\n","/**\n * Fastify Server\n * Main HTTP server for proxy functionality\n */\n\nimport Fastify, { type FastifyInstance } from 'fastify';\nimport cors from '@fastify/cors';\nimport { registerOpenAIRoute } from './routes/openai.js';\nimport { registerMetricsRoute } from './routes/metrics.js';\nimport type { AccountManager } from '../modules/account/manager.js';\nimport type { TokenManager } from '../modules/token/manager.js';\nimport type { RateLimitTracker } from '../modules/token/rate-limit.js';\nimport { MetricsCollector } from '../modules/monitoring/metrics.js';\nimport { getLogger } from '../utils/logger.js';\n\nconst logger = getLogger();\n\nexport interface ServerOptions {\n  host?: string;\n  port?: number;\n  accountManager: AccountManager;\n  tokenManager: TokenManager;\n  rateLimitTracker: RateLimitTracker;\n  metrics?: MetricsCollector;\n}\n\nexport class AntigravityServer {\n  private server: FastifyInstance;\n  private options: ServerOptions;\n  private metrics: MetricsCollector;\n\n  constructor(options: ServerOptions) {\n    this.options = options;\n    this.metrics = options.metrics || new MetricsCollector();\n    this.server = Fastify({\n      logger: true,\n      requestIdLogLabel: 'reqId',\n      disableRequestLogging: false,\n    });\n\n    this.registerPlugins();\n    this.registerRoutes();\n  }\n\n  /**\n   * Register Fastify plugins\n   */\n  private async registerPlugins(): Promise<void> {\n    // Performance tracking hooks\n    this.server.addHook('onRequest', async (request) => {\n      (request as any).startTime = Date.now();\n    });\n\n    this.server.addHook('onResponse', async (request) => {\n      const startTime = (request as any).startTime;\n      if (startTime) {\n        const duration = Date.now() - startTime;\n        this.metrics.recordDuration(request.routeOptions.url || request.url, duration);\n      }\n    });\n\n    // CORS support\n    await this.server.register(cors, {\n      origin: true,\n      credentials: true,\n    });\n\n    // Error handler\n    this.server.setErrorHandler((error, request, reply) => {\n      logger.error({ err: error, reqId: request.id }, 'Request error');\n      reply.status(error.statusCode || 500).send({\n        error: {\n          message: error.message,\n          type: 'server_error',\n        },\n      });\n    });\n  }\n\n  /**\n   * Register routes\n   */\n  private registerRoutes(): void {\n    // Health check\n    this.server.get('/health', async () => {\n      return { status: 'ok', timestamp: new Date().toISOString() };\n    });\n\n    // Root endpoint\n    this.server.get('/', async () => {\n      return {\n        name: 'Antigravity Manager',\n        version: '2.0.0',\n        endpoints: [\n          '/v1/chat/completions',\n          '/health',\n          '/metrics',\n        ],\n      };\n    });\n\n    // Metrics endpoint\n    registerMetricsRoute(this.server, this.metrics);\n\n    // OpenAI routes\n    registerOpenAIRoute(this.server, {\n      accountManager: this.options.accountManager,\n      tokenManager: this.options.tokenManager,\n      rateLimitTracker: this.options.rateLimitTracker,\n      metrics: this.metrics,\n    });\n  }\n\n  /**\n   * Start the server\n   */\n  async start(): Promise<void> {\n    const host = this.options.host || '0.0.0.0';\n    const port = this.options.port || 3000;\n\n    try {\n      await this.server.listen({ host, port });\n      logger.info(`Server listening on ${host}:${port}`);\n    } catch (error) {\n      logger.error({ err: error }, 'Failed to start server');\n      throw error;\n    }\n  }\n\n  /**\n   * Stop the server\n   */\n  async stop(): Promise<void> {\n    try {\n      await this.server.close();\n      logger.info('Server stopped');\n    } catch (error) {\n      logger.error({ err: error }, 'Error stopping server');\n      throw error;\n    }\n  }\n\n  /**\n   * Get the Fastify instance (for testing)\n   */\n  getInstance(): FastifyInstance {\n    return this.server;\n  }\n}\n","/**\n * OpenAI Chat Completions Route\n * Handles /v1/chat/completions endpoint\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport { OpenAIHandler } from '../../protocols/openai/handler.js';\nimport { convertGeminiChunkToOpenAI } from '../../protocols/gemini/converter.js';\nimport type { ChatCompletionRequest } from '../../protocols/openai/types.js';\nimport type { AccountManager } from '../../modules/account/manager.js';\nimport type { TokenManager } from '../../modules/token/manager.js';\nimport type { RateLimitTracker } from '../../modules/token/rate-limit.js';\nimport { getLogger } from '../../utils/logger.js';\n\nconst logger = getLogger();\n\nexport interface OpenAIRouteOptions {\n  accountManager: AccountManager;\n  tokenManager: TokenManager;\n  rateLimitTracker: RateLimitTracker;\n  metrics?: any; // MetricsCollector - optional for now\n}\n\n/**\n * Register OpenAI chat completions route\n */\nexport function registerOpenAIRoute(server: FastifyInstance, options: OpenAIRouteOptions): void {\n  const handler = new OpenAIHandler(options);\n\n  server.post('/v1/chat/completions', async (request, reply) => {\n    const req = request.body as ChatCompletionRequest;\n\n    try {\n      // Handle streaming requests\n      if (req.stream) {\n        const selectedToken = options.tokenManager.selectToken();\n        if (!selectedToken) {\n          return reply.status(503).send({\n            error: { message: 'No available accounts', type: 'service_unavailable' },\n          });\n        }\n\n        const { GeminiClient } = await import('../../protocols/gemini/client.js');\n        const { convertToGeminiRequest, convertGeminiChunkToOpenAI } = await import('../../protocols/gemini/converter.js');\n        const { randomBytes } = await import('crypto');\n\n        reply.raw.writeHead(200, {\n          'Content-Type': 'text/event-stream',\n          'Cache-Control': 'no-cache',\n          'Connection': 'keep-alive',\n        });\n\n        try {\n          const geminiRequest = convertToGeminiRequest(req);\n          const geminiClient = new GeminiClient({ accessToken: selectedToken.accessToken });\n          const geminiStream = geminiClient.streamGenerateContent(geminiRequest);\n\n          const id = 'chatcmpl-' + randomBytes(12).toString('hex');\n          let isFirst = true;\n\n          for await (const geminiChunk of geminiStream) {\n            const openaiChunk = convertGeminiChunkToOpenAI(geminiChunk, req.model, id, isFirst);\n            reply.raw.write(`data: ${JSON.stringify(openaiChunk)}\\n\\n`);\n            if (isFirst) isFirst = false;\n            if (openaiChunk.choices[0]?.finish_reason) break;\n          }\n\n\n          // Send SSE end signal\n          reply.raw.write('data: ');\n          const doneWord = String.fromCharCode(91, 68, 79, 78, 69, 93);\n          reply.raw.write(doneWord + '\\n\\n');\n        } catch (streamErr) {\n          logger.error({ err: streamErr }, 'Streaming error');\n        } finally {\n          reply.raw.end();\n        }\n        return reply;\n      }\n\n      // Handle non-streaming requests\n      const response = await handler.handleChatCompletion(req);\n      return reply.send(response);\n    } catch (error) {\n      logger.error({ err: error, model: req.model }, 'Chat completion error');\n      return reply.status(500).send({\n        error: {\n          message: error instanceof Error ? error.message : 'Unknown error',\n          type: 'server_error',\n        },\n      });\n    }\n  });\n}\n","import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';\nimport { MetricsCollector } from '../../modules/monitoring/metrics.js';\n\nexport async function registerMetricsRoute(\n  server: FastifyInstance,\n  metrics: MetricsCollector\n): Promise<void> {\n  server.get('/metrics', async (request: FastifyRequest, reply: FastifyReply) => {\n    const prometheusText = metrics.getPrometheusFormat();\n\n    reply\n      .type('text/plain; version=0.0.4; charset=utf-8')\n      .send(prometheusText);\n  });\n}\n","/**\n * Metrics Collector for Prometheus-compatible metrics\n */\nexport class MetricsCollector {\n  private requestCounts: Map<string, Map<number, number>> = new Map();\n  private requestDurations: Map<string, number[]> = new Map();\n  private tokenSelectionDurations: number[] = [];\n  private startTime: number = Date.now();\n\n  /**\n   * Increment request counter\n   */\n  incrementRequest(model: string, statusCode: number): void {\n    if (!this.requestCounts.has(model)) {\n      this.requestCounts.set(model, new Map());\n    }\n\n    const modelCounts = this.requestCounts.get(model)!;\n    const currentCount = modelCounts.get(statusCode) || 0;\n    modelCounts.set(statusCode, currentCount + 1);\n  }\n\n  /**\n   * Record request duration\n   */\n  recordDuration(endpoint: string, durationMs: number): void {\n    if (!this.requestDurations.has(endpoint)) {\n      this.requestDurations.set(endpoint, []);\n    }\n\n    const durations = this.requestDurations.get(endpoint)!;\n    durations.push(durationMs);\n\n    // Keep only last 1000 durations to prevent memory leak\n    if (durations.length > 1000) {\n      durations.shift();\n    }\n  }\n\n  /**\n   * Record token selection duration\n   */\n  recordTokenSelection(durationMs: number): void {\n    this.tokenSelectionDurations.push(durationMs);\n\n    // Keep only last 1000 durations\n    if (this.tokenSelectionDurations.length > 1000) {\n      this.tokenSelectionDurations.shift();\n    }\n  }\n\n  /**\n   * Get metrics in Prometheus text format\n   */\n  getPrometheusFormat(): string {\n    const lines: string[] = [];\n\n    // Uptime\n    const uptimeSeconds = Math.floor((Date.now() - this.startTime) / 1000);\n    lines.push('# HELP process_uptime_seconds Process uptime in seconds');\n    lines.push('# TYPE process_uptime_seconds gauge');\n    lines.push(`process_uptime_seconds ${uptimeSeconds}`);\n    lines.push('');\n\n    // Request counts\n    lines.push('# HELP http_requests_total Total HTTP requests');\n    lines.push('# TYPE http_requests_total counter');\n    for (const [model, statusCounts] of this.requestCounts.entries()) {\n      for (const [status, count] of statusCounts.entries()) {\n        lines.push(`http_requests_total{model=\"${model}\",status=\"${status}\"} ${count}`);\n      }\n    }\n    lines.push('');\n\n    // Request durations\n    if (this.requestDurations.size > 0) {\n      lines.push('# HELP http_request_duration_milliseconds Request duration in milliseconds');\n      lines.push('# TYPE http_request_duration_milliseconds summary');\n\n      for (const [endpoint, durations] of this.requestDurations.entries()) {\n        if (durations.length > 0) {\n          const sorted = [...durations].sort((a, b) => a - b);\n          const count = sorted.length;\n          const sum = sorted.reduce((a, b) => a + b, 0);\n          const p50 = sorted[Math.floor(count * 0.5)] || 0;\n          const p95 = sorted[Math.floor(count * 0.95)] || 0;\n          const p99 = sorted[Math.floor(count * 0.99)] || 0;\n\n          lines.push(`http_request_duration_milliseconds{endpoint=\"${endpoint}\",quantile=\"0.5\"} ${p50.toFixed(2)}`);\n          lines.push(`http_request_duration_milliseconds{endpoint=\"${endpoint}\",quantile=\"0.95\"} ${p95.toFixed(2)}`);\n          lines.push(`http_request_duration_milliseconds{endpoint=\"${endpoint}\",quantile=\"0.99\"} ${p99.toFixed(2)}`);\n          lines.push(`http_request_duration_milliseconds_sum{endpoint=\"${endpoint}\"} ${sum.toFixed(2)}`);\n          lines.push(`http_request_duration_milliseconds_count{endpoint=\"${endpoint}\"} ${count}`);\n        }\n      }\n      lines.push('');\n    }\n\n    // Token selection durations\n    if (this.tokenSelectionDurations.length > 0) {\n      const sorted = [...this.tokenSelectionDurations].sort((a, b) => a - b);\n      const count = sorted.length;\n      const sum = sorted.reduce((a, b) => a + b, 0);\n      const p50 = sorted[Math.floor(count * 0.5)] || 0;\n      const p95 = sorted[Math.floor(count * 0.95)] || 0;\n      const p99 = sorted[Math.floor(count * 0.99)] || 0;\n\n      lines.push('# HELP token_selection_duration_milliseconds Token selection duration in milliseconds');\n      lines.push('# TYPE token_selection_duration_milliseconds summary');\n      lines.push(`token_selection_duration_milliseconds{quantile=\"0.5\"} ${p50.toFixed(2)}`);\n      lines.push(`token_selection_duration_milliseconds{quantile=\"0.95\"} ${p95.toFixed(2)}`);\n      lines.push(`token_selection_duration_milliseconds{quantile=\"0.99\"} ${p99.toFixed(2)}`);\n      lines.push(`token_selection_duration_milliseconds_sum ${sum.toFixed(2)}`);\n      lines.push(`token_selection_duration_milliseconds_count ${count}`);\n      lines.push('');\n    }\n\n    // Memory usage\n    const memUsage = process.memoryUsage();\n    lines.push('# HELP process_memory_bytes Process memory usage in bytes');\n    lines.push('# TYPE process_memory_bytes gauge');\n    lines.push(`process_memory_bytes{type=\"rss\"} ${memUsage.rss}`);\n    lines.push(`process_memory_bytes{type=\"heap_total\"} ${memUsage.heapTotal}`);\n    lines.push(`process_memory_bytes{type=\"heap_used\"} ${memUsage.heapUsed}`);\n    lines.push(`process_memory_bytes{type=\"external\"} ${memUsage.external}`);\n    lines.push('');\n\n    return lines.join('\\n');\n  }\n\n  /**\n   * Reset all metrics\n   */\n  reset(): void {\n    this.requestCounts.clear();\n    this.requestDurations.clear();\n    this.tokenSelectionDurations = [];\n    this.startTime = Date.now();\n  }\n\n  /**\n   * Get request count for a specific model and status\n   */\n  getRequestCount(model: string, statusCode: number): number {\n    return this.requestCounts.get(model)?.get(statusCode) || 0;\n  }\n\n  /**\n   * Get total request count\n   */\n  getTotalRequests(): number {\n    let total = 0;\n    for (const statusCounts of this.requestCounts.values()) {\n      for (const count of statusCounts.values()) {\n        total += count;\n      }\n    }\n    return total;\n  }\n}\n","import Database from 'better-sqlite3';\nimport { join } from 'path';\nimport { existsSync, mkdirSync } from 'fs';\nimport { DatabaseError } from '../utils/errors.js';\nimport { getLogger } from '../utils/logger.js';\nimport { initializeSchema } from './schema.js';\n\nexport interface DatabaseConfig {\n  path?: string;\n  readonly?: boolean;\n  verbose?: boolean;\n}\n\n/**\n * Database wrapper class\n */\nexport class DB {\n  private db: Database.Database;\n  private logger = getLogger();\n\n  constructor(config: DatabaseConfig = {}) {\n    const {\n      path = process.env.DB_PATH || join(process.cwd(), 'data', 'antigravity.db'),\n      readonly = false,\n      verbose = false,\n    } = config;\n\n    try {\n      // Ensure directory exists\n      const dir = join(path, '..');\n      if (!existsSync(dir)) {\n        mkdirSync(dir, { recursive: true });\n      }\n\n      this.db = new Database(path, {\n        readonly,\n        verbose: verbose ? (msg) => this.logger.debug(msg) : undefined,\n      });\n\n      this.configure();\n      this.initialize();\n\n      this.logger.info({ path }, 'Database initialized');\n    } catch (error) {\n      throw new DatabaseError('Failed to initialize database', { error });\n    }\n  }\n\n  /**\n   * Configures database optimizations\n   */\n  private configure(): void {\n    // Enable WAL mode for better concurrency\n    this.db.pragma('journal_mode = WAL');\n    // Synchronous mode for durability and performance balance\n    this.db.pragma('synchronous = NORMAL');\n    // Cache size (64MB)\n    this.db.pragma('cache_size = -64000');\n    // Enable foreign keys\n    this.db.pragma('foreign_keys = ON');\n    // Temp store in memory\n    this.db.pragma('temp_store = MEMORY');\n  }\n\n  /**\n   * Initializes database schema\n   */\n  private initialize(): void {\n    initializeSchema(this.db);\n  }\n\n  /**\n   * Gets the underlying better-sqlite3 database instance\n   */\n  getInstance(): Database.Database {\n    return this.db;\n  }\n\n  /**\n   * Prepares a SQL statement\n   */\n  prepare(sql: string): Database.Statement {\n    return this.db.prepare(sql);\n  }\n\n  /**\n   * Executes SQL directly\n   */\n  exec(sql: string): void {\n    this.db.exec(sql);\n  }\n\n  /**\n   * Runs a transaction\n   */\n  transaction<T>(fn: () => T): T {\n    const txn = this.db.transaction(fn);\n    return txn();\n  }\n\n  /**\n   * Closes the database connection\n   */\n  close(): void {\n    try {\n      this.db.close();\n      this.logger.info('Database connection closed');\n    } catch (error) {\n      this.logger.error({ error }, 'Failed to close database');\n      throw new DatabaseError('Failed to close database', { error });\n    }\n  }\n\n  /**\n   * Backs up the database to a file\n   */\n  backup(destinationPath: string): void {\n    try {\n      this.db.backup(destinationPath);\n      this.logger.info({ destinationPath }, 'Database backed up');\n    } catch (error) {\n      throw new DatabaseError('Failed to backup database', { error });\n    }\n  }\n\n  /**\n   * Optimizes the database (VACUUM)\n   */\n  optimize(): void {\n    try {\n      this.db.exec('VACUUM');\n      this.logger.info('Database optimized');\n    } catch (error) {\n      throw new DatabaseError('Failed to optimize database', { error });\n    }\n  }\n}\n\n/**\n * Global database instance\n */\nlet globalDB: DB | null = null;\n\n/**\n * Gets or creates the global database instance\n */\nexport function getDatabase(config?: DatabaseConfig): DB {\n  if (!globalDB) {\n    globalDB = new DB(config);\n  }\n  return globalDB;\n}\n\n/**\n * Sets a custom global database instance\n */\nexport function setDatabase(db: DB): void {\n  globalDB = db;\n}\n\n/**\n * Closes the global database instance\n */\nexport function closeDatabase(): void {\n  if (globalDB) {\n    globalDB.close();\n    globalDB = null;\n  }\n}\n","/**\n * Database schema definitions\n */\n\nexport const SCHEMA = {\n  accounts: `\n    CREATE TABLE IF NOT EXISTS accounts (\n      id TEXT PRIMARY KEY,\n      provider TEXT NOT NULL CHECK(provider IN ('google', 'anthropic')),\n      email TEXT NOT NULL,\n      tokens TEXT NOT NULL,\n      quota TEXT NOT NULL DEFAULT '{}',\n      created_at INTEGER NOT NULL,\n      updated_at INTEGER NOT NULL,\n      last_used INTEGER\n    );\n    CREATE INDEX IF NOT EXISTS idx_accounts_provider ON accounts(provider);\n    CREATE INDEX IF NOT EXISTS idx_accounts_email ON accounts(email);\n    CREATE INDEX IF NOT EXISTS idx_accounts_last_used ON accounts(last_used);\n  `,\n\n  sessions: `\n    CREATE TABLE IF NOT EXISTS sessions (\n      id TEXT PRIMARY KEY,\n      fingerprint TEXT NOT NULL,\n      account_id TEXT NOT NULL,\n      created_at INTEGER NOT NULL,\n      expires_at INTEGER NOT NULL,\n      FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE\n    );\n    CREATE INDEX IF NOT EXISTS idx_sessions_fingerprint ON sessions(fingerprint);\n    CREATE INDEX IF NOT EXISTS idx_sessions_account_id ON sessions(account_id);\n    CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);\n  `,\n\n  config: `\n    CREATE TABLE IF NOT EXISTS config (\n      key TEXT PRIMARY KEY,\n      value TEXT NOT NULL,\n      updated_at INTEGER NOT NULL\n    );\n  `,\n\n  logs: `\n    CREATE TABLE IF NOT EXISTS logs (\n      id INTEGER PRIMARY KEY AUTOINCREMENT,\n      timestamp INTEGER NOT NULL,\n      level TEXT NOT NULL,\n      message TEXT NOT NULL,\n      metadata TEXT\n    );\n    CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp);\n    CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);\n  `,\n};\n\n/**\n * Initializes all database tables\n */\nexport function initializeSchema(db: any): void {\n  db.exec(SCHEMA.accounts);\n  db.exec(SCHEMA.sessions);\n  db.exec(SCHEMA.config);\n  db.exec(SCHEMA.logs);\n}\n"]}