@office-ai/aioncli-core 0.30.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/dist/docs/assets/theme-ansi-dark.png +0 -0
  2. package/dist/docs/assets/theme-atom-one-dark.png +0 -0
  3. package/dist/docs/assets/theme-ayu-dark.png +0 -0
  4. package/dist/docs/assets/theme-default-dark.png +0 -0
  5. package/dist/docs/assets/theme-dracula-dark.png +0 -0
  6. package/dist/docs/assets/theme-github-dark.png +0 -0
  7. package/dist/docs/assets/theme-holiday-dark.png +0 -0
  8. package/dist/docs/assets/theme-shades-of-purple-dark.png +0 -0
  9. package/dist/docs/assets/theme-solarized-dark.png +0 -0
  10. package/dist/docs/assets/theme-solarized-light.png +0 -0
  11. package/dist/docs/cli/notifications.md +58 -0
  12. package/dist/docs/redirects.json +20 -0
  13. package/dist/docs/reference/commands.md +563 -0
  14. package/dist/docs/reference/configuration.md +1804 -0
  15. package/dist/docs/reference/keyboard-shortcuts.md +168 -0
  16. package/dist/docs/reference/memport.md +246 -0
  17. package/dist/docs/reference/policy-engine.md +386 -0
  18. package/dist/docs/reference/tools.md +106 -0
  19. package/dist/docs/resources/faq.md +175 -0
  20. package/dist/docs/resources/quota-and-pricing.md +199 -0
  21. package/dist/docs/resources/tos-privacy.md +102 -0
  22. package/dist/docs/resources/troubleshooting.md +176 -0
  23. package/dist/docs/resources/uninstall.md +56 -0
  24. package/dist/src/agents/a2a-errors.d.ts +65 -0
  25. package/dist/src/agents/a2a-errors.js +164 -0
  26. package/dist/src/agents/a2a-errors.js.map +1 -0
  27. package/dist/src/agents/a2a-errors.test.d.ts +6 -0
  28. package/dist/src/agents/a2a-errors.test.js +183 -0
  29. package/dist/src/agents/a2a-errors.test.js.map +1 -0
  30. package/dist/src/agents/auth-provider/api-key-provider.d.ts +30 -0
  31. package/dist/src/agents/auth-provider/api-key-provider.js +66 -0
  32. package/dist/src/agents/auth-provider/api-key-provider.js.map +1 -0
  33. package/dist/src/agents/auth-provider/api-key-provider.test.d.ts +6 -0
  34. package/dist/src/agents/auth-provider/api-key-provider.test.js +130 -0
  35. package/dist/src/agents/auth-provider/api-key-provider.test.js.map +1 -0
  36. package/dist/src/agents/auth-provider/http-provider.d.ts +28 -0
  37. package/dist/src/agents/auth-provider/http-provider.js +73 -0
  38. package/dist/src/agents/auth-provider/http-provider.js.map +1 -0
  39. package/dist/src/agents/auth-provider/http-provider.test.d.ts +6 -0
  40. package/dist/src/agents/auth-provider/http-provider.test.js +112 -0
  41. package/dist/src/agents/auth-provider/http-provider.test.js.map +1 -0
  42. package/dist/src/agents/auth-provider/oauth2-provider.d.ts +65 -0
  43. package/dist/src/agents/auth-provider/oauth2-provider.js +233 -0
  44. package/dist/src/agents/auth-provider/oauth2-provider.js.map +1 -0
  45. package/dist/src/agents/auth-provider/oauth2-provider.test.d.ts +6 -0
  46. package/dist/src/agents/auth-provider/oauth2-provider.test.js +490 -0
  47. package/dist/src/agents/auth-provider/oauth2-provider.test.js.map +1 -0
  48. package/dist/src/agents/browser/analyzeScreenshot.d.ts +35 -0
  49. package/dist/src/agents/browser/analyzeScreenshot.js +183 -0
  50. package/dist/src/agents/browser/analyzeScreenshot.js.map +1 -0
  51. package/dist/src/agents/browser/analyzeScreenshot.test.d.ts +6 -0
  52. package/dist/src/agents/browser/analyzeScreenshot.test.js +161 -0
  53. package/dist/src/agents/browser/analyzeScreenshot.test.js.map +1 -0
  54. package/dist/src/agents/browser/automationOverlay.d.ts +26 -0
  55. package/dist/src/agents/browser/automationOverlay.js +100 -0
  56. package/dist/src/agents/browser/automationOverlay.js.map +1 -0
  57. package/dist/src/agents/browser/browserAgentDefinition.d.ts +50 -0
  58. package/dist/src/agents/browser/browserAgentDefinition.js +141 -0
  59. package/dist/src/agents/browser/browserAgentDefinition.js.map +1 -0
  60. package/dist/src/agents/browser/browserAgentFactory.d.ts +42 -0
  61. package/dist/src/agents/browser/browserAgentFactory.js +116 -0
  62. package/dist/src/agents/browser/browserAgentFactory.js.map +1 -0
  63. package/dist/src/agents/browser/browserAgentFactory.test.d.ts +6 -0
  64. package/dist/src/agents/browser/browserAgentFactory.test.js +240 -0
  65. package/dist/src/agents/browser/browserAgentFactory.test.js.map +1 -0
  66. package/dist/src/agents/browser/browserAgentInvocation.d.ts +34 -0
  67. package/dist/src/agents/browser/browserAgentInvocation.js +386 -0
  68. package/dist/src/agents/browser/browserAgentInvocation.js.map +1 -0
  69. package/dist/src/agents/browser/browserAgentInvocation.test.d.ts +6 -0
  70. package/dist/src/agents/browser/browserAgentInvocation.test.js +382 -0
  71. package/dist/src/agents/browser/browserAgentInvocation.test.js.map +1 -0
  72. package/dist/src/agents/browser/browserManager.d.ts +115 -0
  73. package/dist/src/agents/browser/browserManager.js +370 -0
  74. package/dist/src/agents/browser/browserManager.js.map +1 -0
  75. package/dist/src/agents/browser/browserManager.test.d.ts +6 -0
  76. package/dist/src/agents/browser/browserManager.test.js +382 -0
  77. package/dist/src/agents/browser/browserManager.test.js.map +1 -0
  78. package/dist/src/agents/browser/mcpToolWrapper.d.ts +45 -0
  79. package/dist/src/agents/browser/mcpToolWrapper.js +358 -0
  80. package/dist/src/agents/browser/mcpToolWrapper.js.map +1 -0
  81. package/dist/src/agents/browser/mcpToolWrapper.test.d.ts +6 -0
  82. package/dist/src/agents/browser/mcpToolWrapper.test.js +126 -0
  83. package/dist/src/agents/browser/mcpToolWrapper.test.js.map +1 -0
  84. package/dist/src/agents/browser/mcpToolWrapperConfirmation.test.d.ts +6 -0
  85. package/dist/src/agents/browser/mcpToolWrapperConfirmation.test.js +59 -0
  86. package/dist/src/agents/browser/mcpToolWrapperConfirmation.test.js.map +1 -0
  87. package/dist/src/agents/browser/modelAvailability.d.ts +23 -0
  88. package/dist/src/agents/browser/modelAvailability.js +23 -0
  89. package/dist/src/agents/browser/modelAvailability.js.map +1 -0
  90. package/dist/src/agents/cli-help-agent.d.ts +2 -2
  91. package/dist/src/billing/billing.d.ts +80 -0
  92. package/dist/src/billing/billing.js +128 -0
  93. package/dist/src/billing/billing.js.map +1 -0
  94. package/dist/src/billing/billing.test.d.ts +6 -0
  95. package/dist/src/billing/billing.test.js +182 -0
  96. package/dist/src/billing/billing.test.js.map +1 -0
  97. package/dist/src/billing/index.d.ts +6 -0
  98. package/dist/src/billing/index.js +7 -0
  99. package/dist/src/billing/index.js.map +1 -0
  100. package/dist/src/code_assist/oauth2.d.ts +1 -1
  101. package/dist/src/code_assist/types.d.ts +26 -26
  102. package/dist/src/config/agent-loop-context.d.ts +22 -0
  103. package/dist/src/config/agent-loop-context.js +7 -0
  104. package/dist/src/config/agent-loop-context.js.map +1 -0
  105. package/dist/src/config/trackerFeatureFlag.test.d.ts +6 -0
  106. package/dist/src/config/trackerFeatureFlag.test.js +43 -0
  107. package/dist/src/config/trackerFeatureFlag.test.js.map +1 -0
  108. package/dist/src/config/userHintService.d.ts +46 -0
  109. package/dist/src/config/userHintService.js +81 -0
  110. package/dist/src/config/userHintService.js.map +1 -0
  111. package/dist/src/config/userHintService.test.d.ts +6 -0
  112. package/dist/src/config/userHintService.test.js +62 -0
  113. package/dist/src/config/userHintService.test.js.map +1 -0
  114. package/dist/src/core/localLiteRtLmClient.d.ts +24 -0
  115. package/dist/src/core/localLiteRtLmClient.js +77 -0
  116. package/dist/src/core/localLiteRtLmClient.js.map +1 -0
  117. package/dist/src/core/localLiteRtLmClient.test.d.ts +6 -0
  118. package/dist/src/core/localLiteRtLmClient.test.js +87 -0
  119. package/dist/src/core/localLiteRtLmClient.test.js.map +1 -0
  120. package/dist/src/core/openaiContentGenerator.d.ts +1 -0
  121. package/dist/src/core/openaiContentGenerator.js +13 -13
  122. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  123. package/dist/src/hooks/runtimeHooks.test.d.ts +6 -0
  124. package/dist/src/hooks/runtimeHooks.test.js +100 -0
  125. package/dist/src/hooks/runtimeHooks.test.js.map +1 -0
  126. package/dist/src/ide/types.d.ts +6 -6
  127. package/dist/src/mcp/mcp-oauth-provider.d.ts +43 -0
  128. package/dist/src/mcp/mcp-oauth-provider.js +67 -0
  129. package/dist/src/mcp/mcp-oauth-provider.js.map +1 -0
  130. package/dist/src/mcp/mcp-oauth-provider.test.d.ts +6 -0
  131. package/dist/src/mcp/mcp-oauth-provider.test.js +63 -0
  132. package/dist/src/mcp/mcp-oauth-provider.test.js.map +1 -0
  133. package/dist/src/policy/integrity.d.ts +45 -0
  134. package/dist/src/policy/integrity.js +121 -0
  135. package/dist/src/policy/integrity.js.map +1 -0
  136. package/dist/src/policy/integrity.test.d.ts +6 -0
  137. package/dist/src/policy/integrity.test.js +132 -0
  138. package/dist/src/policy/integrity.test.js.map +1 -0
  139. package/dist/src/policy/policies/conseca.toml +6 -0
  140. package/dist/src/policy/workspace-policy.test.js +231 -0
  141. package/dist/src/policy/workspace-policy.test.js.map +1 -0
  142. package/dist/src/routing/strategies/approvalModeStrategy.d.ts +18 -0
  143. package/dist/src/routing/strategies/approvalModeStrategy.js +59 -0
  144. package/dist/src/routing/strategies/approvalModeStrategy.js.map +1 -0
  145. package/dist/src/routing/strategies/approvalModeStrategy.test.d.ts +6 -0
  146. package/dist/src/routing/strategies/approvalModeStrategy.test.js +140 -0
  147. package/dist/src/routing/strategies/approvalModeStrategy.test.js.map +1 -0
  148. package/dist/src/routing/strategies/gemmaClassifierStrategy.d.ts +14 -0
  149. package/dist/src/routing/strategies/gemmaClassifierStrategy.js +182 -0
  150. package/dist/src/routing/strategies/gemmaClassifierStrategy.js.map +1 -0
  151. package/dist/src/routing/strategies/gemmaClassifierStrategy.test.d.ts +6 -0
  152. package/dist/src/routing/strategies/gemmaClassifierStrategy.test.js +218 -0
  153. package/dist/src/routing/strategies/gemmaClassifierStrategy.test.js.map +1 -0
  154. package/dist/src/safety/conseca/conseca.d.ts +31 -0
  155. package/dist/src/safety/conseca/conseca.js +105 -0
  156. package/dist/src/safety/conseca/conseca.js.map +1 -0
  157. package/dist/src/safety/conseca/conseca.test.js +226 -0
  158. package/dist/src/safety/conseca/conseca.test.js.map +1 -0
  159. package/dist/src/safety/conseca/integration.test.js +19 -0
  160. package/dist/src/safety/conseca/integration.test.js.map +1 -0
  161. package/dist/src/safety/conseca/policy-enforcer.d.ts +13 -0
  162. package/dist/src/safety/conseca/policy-enforcer.js +135 -0
  163. package/dist/src/safety/conseca/policy-enforcer.js.map +1 -0
  164. package/dist/src/safety/conseca/policy-enforcer.test.js +141 -0
  165. package/dist/src/safety/conseca/policy-enforcer.test.js.map +1 -0
  166. package/dist/src/safety/conseca/policy-generator.d.ts +15 -0
  167. package/dist/src/safety/conseca/policy-generator.js +144 -0
  168. package/dist/src/safety/conseca/policy-generator.js.map +1 -0
  169. package/dist/src/safety/conseca/policy-generator.test.d.ts +6 -0
  170. package/dist/src/safety/conseca/policy-generator.test.js +84 -0
  171. package/dist/src/safety/conseca/policy-generator.test.js.map +1 -0
  172. package/dist/src/safety/conseca/types.d.ts +15 -0
  173. package/dist/src/safety/conseca/types.js +7 -0
  174. package/dist/src/safety/conseca/types.js.map +1 -0
  175. package/dist/src/scheduler/scheduler_parallel.test.d.ts +6 -0
  176. package/dist/src/scheduler/scheduler_parallel.test.js +401 -0
  177. package/dist/src/scheduler/scheduler_parallel.test.js.map +1 -0
  178. package/dist/src/services/FolderTrustDiscoveryService.d.ts +32 -0
  179. package/dist/src/services/FolderTrustDiscoveryService.js +169 -0
  180. package/dist/src/services/FolderTrustDiscoveryService.js.map +1 -0
  181. package/dist/src/services/FolderTrustDiscoveryService.test.d.ts +6 -0
  182. package/dist/src/services/FolderTrustDiscoveryService.test.js +118 -0
  183. package/dist/src/services/FolderTrustDiscoveryService.test.js.map +1 -0
  184. package/dist/src/services/fileKeychain.d.ts +24 -0
  185. package/dist/src/services/fileKeychain.js +123 -0
  186. package/dist/src/services/fileKeychain.js.map +1 -0
  187. package/dist/src/services/keychainService.d.ts +51 -0
  188. package/dist/src/services/keychainService.js +133 -0
  189. package/dist/src/services/keychainService.js.map +1 -0
  190. package/dist/src/services/keychainService.test.d.ts +6 -0
  191. package/dist/src/services/keychainService.test.js +150 -0
  192. package/dist/src/services/keychainService.test.js.map +1 -0
  193. package/dist/src/services/keychainTypes.d.ts +41 -0
  194. package/dist/src/services/keychainTypes.js +18 -0
  195. package/dist/src/services/keychainTypes.js.map +1 -0
  196. package/dist/src/services/trackerService.d.ts +49 -0
  197. package/dist/src/services/trackerService.js +193 -0
  198. package/dist/src/services/trackerService.js.map +1 -0
  199. package/dist/src/services/trackerService.test.d.ts +6 -0
  200. package/dist/src/services/trackerService.test.js +117 -0
  201. package/dist/src/services/trackerService.test.js.map +1 -0
  202. package/dist/src/services/trackerTypes.d.ts +51 -0
  203. package/dist/src/services/trackerTypes.js +33 -0
  204. package/dist/src/services/trackerTypes.js.map +1 -0
  205. package/dist/src/telemetry/billingEvents.d.ts +75 -0
  206. package/dist/src/telemetry/billingEvents.js +181 -0
  207. package/dist/src/telemetry/billingEvents.js.map +1 -0
  208. package/dist/src/telemetry/billingEvents.test.d.ts +6 -0
  209. package/dist/src/telemetry/billingEvents.test.js +139 -0
  210. package/dist/src/telemetry/billingEvents.test.js.map +1 -0
  211. package/dist/src/telemetry/conseca-logger.d.ts +9 -0
  212. package/dist/src/telemetry/conseca-logger.js +91 -0
  213. package/dist/src/telemetry/conseca-logger.js.map +1 -0
  214. package/dist/src/telemetry/conseca-logger.test.d.ts +6 -0
  215. package/dist/src/telemetry/conseca-logger.test.js +90 -0
  216. package/dist/src/telemetry/conseca-logger.test.js.map +1 -0
  217. package/dist/src/telemetry/trace.test.d.ts +6 -0
  218. package/dist/src/telemetry/trace.test.js +116 -0
  219. package/dist/src/telemetry/trace.test.js.map +1 -0
  220. package/dist/src/tools/definitions/trackerTools.d.ts +12 -0
  221. package/dist/src/tools/definitions/trackerTools.js +146 -0
  222. package/dist/src/tools/definitions/trackerTools.js.map +1 -0
  223. package/dist/src/tools/diff-utils.d.ts +9 -0
  224. package/dist/src/tools/diff-utils.js +66 -0
  225. package/dist/src/tools/diff-utils.js.map +1 -0
  226. package/dist/src/tools/diff-utils.test.d.ts +6 -0
  227. package/dist/src/tools/diff-utils.test.js +53 -0
  228. package/dist/src/tools/diff-utils.test.js.map +1 -0
  229. package/dist/src/tools/grep-utils.d.ts +49 -0
  230. package/dist/src/tools/grep-utils.js +147 -0
  231. package/dist/src/tools/grep-utils.js.map +1 -0
  232. package/dist/src/tools/omissionPlaceholderDetector.d.ts +15 -0
  233. package/dist/src/tools/omissionPlaceholderDetector.js +90 -0
  234. package/dist/src/tools/omissionPlaceholderDetector.js.map +1 -0
  235. package/dist/src/tools/omissionPlaceholderDetector.test.d.ts +6 -0
  236. package/dist/src/tools/omissionPlaceholderDetector.test.js +49 -0
  237. package/dist/src/tools/omissionPlaceholderDetector.test.js.map +1 -0
  238. package/dist/src/tools/trackerTools.d.ts +122 -0
  239. package/dist/src/tools/trackerTools.js +365 -0
  240. package/dist/src/tools/trackerTools.js.map +1 -0
  241. package/dist/src/tools/trackerTools.test.d.ts +6 -0
  242. package/dist/src/tools/trackerTools.test.js +97 -0
  243. package/dist/src/tools/trackerTools.test.js.map +1 -0
  244. package/dist/src/utils/approvalModeUtils.d.ts +14 -0
  245. package/dist/src/utils/approvalModeUtils.js +35 -0
  246. package/dist/src/utils/approvalModeUtils.js.map +1 -0
  247. package/dist/src/utils/approvalModeUtils.test.d.ts +6 -0
  248. package/dist/src/utils/approvalModeUtils.test.js +36 -0
  249. package/dist/src/utils/approvalModeUtils.test.js.map +1 -0
  250. package/dist/src/utils/cache.d.ts +63 -0
  251. package/dist/src/utils/cache.js +103 -0
  252. package/dist/src/utils/cache.js.map +1 -0
  253. package/dist/src/utils/cache.test.d.ts +6 -0
  254. package/dist/src/utils/cache.test.js +158 -0
  255. package/dist/src/utils/cache.test.js.map +1 -0
  256. package/dist/src/utils/checkpointUtils.d.ts +2 -2
  257. package/dist/src/utils/compatibility.d.ts +41 -0
  258. package/dist/src/utils/compatibility.js +112 -0
  259. package/dist/src/utils/compatibility.js.map +1 -0
  260. package/dist/src/utils/compatibility.test.d.ts +6 -0
  261. package/dist/src/utils/compatibility.test.js +233 -0
  262. package/dist/src/utils/compatibility.test.js.map +1 -0
  263. package/dist/src/utils/envExpansion.d.ts +18 -0
  264. package/dist/src/utils/envExpansion.js +46 -0
  265. package/dist/src/utils/envExpansion.js.map +1 -0
  266. package/dist/src/utils/envExpansion.test.d.ts +6 -0
  267. package/dist/src/utils/envExpansion.test.js +110 -0
  268. package/dist/src/utils/envExpansion.test.js.map +1 -0
  269. package/dist/src/utils/errors_timeout.test.d.ts +6 -0
  270. package/dist/src/utils/errors_timeout.test.js +40 -0
  271. package/dist/src/utils/errors_timeout.test.js.map +1 -0
  272. package/dist/src/utils/fetch.test.d.ts +6 -0
  273. package/dist/src/utils/fetch.test.js +206 -0
  274. package/dist/src/utils/fetch.test.js.map +1 -0
  275. package/dist/src/utils/markdownUtils.d.ts +22 -0
  276. package/dist/src/utils/markdownUtils.js +126 -0
  277. package/dist/src/utils/markdownUtils.js.map +1 -0
  278. package/dist/src/utils/markdownUtils.test.d.ts +6 -0
  279. package/dist/src/utils/markdownUtils.test.js +107 -0
  280. package/dist/src/utils/markdownUtils.test.js.map +1 -0
  281. package/dist/src/utils/oauth-flow.d.ts +105 -0
  282. package/dist/src/utils/oauth-flow.js +370 -0
  283. package/dist/src/utils/oauth-flow.js.map +1 -0
  284. package/dist/src/utils/oauth-flow.test.d.ts +6 -0
  285. package/dist/src/utils/oauth-flow.test.js +360 -0
  286. package/dist/src/utils/oauth-flow.test.js.map +1 -0
  287. package/dist/src/utils/sessionUtils.d.ts +14 -0
  288. package/dist/src/utils/sessionUtils.js +122 -0
  289. package/dist/src/utils/sessionUtils.js.map +1 -0
  290. package/dist/src/utils/sessionUtils.test.d.ts +1 -0
  291. package/dist/src/utils/sessionUtils.test.js +171 -0
  292. package/dist/src/utils/sessionUtils.test.js.map +1 -0
  293. package/dist/src/voice/responseFormatter.d.ts +38 -0
  294. package/dist/src/voice/responseFormatter.js +130 -0
  295. package/dist/src/voice/responseFormatter.js.map +1 -0
  296. package/dist/src/voice/responseFormatter.test.d.ts +6 -0
  297. package/dist/src/voice/responseFormatter.test.js +214 -0
  298. package/dist/src/voice/responseFormatter.test.js.map +1 -0
  299. package/dist/tsconfig.tsbuildinfo +1 -1
  300. package/package.json +1 -1
  301. package/dist/docs/CONTRIBUTING.md +0 -555
  302. package/dist/src/agents/executor.d.ts +0 -114
  303. package/dist/src/agents/executor.js +0 -779
  304. package/dist/src/agents/executor.js.map +0 -1
  305. package/dist/src/agents/executor.test.js +0 -1362
  306. package/dist/src/agents/executor.test.js.map +0 -1
  307. package/dist/src/agents/invocation.d.ts +0 -46
  308. package/dist/src/agents/invocation.js +0 -102
  309. package/dist/src/agents/invocation.js.map +0 -1
  310. package/dist/src/agents/invocation.test.js +0 -215
  311. package/dist/src/agents/invocation.test.js.map +0 -1
  312. package/dist/src/core/subagent.d.ts +0 -236
  313. package/dist/src/core/subagent.js +0 -482
  314. package/dist/src/core/subagent.js.map +0 -1
  315. package/dist/src/core/subagent.test.js +0 -530
  316. package/dist/src/core/subagent.test.js.map +0 -1
  317. package/dist/src/tools/smart-edit.d.ts +0 -78
  318. package/dist/src/tools/smart-edit.js +0 -717
  319. package/dist/src/tools/smart-edit.js.map +0 -1
  320. package/dist/src/tools/smart-edit.test.js +0 -592
  321. package/dist/src/tools/smart-edit.test.js.map +0 -1
  322. /package/dist/src/{agents/executor.test.d.ts → policy/workspace-policy.test.d.ts} +0 -0
  323. /package/dist/src/{agents/invocation.test.d.ts → safety/conseca/conseca.test.d.ts} +0 -0
  324. /package/dist/src/{core/subagent.test.d.ts → safety/conseca/integration.test.d.ts} +0 -0
  325. /package/dist/src/{tools/smart-edit.test.d.ts → safety/conseca/policy-enforcer.test.d.ts} +0 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import * as fs from 'node:fs/promises';
8
+ import * as path from 'node:path';
9
+ import * as os from 'node:os';
10
+ import { FolderTrustDiscoveryService } from './FolderTrustDiscoveryService.js';
11
+ import { GEMINI_DIR } from '../utils/paths.js';
12
+ describe('FolderTrustDiscoveryService', () => {
13
+ let tempDir;
14
+ beforeEach(async () => {
15
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gemini-discovery-test-'));
16
+ });
17
+ afterEach(async () => {
18
+ vi.restoreAllMocks();
19
+ await fs.rm(tempDir, { recursive: true, force: true });
20
+ });
21
+ it('should discover commands, skills, mcps, and hooks', async () => {
22
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
23
+ await fs.mkdir(geminiDir, { recursive: true });
24
+ // Mock commands
25
+ const commandsDir = path.join(geminiDir, 'commands');
26
+ await fs.mkdir(commandsDir);
27
+ await fs.writeFile(path.join(commandsDir, 'test-cmd.toml'), 'prompt = "test"');
28
+ // Mock skills
29
+ const skillsDir = path.join(geminiDir, 'skills');
30
+ await fs.mkdir(path.join(skillsDir, 'test-skill'), { recursive: true });
31
+ await fs.writeFile(path.join(skillsDir, 'test-skill', 'SKILL.md'), 'body');
32
+ // Mock settings (MCPs, Hooks, and general settings)
33
+ const settings = {
34
+ mcpServers: {
35
+ 'test-mcp': { command: 'node', args: ['test.js'] },
36
+ },
37
+ hooks: {
38
+ BeforeTool: [{ command: 'test-hook' }],
39
+ },
40
+ general: { vimMode: true },
41
+ ui: { theme: 'Dark' },
42
+ };
43
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), JSON.stringify(settings));
44
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
45
+ expect(results.commands).toContain('test-cmd');
46
+ expect(results.skills).toContain('test-skill');
47
+ expect(results.mcps).toContain('test-mcp');
48
+ expect(results.hooks).toContain('test-hook');
49
+ expect(results.settings).toContain('general');
50
+ expect(results.settings).toContain('ui');
51
+ expect(results.settings).not.toContain('mcpServers');
52
+ expect(results.settings).not.toContain('hooks');
53
+ });
54
+ it('should flag security warnings for sensitive settings', async () => {
55
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
56
+ await fs.mkdir(geminiDir, { recursive: true });
57
+ const settings = {
58
+ tools: {
59
+ allowed: ['git'],
60
+ sandbox: false,
61
+ },
62
+ experimental: {
63
+ enableAgents: true,
64
+ },
65
+ security: {
66
+ folderTrust: {
67
+ enabled: false,
68
+ },
69
+ },
70
+ };
71
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), JSON.stringify(settings));
72
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
73
+ expect(results.securityWarnings).toContain('This project auto-approves certain tools (tools.allowed).');
74
+ expect(results.securityWarnings).toContain('This project enables autonomous agents (enableAgents).');
75
+ expect(results.securityWarnings).toContain('This project attempts to disable folder trust (security.folderTrust.enabled).');
76
+ expect(results.securityWarnings).toContain('This project disables the security sandbox (tools.sandbox).');
77
+ });
78
+ it('should handle missing .gemini directory', async () => {
79
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
80
+ expect(results.commands).toHaveLength(0);
81
+ expect(results.skills).toHaveLength(0);
82
+ expect(results.mcps).toHaveLength(0);
83
+ expect(results.hooks).toHaveLength(0);
84
+ expect(results.settings).toHaveLength(0);
85
+ });
86
+ it('should handle malformed settings.json', async () => {
87
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
88
+ await fs.mkdir(geminiDir, { recursive: true });
89
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), 'invalid json');
90
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
91
+ expect(results.discoveryErrors[0]).toContain('Failed to discover settings: Unexpected token');
92
+ });
93
+ it('should handle null settings.json', async () => {
94
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
95
+ await fs.mkdir(geminiDir, { recursive: true });
96
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), 'null');
97
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
98
+ expect(results.discoveryErrors).toHaveLength(0);
99
+ expect(results.settings).toHaveLength(0);
100
+ });
101
+ it('should handle array settings.json', async () => {
102
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
103
+ await fs.mkdir(geminiDir, { recursive: true });
104
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), '[]');
105
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
106
+ expect(results.discoveryErrors).toHaveLength(0);
107
+ expect(results.settings).toHaveLength(0);
108
+ });
109
+ it('should handle string settings.json', async () => {
110
+ const geminiDir = path.join(tempDir, GEMINI_DIR);
111
+ await fs.mkdir(geminiDir, { recursive: true });
112
+ await fs.writeFile(path.join(geminiDir, 'settings.json'), '"string"');
113
+ const results = await FolderTrustDiscoveryService.discover(tempDir);
114
+ expect(results.discoveryErrors).toHaveLength(0);
115
+ expect(results.settings).toHaveLength(0);
116
+ });
117
+ });
118
+ //# sourceMappingURL=FolderTrustDiscoveryService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FolderTrustDiscoveryService.test.js","sourceRoot":"","sources":["../../../src/services/FolderTrustDiscoveryService.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { Keychain } from './keychainTypes.js';
7
+ export declare class FileKeychain implements Keychain {
8
+ private readonly tokenFilePath;
9
+ private readonly encryptionKey;
10
+ constructor();
11
+ private deriveEncryptionKey;
12
+ private encrypt;
13
+ private decrypt;
14
+ private ensureDirectoryExists;
15
+ private loadData;
16
+ private saveData;
17
+ getPassword(service: string, account: string): Promise<string | null>;
18
+ setPassword(service: string, account: string, password: string): Promise<void>;
19
+ deletePassword(service: string, account: string): Promise<boolean>;
20
+ findCredentials(service: string): Promise<Array<{
21
+ account: string;
22
+ password: string;
23
+ }>>;
24
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { promises as fs } from 'node:fs';
7
+ import * as path from 'node:path';
8
+ import * as os from 'node:os';
9
+ import * as crypto from 'node:crypto';
10
+ import { GEMINI_DIR, homedir } from '../utils/paths.js';
11
+ export class FileKeychain {
12
+ tokenFilePath;
13
+ encryptionKey;
14
+ constructor() {
15
+ const configDir = path.join(homedir(), GEMINI_DIR);
16
+ this.tokenFilePath = path.join(configDir, 'gemini-credentials.json');
17
+ this.encryptionKey = this.deriveEncryptionKey();
18
+ }
19
+ deriveEncryptionKey() {
20
+ const salt = `${os.hostname()}-${os.userInfo().username}-gemini-cli`;
21
+ return crypto.scryptSync('gemini-cli-oauth', salt, 32);
22
+ }
23
+ encrypt(text) {
24
+ const iv = crypto.randomBytes(16);
25
+ const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
26
+ let encrypted = cipher.update(text, 'utf8', 'hex');
27
+ encrypted += cipher.final('hex');
28
+ const authTag = cipher.getAuthTag();
29
+ return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
30
+ }
31
+ decrypt(encryptedData) {
32
+ const parts = encryptedData.split(':');
33
+ if (parts.length !== 3) {
34
+ throw new Error('Invalid encrypted data format');
35
+ }
36
+ const iv = Buffer.from(parts[0], 'hex');
37
+ const authTag = Buffer.from(parts[1], 'hex');
38
+ const encrypted = parts[2];
39
+ const decipher = crypto.createDecipheriv('aes-256-gcm', this.encryptionKey, iv);
40
+ decipher.setAuthTag(authTag);
41
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
42
+ decrypted += decipher.final('utf8');
43
+ return decrypted;
44
+ }
45
+ async ensureDirectoryExists() {
46
+ const dir = path.dirname(this.tokenFilePath);
47
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
48
+ }
49
+ async loadData() {
50
+ try {
51
+ const data = await fs.readFile(this.tokenFilePath, 'utf-8');
52
+ const decrypted = this.decrypt(data);
53
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
54
+ return JSON.parse(decrypted);
55
+ }
56
+ catch (error) {
57
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
58
+ const err = error;
59
+ if (err.code === 'ENOENT') {
60
+ return {};
61
+ }
62
+ if (err.message?.includes('Invalid encrypted data format') ||
63
+ err.message?.includes('Unsupported state or unable to authenticate data')) {
64
+ throw new Error(`Corrupted credentials file detected at: ${this.tokenFilePath}\n` +
65
+ `Please delete or rename this file to resolve the issue.`);
66
+ }
67
+ throw error;
68
+ }
69
+ }
70
+ async saveData(data) {
71
+ await this.ensureDirectoryExists();
72
+ const json = JSON.stringify(data, null, 2);
73
+ const encrypted = this.encrypt(json);
74
+ await fs.writeFile(this.tokenFilePath, encrypted, { mode: 0o600 });
75
+ }
76
+ async getPassword(service, account) {
77
+ const data = await this.loadData();
78
+ return data[service]?.[account] ?? null;
79
+ }
80
+ async setPassword(service, account, password) {
81
+ const data = await this.loadData();
82
+ if (!data[service]) {
83
+ data[service] = {};
84
+ }
85
+ data[service][account] = password;
86
+ await this.saveData(data);
87
+ }
88
+ async deletePassword(service, account) {
89
+ const data = await this.loadData();
90
+ if (data[service] && account in data[service]) {
91
+ delete data[service][account];
92
+ if (Object.keys(data[service]).length === 0) {
93
+ delete data[service];
94
+ }
95
+ if (Object.keys(data).length === 0) {
96
+ try {
97
+ await fs.unlink(this.tokenFilePath);
98
+ }
99
+ catch (error) {
100
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
101
+ const err = error;
102
+ if (err.code !== 'ENOENT') {
103
+ throw error;
104
+ }
105
+ }
106
+ }
107
+ else {
108
+ await this.saveData(data);
109
+ }
110
+ return true;
111
+ }
112
+ return false;
113
+ }
114
+ async findCredentials(service) {
115
+ const data = await this.loadData();
116
+ const serviceData = data[service] || {};
117
+ return Object.entries(serviceData).map(([account, password]) => ({
118
+ account,
119
+ password,
120
+ }));
121
+ }
122
+ }
123
+ //# sourceMappingURL=fileKeychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileKeychain.js","sourceRoot":"","sources":["../../../src/services/fileKeychain.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,OAAO,YAAY;IACN,aAAa,CAAS;IACtB,aAAa,CAAS;IAEvC;QACE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;QACrE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAClD,CAAC;IAEO,mBAAmB;QACzB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,aAAa,CAAC;QACrE,OAAO,MAAM,CAAC,UAAU,CAAC,kBAAkB,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IAEO,OAAO,CAAC,IAAY;QAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAE5E,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACnD,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEjC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAEpC,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC;IAC9E,CAAC;IAEO,OAAO,CAAC,aAAqB;QACnC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAE3B,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CACtC,aAAa,EACb,IAAI,CAAC,aAAa,EAClB,EAAE,CACH,CAAC;QACF,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAE7B,IAAI,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC1D,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrC,uEAAuE;YACvE,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAA2C,CAAC;QACzE,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,uEAAuE;YACvE,MAAM,GAAG,GAAG,KAAqD,CAAC;YAClE,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,IACE,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,+BAA+B,CAAC;gBACtD,GAAG,CAAC,OAAO,EAAE,QAAQ,CACnB,kDAAkD,CACnD,EACD,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,CAAC,aAAa,IAAI;oBAC/D,yDAAyD,CAC5D,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,QAAQ,CACpB,IAA4C;QAE5C,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,OAAe;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,WAAW,CACf,OAAe,EACf,OAAe,EACf,QAAgB;QAEhB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;QAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,OAAe,EAAE,OAAe;QACnD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;YAE9B,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC;oBACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACtC,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,uEAAuE;oBACvE,MAAM,GAAG,GAAG,KAA8B,CAAC;oBAC3C,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;wBAC1B,MAAM,KAAK,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,OAAe;QAEf,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACxC,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/D,OAAO;YACP,QAAQ;SACT,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export declare const FORCE_FILE_STORAGE_ENV_VAR = "GEMINI_FORCE_FILE_STORAGE";
7
+ /**
8
+ * Service for interacting with OS-level secure storage (e.g. keytar).
9
+ */
10
+ export declare class KeychainService {
11
+ private readonly serviceName;
12
+ private initializationPromise?;
13
+ /**
14
+ * @param serviceName Unique identifier for the app in the OS keychain.
15
+ */
16
+ constructor(serviceName: string);
17
+ isAvailable(): Promise<boolean>;
18
+ /**
19
+ * Returns true if the service is using the encrypted file fallback backend.
20
+ */
21
+ isUsingFileFallback(): Promise<boolean>;
22
+ /**
23
+ * Retrieves a secret for the given account.
24
+ * @throws Error if the keychain is unavailable.
25
+ */
26
+ getPassword(account: string): Promise<string | null>;
27
+ /**
28
+ * Securely stores a secret.
29
+ * @throws Error if the keychain is unavailable.
30
+ */
31
+ setPassword(account: string, value: string): Promise<void>;
32
+ /**
33
+ * Removes a secret from the keychain.
34
+ * @returns true if the secret was deleted, false otherwise.
35
+ * @throws Error if the keychain is unavailable.
36
+ */
37
+ deletePassword(account: string): Promise<boolean>;
38
+ /**
39
+ * Lists all account/secret pairs stored under this service.
40
+ * @throws Error if the keychain is unavailable.
41
+ */
42
+ findCredentials(): Promise<Array<{
43
+ account: string;
44
+ password: string;
45
+ }>>;
46
+ private getKeychainOrThrow;
47
+ private getKeychain;
48
+ private initializeKeychain;
49
+ private loadKeychainModule;
50
+ private isKeychainFunctional;
51
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import * as crypto from 'node:crypto';
7
+ import { coreEvents } from '../utils/events.js';
8
+ import { KeychainAvailabilityEvent } from '../telemetry/types.js';
9
+ import { debugLogger } from '../utils/debugLogger.js';
10
+ import { KeychainSchema, KEYCHAIN_TEST_PREFIX, } from './keychainTypes.js';
11
+ import { isRecord } from '../utils/markdownUtils.js';
12
+ import { FileKeychain } from './fileKeychain.js';
13
+ export const FORCE_FILE_STORAGE_ENV_VAR = 'GEMINI_FORCE_FILE_STORAGE';
14
+ /**
15
+ * Service for interacting with OS-level secure storage (e.g. keytar).
16
+ */
17
+ export class KeychainService {
18
+ serviceName;
19
+ // Track an ongoing initialization attempt to avoid race conditions.
20
+ initializationPromise;
21
+ /**
22
+ * @param serviceName Unique identifier for the app in the OS keychain.
23
+ */
24
+ constructor(serviceName) {
25
+ this.serviceName = serviceName;
26
+ }
27
+ async isAvailable() {
28
+ return (await this.getKeychain()) !== null;
29
+ }
30
+ /**
31
+ * Returns true if the service is using the encrypted file fallback backend.
32
+ */
33
+ async isUsingFileFallback() {
34
+ const keychain = await this.getKeychain();
35
+ return keychain instanceof FileKeychain;
36
+ }
37
+ /**
38
+ * Retrieves a secret for the given account.
39
+ * @throws Error if the keychain is unavailable.
40
+ */
41
+ async getPassword(account) {
42
+ const keychain = await this.getKeychainOrThrow();
43
+ return keychain.getPassword(this.serviceName, account);
44
+ }
45
+ /**
46
+ * Securely stores a secret.
47
+ * @throws Error if the keychain is unavailable.
48
+ */
49
+ async setPassword(account, value) {
50
+ const keychain = await this.getKeychainOrThrow();
51
+ await keychain.setPassword(this.serviceName, account, value);
52
+ }
53
+ /**
54
+ * Removes a secret from the keychain.
55
+ * @returns true if the secret was deleted, false otherwise.
56
+ * @throws Error if the keychain is unavailable.
57
+ */
58
+ async deletePassword(account) {
59
+ const keychain = await this.getKeychainOrThrow();
60
+ return keychain.deletePassword(this.serviceName, account);
61
+ }
62
+ /**
63
+ * Lists all account/secret pairs stored under this service.
64
+ * @throws Error if the keychain is unavailable.
65
+ */
66
+ async findCredentials() {
67
+ const keychain = await this.getKeychainOrThrow();
68
+ return keychain.findCredentials(this.serviceName);
69
+ }
70
+ async getKeychainOrThrow() {
71
+ const keychain = await this.getKeychain();
72
+ if (!keychain) {
73
+ throw new Error('Keychain is not available');
74
+ }
75
+ return keychain;
76
+ }
77
+ getKeychain() {
78
+ return (this.initializationPromise ??= this.initializeKeychain());
79
+ }
80
+ // High-level orchestration of the loading and testing cycle.
81
+ async initializeKeychain() {
82
+ let resultKeychain = null;
83
+ const forceFileStorage = process.env[FORCE_FILE_STORAGE_ENV_VAR] === 'true';
84
+ if (!forceFileStorage) {
85
+ try {
86
+ const keychainModule = await this.loadKeychainModule();
87
+ if (keychainModule) {
88
+ if (await this.isKeychainFunctional(keychainModule)) {
89
+ resultKeychain = keychainModule;
90
+ }
91
+ else {
92
+ debugLogger.log('Keychain functional verification failed');
93
+ }
94
+ }
95
+ }
96
+ catch (error) {
97
+ // Avoid logging full error objects to prevent PII exposure.
98
+ const message = error instanceof Error ? error.message : String(error);
99
+ debugLogger.log('Keychain initialization encountered an error:', message);
100
+ }
101
+ }
102
+ coreEvents.emitTelemetryKeychainAvailability(new KeychainAvailabilityEvent(resultKeychain !== null && !forceFileStorage));
103
+ // Fallback to FileKeychain if native keychain is unavailable or file storage is forced
104
+ if (!resultKeychain) {
105
+ resultKeychain = new FileKeychain();
106
+ debugLogger.log('Using FileKeychain fallback for secure storage.');
107
+ }
108
+ return resultKeychain;
109
+ }
110
+ // Low-level dynamic loading and structural validation.
111
+ async loadKeychainModule() {
112
+ const moduleName = 'keytar';
113
+ const module = await import(moduleName);
114
+ const potential = (isRecord(module) && module['default']) || module;
115
+ const result = KeychainSchema.safeParse(potential);
116
+ if (result.success) {
117
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
118
+ return potential;
119
+ }
120
+ debugLogger.log('Keychain module failed structural validation:', result.error.flatten().fieldErrors);
121
+ return null;
122
+ }
123
+ // Performs a set-get-delete cycle to verify keychain functionality.
124
+ async isKeychainFunctional(keychain) {
125
+ const testAccount = `${KEYCHAIN_TEST_PREFIX}${crypto.randomBytes(8).toString('hex')}`;
126
+ const testPassword = 'test';
127
+ await keychain.setPassword(this.serviceName, testAccount, testPassword);
128
+ const retrieved = await keychain.getPassword(this.serviceName, testAccount);
129
+ const deleted = await keychain.deletePassword(this.serviceName, testAccount);
130
+ return deleted && retrieved === testPassword;
131
+ }
132
+ }
133
+ //# sourceMappingURL=keychainService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychainService.js","sourceRoot":"","sources":["../../../src/services/keychainService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export {};
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
7
+ import { KeychainService } from './keychainService.js';
8
+ import { coreEvents } from '../utils/events.js';
9
+ import { debugLogger } from '../utils/debugLogger.js';
10
+ import { FileKeychain } from './fileKeychain.js';
11
+ const mockKeytar = {
12
+ getPassword: vi.fn(),
13
+ setPassword: vi.fn(),
14
+ deletePassword: vi.fn(),
15
+ findCredentials: vi.fn(),
16
+ };
17
+ const mockFileKeychain = {
18
+ getPassword: vi.fn(),
19
+ setPassword: vi.fn(),
20
+ deletePassword: vi.fn(),
21
+ findCredentials: vi.fn(),
22
+ };
23
+ vi.mock('keytar', () => ({ default: mockKeytar }));
24
+ vi.mock('./fileKeychain.js', () => ({
25
+ FileKeychain: vi.fn(() => mockFileKeychain),
26
+ }));
27
+ vi.mock('../utils/events.js', () => ({
28
+ coreEvents: { emitTelemetryKeychainAvailability: vi.fn() },
29
+ }));
30
+ vi.mock('../utils/debugLogger.js', () => ({
31
+ debugLogger: { log: vi.fn() },
32
+ }));
33
+ describe('KeychainService', () => {
34
+ let service;
35
+ const SERVICE_NAME = 'test-service';
36
+ let passwords = {};
37
+ const originalEnv = process.env;
38
+ beforeEach(() => {
39
+ vi.clearAllMocks();
40
+ process.env = { ...originalEnv };
41
+ service = new KeychainService(SERVICE_NAME);
42
+ passwords = {};
43
+ // Stateful mock implementation for native keychain
44
+ mockKeytar.setPassword?.mockImplementation((_svc, acc, val) => {
45
+ passwords[acc] = val;
46
+ return Promise.resolve();
47
+ });
48
+ mockKeytar.getPassword?.mockImplementation((_svc, acc) => Promise.resolve(passwords[acc] ?? null));
49
+ mockKeytar.deletePassword?.mockImplementation((_svc, acc) => {
50
+ const exists = !!passwords[acc];
51
+ delete passwords[acc];
52
+ return Promise.resolve(exists);
53
+ });
54
+ mockKeytar.findCredentials?.mockImplementation(() => Promise.resolve(Object.entries(passwords).map(([account, password]) => ({
55
+ account,
56
+ password,
57
+ }))));
58
+ // Stateful mock implementation for fallback file keychain
59
+ mockFileKeychain.setPassword?.mockImplementation((_svc, acc, val) => {
60
+ passwords[acc] = val;
61
+ return Promise.resolve();
62
+ });
63
+ mockFileKeychain.getPassword?.mockImplementation((_svc, acc) => Promise.resolve(passwords[acc] ?? null));
64
+ mockFileKeychain.deletePassword?.mockImplementation((_svc, acc) => {
65
+ const exists = !!passwords[acc];
66
+ delete passwords[acc];
67
+ return Promise.resolve(exists);
68
+ });
69
+ mockFileKeychain.findCredentials?.mockImplementation(() => Promise.resolve(Object.entries(passwords).map(([account, password]) => ({
70
+ account,
71
+ password,
72
+ }))));
73
+ });
74
+ afterEach(() => {
75
+ process.env = originalEnv;
76
+ });
77
+ describe('isAvailable', () => {
78
+ it('should return true and emit telemetry on successful functional test with native keychain', async () => {
79
+ const available = await service.isAvailable();
80
+ expect(available).toBe(true);
81
+ expect(mockKeytar.setPassword).toHaveBeenCalled();
82
+ expect(coreEvents.emitTelemetryKeychainAvailability).toHaveBeenCalledWith(expect.objectContaining({ available: true }));
83
+ });
84
+ it('should return true (via fallback), log error, and emit telemetry indicating native is unavailable on failed functional test', async () => {
85
+ mockKeytar.setPassword?.mockRejectedValue(new Error('locked'));
86
+ const available = await service.isAvailable();
87
+ // Because it falls back to FileKeychain, it is always available.
88
+ expect(available).toBe(true);
89
+ expect(debugLogger.log).toHaveBeenCalledWith(expect.stringContaining('encountered an error'), 'locked');
90
+ expect(coreEvents.emitTelemetryKeychainAvailability).toHaveBeenCalledWith(expect.objectContaining({ available: false }));
91
+ expect(debugLogger.log).toHaveBeenCalledWith(expect.stringContaining('Using FileKeychain fallback'));
92
+ expect(FileKeychain).toHaveBeenCalled();
93
+ });
94
+ it('should return true (via fallback), log validation error, and emit telemetry on module load failure', async () => {
95
+ const originalMock = mockKeytar.getPassword;
96
+ mockKeytar.getPassword = undefined; // Break schema
97
+ const available = await service.isAvailable();
98
+ expect(available).toBe(true);
99
+ expect(debugLogger.log).toHaveBeenCalledWith(expect.stringContaining('failed structural validation'), expect.objectContaining({ getPassword: expect.any(Array) }));
100
+ expect(coreEvents.emitTelemetryKeychainAvailability).toHaveBeenCalledWith(expect.objectContaining({ available: false }));
101
+ expect(FileKeychain).toHaveBeenCalled();
102
+ mockKeytar.getPassword = originalMock;
103
+ });
104
+ it('should log failure if functional test cycle returns false, then fallback', async () => {
105
+ mockKeytar.getPassword?.mockResolvedValue('wrong-password');
106
+ const available = await service.isAvailable();
107
+ expect(available).toBe(true);
108
+ expect(debugLogger.log).toHaveBeenCalledWith(expect.stringContaining('functional verification failed'));
109
+ expect(FileKeychain).toHaveBeenCalled();
110
+ });
111
+ it('should fallback to FileKeychain when GEMINI_FORCE_FILE_STORAGE is true', async () => {
112
+ process.env['GEMINI_FORCE_FILE_STORAGE'] = 'true';
113
+ const available = await service.isAvailable();
114
+ expect(available).toBe(true);
115
+ expect(FileKeychain).toHaveBeenCalled();
116
+ expect(coreEvents.emitTelemetryKeychainAvailability).toHaveBeenCalledWith(expect.objectContaining({ available: false }));
117
+ });
118
+ it('should cache the result and handle concurrent initialization attempts once', async () => {
119
+ await Promise.all([
120
+ service.isAvailable(),
121
+ service.isAvailable(),
122
+ service.isAvailable(),
123
+ ]);
124
+ expect(mockKeytar.setPassword).toHaveBeenCalledTimes(1);
125
+ });
126
+ });
127
+ describe('Password Operations', () => {
128
+ beforeEach(async () => {
129
+ await service.isAvailable();
130
+ vi.clearAllMocks();
131
+ });
132
+ it('should store, retrieve, and delete passwords correctly', async () => {
133
+ await service.setPassword('acc1', 'secret1');
134
+ await service.setPassword('acc2', 'secret2');
135
+ expect(await service.getPassword('acc1')).toBe('secret1');
136
+ expect(await service.getPassword('acc2')).toBe('secret2');
137
+ const creds = await service.findCredentials();
138
+ expect(creds).toHaveLength(2);
139
+ expect(creds).toContainEqual({ account: 'acc1', password: 'secret1' });
140
+ expect(await service.deletePassword('acc1')).toBe(true);
141
+ expect(await service.getPassword('acc1')).toBeNull();
142
+ expect(await service.findCredentials()).toHaveLength(1);
143
+ });
144
+ it('getPassword should return null if key is missing', async () => {
145
+ expect(await service.getPassword('missing')).toBeNull();
146
+ });
147
+ });
148
+ // Removing 'When Unavailable' tests since the service is always available via fallback
149
+ });
150
+ //# sourceMappingURL=keychainService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychainService.test.js","sourceRoot":"","sources":["../../../src/services/keychainService.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,QAAQ,EACR,EAAE,EACF,MAAM,EACN,EAAE,EACF,UAAU,EACV,SAAS,GAEV,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AASjD,MAAM,UAAU,GAAiB;IAC/B,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE;IACpB,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE;IACpB,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;IACvB,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE;CACzB,CAAC;AAEF,MAAM,gBAAgB,GAAiB;IACrC,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE;IACpB,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE;IACpB,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;IACvB,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE;CACzB,CAAC;AAEF,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AAEnD,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC;CAC5C,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,EAAE,iCAAiC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;CAC3D,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;CAC9B,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,OAAwB,CAAC;IAC7B,MAAM,YAAY,GAAG,cAAc,CAAC;IACpC,IAAI,SAAS,GAA2B,EAAE,CAAC;IAC3C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;QACjC,OAAO,GAAG,IAAI,eAAe,CAAC,YAAY,CAAC,CAAC;QAC5C,SAAS,GAAG,EAAE,CAAC;QAEf,mDAAmD;QACnD,UAAU,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5D,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;YACrB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CACvD,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CACxC,CAAC;QACF,UAAU,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC1D,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAChC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;YACtB,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,EAAE,CAClD,OAAO,CAAC,OAAO,CACb,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,OAAO;YACP,QAAQ;SACT,CAAC,CAAC,CACJ,CACF,CAAC;QAEF,0DAA0D;QAC1D,gBAAgB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAClE,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;YACrB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,gBAAgB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAC7D,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CACxC,CAAC;QACF,gBAAgB,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAChE,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAChC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;YACtB,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,gBAAgB,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,EAAE,CACxD,OAAO,CAAC,OAAO,CACb,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,OAAO;YACP,QAAQ;SACT,CAAC,CAAC,CACJ,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,GAAG,GAAG,WAAW,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,0FAA0F,EAAE,KAAK,IAAI,EAAE;YACxG,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE9C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,gBAAgB,EAAE,CAAC;YAClD,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC,oBAAoB,CACvE,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAC7C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6HAA6H,EAAE,KAAK,IAAI,EAAE;YAC3I,UAAU,CAAC,WAAW,EAAE,iBAAiB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAE/D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE9C,iEAAiE;YACjE,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,EAC/C,QAAQ,CACT,CAAC;YACF,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC,oBAAoB,CACvE,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAC9C,CAAC;YACF,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CAAC,6BAA6B,CAAC,CACvD,CAAC;YACF,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oGAAoG,EAAE,KAAK,IAAI,EAAE;YAClH,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC;YAC5C,UAAU,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,eAAe;YAEnD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE9C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CAAC,8BAA8B,CAAC,EACvD,MAAM,CAAC,gBAAgB,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAC5D,CAAC;YACF,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC,oBAAoB,CACvE,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAC9C,CAAC;YACF,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAC;YAExC,UAAU,CAAC,WAAW,GAAG,YAAY,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;YACxF,UAAU,CAAC,WAAW,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;YAE5D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE9C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CAAC,gCAAgC,CAAC,CAC1D,CAAC;YACF,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;YACtF,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,GAAG,MAAM,CAAC;YAClD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACxC,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC,oBAAoB,CACvE,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAC9C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;YAC1F,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChB,OAAO,CAAC,WAAW,EAAE;gBACrB,OAAO,CAAC,WAAW,EAAE;gBACrB,OAAO,CAAC,WAAW,EAAE;aACtB,CAAC,CAAC;YAEH,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,UAAU,CAAC,KAAK,IAAI,EAAE;YACpB,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAC5B,EAAE,CAAC,aAAa,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;YACtE,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC7C,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAE7C,MAAM,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC1D,MAAM,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAE1D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YAEvE,MAAM,CAAC,MAAM,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;YACrD,MAAM,CAAC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,uFAAuF;AACzF,CAAC,CAAC,CAAC"}