@ashx-j/lunr 0.2.26 → 0.2.27

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 (639) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/LICENSE +22 -0
  3. package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
  4. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-thinking.js +1 -1
  6. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
  8. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  9. package/dist/builtin-extensions/ashxj-tui.js +6 -10
  10. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  11. package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
  12. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  13. package/dist/builtin-extensions/lunr-cron.js +94 -108
  14. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  15. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
  16. package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
  17. package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
  18. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
  19. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
  20. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
  21. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  22. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
  23. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  24. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
  25. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
  26. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
  27. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-intercom/index.js +2 -2
  29. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  30. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
  31. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
  33. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
  34. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
  36. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  37. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
  38. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
  40. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
  41. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
  42. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
  44. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
  45. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
  46. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
  47. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
  48. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
  49. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
  50. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
  52. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
  53. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
  54. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
  56. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
  57. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
  58. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
  60. package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
  61. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
  62. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
  64. package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
  65. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
  67. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
  68. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
  69. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
  70. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
  71. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
  73. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  74. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  75. package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
  76. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  77. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
  78. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
  79. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
  80. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
  81. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
  82. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
  83. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
  84. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
  85. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
  86. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
  87. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
  88. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
  89. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
  90. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
  91. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
  92. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
  93. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
  94. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
  95. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
  96. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
  97. package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
  98. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
  99. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
  101. package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
  102. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
  103. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
  104. package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
  105. package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
  106. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
  107. package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
  108. package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
  109. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
  110. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  111. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
  112. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  113. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  114. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
  115. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  116. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  117. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
  118. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  119. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
  120. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  121. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +15 -31
  122. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  123. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  124. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
  125. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  126. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  127. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +10 -2
  128. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  129. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  130. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
  131. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  132. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  133. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +106 -24
  134. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  135. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  136. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +25 -5
  137. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  138. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
  139. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  140. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
  141. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  142. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
  143. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  144. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
  145. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  146. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
  147. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
  148. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
  149. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
  150. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  151. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
  152. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  153. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
  154. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  155. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +17 -8
  156. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  157. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  158. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
  159. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  160. package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
  161. package/dist/builtin-extensions/pi-tps.js +20 -17
  162. package/dist/builtin-extensions/pi-tps.js.map +1 -1
  163. package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
  164. package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
  165. package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
  166. package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
  167. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
  168. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
  169. package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
  170. package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
  171. package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
  172. package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
  173. package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
  174. package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
  175. package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
  176. package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
  177. package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
  178. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
  179. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
  180. package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
  181. package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
  182. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  183. package/dist/builtin-extensions/pi-web-access/index.js +48 -73
  184. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  185. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
  186. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
  187. package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
  188. package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
  189. package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
  190. package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
  191. package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
  192. package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
  193. package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
  194. package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
  195. package/dist/builtin-extensions/simple-pi-memory.js +1 -1
  196. package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
  197. package/dist/cli/install-cli.d.ts.map +1 -1
  198. package/dist/cli/install-cli.js +38 -30
  199. package/dist/cli/install-cli.js.map +1 -1
  200. package/dist/core/agent-session-runtime.d.ts +13 -0
  201. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  202. package/dist/core/agent-session-runtime.js +75 -11
  203. package/dist/core/agent-session-runtime.js.map +1 -1
  204. package/dist/core/agent-session-services.d.ts +1 -0
  205. package/dist/core/agent-session-services.d.ts.map +1 -1
  206. package/dist/core/agent-session-services.js +1 -0
  207. package/dist/core/agent-session-services.js.map +1 -1
  208. package/dist/core/agent-session.d.ts +40 -6
  209. package/dist/core/agent-session.d.ts.map +1 -1
  210. package/dist/core/agent-session.js +309 -76
  211. package/dist/core/agent-session.js.map +1 -1
  212. package/dist/core/browser/runtime.d.ts +2 -0
  213. package/dist/core/browser/runtime.d.ts.map +1 -1
  214. package/dist/core/browser/runtime.js +80 -45
  215. package/dist/core/browser/runtime.js.map +1 -1
  216. package/dist/core/browser/setup.d.ts.map +1 -1
  217. package/dist/core/browser/setup.js +12 -1
  218. package/dist/core/browser/setup.js.map +1 -1
  219. package/dist/core/computer-use/adapter.d.ts.map +1 -1
  220. package/dist/core/computer-use/adapter.js +7 -2
  221. package/dist/core/computer-use/adapter.js.map +1 -1
  222. package/dist/core/computer-use/image.d.ts.map +1 -1
  223. package/dist/core/computer-use/image.js +17 -5
  224. package/dist/core/computer-use/image.js.map +1 -1
  225. package/dist/core/computer-use/schemas.d.ts.map +1 -1
  226. package/dist/core/computer-use/schemas.js +23 -6
  227. package/dist/core/computer-use/schemas.js.map +1 -1
  228. package/dist/core/computer-use/workflow.d.ts.map +1 -1
  229. package/dist/core/computer-use/workflow.js +459 -119
  230. package/dist/core/computer-use/workflow.js.map +1 -1
  231. package/dist/core/cron/jobs.d.ts +26 -8
  232. package/dist/core/cron/jobs.d.ts.map +1 -1
  233. package/dist/core/cron/jobs.js +357 -232
  234. package/dist/core/cron/jobs.js.map +1 -1
  235. package/dist/core/cron/origin-context.d.ts +1 -0
  236. package/dist/core/cron/origin-context.d.ts.map +1 -1
  237. package/dist/core/cron/origin-context.js.map +1 -1
  238. package/dist/core/cron/scheduler.d.ts +22 -17
  239. package/dist/core/cron/scheduler.d.ts.map +1 -1
  240. package/dist/core/cron/scheduler.js +153 -73
  241. package/dist/core/cron/scheduler.js.map +1 -1
  242. package/dist/core/extensions/index.d.ts +1 -1
  243. package/dist/core/extensions/index.d.ts.map +1 -1
  244. package/dist/core/extensions/index.js.map +1 -1
  245. package/dist/core/extensions/runner.d.ts +1 -0
  246. package/dist/core/extensions/runner.d.ts.map +1 -1
  247. package/dist/core/extensions/runner.js +8 -0
  248. package/dist/core/extensions/runner.js.map +1 -1
  249. package/dist/core/extensions/types.d.ts +8 -0
  250. package/dist/core/extensions/types.d.ts.map +1 -1
  251. package/dist/core/extensions/types.js.map +1 -1
  252. package/dist/core/footer-data-provider.d.ts +4 -0
  253. package/dist/core/footer-data-provider.d.ts.map +1 -1
  254. package/dist/core/footer-data-provider.js +22 -1
  255. package/dist/core/footer-data-provider.js.map +1 -1
  256. package/dist/core/large-subagent-launch.d.ts +1 -1
  257. package/dist/core/large-subagent-launch.d.ts.map +1 -1
  258. package/dist/core/large-subagent-launch.js +7 -7
  259. package/dist/core/large-subagent-launch.js.map +1 -1
  260. package/dist/core/model-registry.d.ts +2 -0
  261. package/dist/core/model-registry.d.ts.map +1 -1
  262. package/dist/core/model-registry.js +5 -0
  263. package/dist/core/model-registry.js.map +1 -1
  264. package/dist/core/model-runtime.d.ts +4 -1
  265. package/dist/core/model-runtime.d.ts.map +1 -1
  266. package/dist/core/model-runtime.js +40 -4
  267. package/dist/core/model-runtime.js.map +1 -1
  268. package/dist/core/package-manager.d.ts.map +1 -1
  269. package/dist/core/package-manager.js.map +1 -1
  270. package/dist/core/permissions.d.ts +3 -3
  271. package/dist/core/permissions.d.ts.map +1 -1
  272. package/dist/core/permissions.js +11 -35
  273. package/dist/core/permissions.js.map +1 -1
  274. package/dist/core/plan-mode.d.ts +5 -13
  275. package/dist/core/plan-mode.d.ts.map +1 -1
  276. package/dist/core/plan-mode.js +126 -116
  277. package/dist/core/plan-mode.js.map +1 -1
  278. package/dist/core/project-trust.d.ts.map +1 -1
  279. package/dist/core/project-trust.js +1 -1
  280. package/dist/core/project-trust.js.map +1 -1
  281. package/dist/core/prompt-resource-bridge.d.ts +12 -0
  282. package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
  283. package/dist/core/prompt-resource-bridge.js +9 -0
  284. package/dist/core/prompt-resource-bridge.js.map +1 -0
  285. package/dist/core/protected-targets.d.ts +6 -0
  286. package/dist/core/protected-targets.d.ts.map +1 -0
  287. package/dist/core/protected-targets.js +45 -0
  288. package/dist/core/protected-targets.js.map +1 -0
  289. package/dist/core/resource-loader.d.ts.map +1 -1
  290. package/dist/core/resource-loader.js +7 -0
  291. package/dist/core/resource-loader.js.map +1 -1
  292. package/dist/core/rollback.d.ts +19 -3
  293. package/dist/core/rollback.d.ts.map +1 -1
  294. package/dist/core/rollback.js +172 -56
  295. package/dist/core/rollback.js.map +1 -1
  296. package/dist/core/runtime-credentials.d.ts +3 -0
  297. package/dist/core/runtime-credentials.d.ts.map +1 -1
  298. package/dist/core/runtime-credentials.js +14 -1
  299. package/dist/core/runtime-credentials.js.map +1 -1
  300. package/dist/core/sdk.d.ts +3 -0
  301. package/dist/core/sdk.d.ts.map +1 -1
  302. package/dist/core/sdk.js +1 -0
  303. package/dist/core/sdk.js.map +1 -1
  304. package/dist/core/session-manager.d.ts +6 -0
  305. package/dist/core/session-manager.d.ts.map +1 -1
  306. package/dist/core/session-manager.js +95 -32
  307. package/dist/core/session-manager.js.map +1 -1
  308. package/dist/core/session-ownership.d.ts.map +1 -1
  309. package/dist/core/session-ownership.js +8 -3
  310. package/dist/core/session-ownership.js.map +1 -1
  311. package/dist/core/session-retention.d.ts.map +1 -1
  312. package/dist/core/session-retention.js +18 -7
  313. package/dist/core/session-retention.js.map +1 -1
  314. package/dist/core/session-startup-settings.d.ts +10 -0
  315. package/dist/core/session-startup-settings.d.ts.map +1 -0
  316. package/dist/core/session-startup-settings.js +70 -0
  317. package/dist/core/session-startup-settings.js.map +1 -0
  318. package/dist/core/subscriptions.d.ts +16 -7
  319. package/dist/core/subscriptions.d.ts.map +1 -1
  320. package/dist/core/subscriptions.js +96 -55
  321. package/dist/core/subscriptions.js.map +1 -1
  322. package/dist/core/trust-manager.d.ts +2 -1
  323. package/dist/core/trust-manager.d.ts.map +1 -1
  324. package/dist/core/trust-manager.js +6 -1
  325. package/dist/core/trust-manager.js.map +1 -1
  326. package/dist/core/update-check.d.ts.map +1 -1
  327. package/dist/core/update-check.js.map +1 -1
  328. package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
  329. package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
  330. package/dist/core/usage-adapters/kimi-coding.js +2 -2
  331. package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
  332. package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
  333. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  334. package/dist/core/usage-adapters/openai-codex.js +3 -3
  335. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  336. package/dist/core/usage-adapters/xai.d.ts +2 -2
  337. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  338. package/dist/core/usage-adapters/xai.js +3 -3
  339. package/dist/core/usage-adapters/xai.js.map +1 -1
  340. package/dist/core/usage-adapters/zai.d.ts +2 -2
  341. package/dist/core/usage-adapters/zai.d.ts.map +1 -1
  342. package/dist/core/usage-adapters/zai.js +2 -2
  343. package/dist/core/usage-adapters/zai.js.map +1 -1
  344. package/dist/core/usage-service.d.ts +9 -2
  345. package/dist/core/usage-service.d.ts.map +1 -1
  346. package/dist/core/usage-service.js +95 -50
  347. package/dist/core/usage-service.js.map +1 -1
  348. package/dist/gateway/agent-bridge.d.ts +9 -1
  349. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  350. package/dist/gateway/agent-bridge.js +91 -37
  351. package/dist/gateway/agent-bridge.js.map +1 -1
  352. package/dist/gateway/buttons.d.ts +8 -1
  353. package/dist/gateway/buttons.d.ts.map +1 -1
  354. package/dist/gateway/buttons.js +76 -18
  355. package/dist/gateway/buttons.js.map +1 -1
  356. package/dist/gateway/commands.d.ts +1 -0
  357. package/dist/gateway/commands.d.ts.map +1 -1
  358. package/dist/gateway/commands.js +10 -4
  359. package/dist/gateway/commands.js.map +1 -1
  360. package/dist/gateway/cron.d.ts +12 -11
  361. package/dist/gateway/cron.d.ts.map +1 -1
  362. package/dist/gateway/cron.js +65 -56
  363. package/dist/gateway/cron.js.map +1 -1
  364. package/dist/gateway/index.d.ts.map +1 -1
  365. package/dist/gateway/index.js +2 -1
  366. package/dist/gateway/index.js.map +1 -1
  367. package/dist/gateway/mobile-commands.d.ts +1 -0
  368. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  369. package/dist/gateway/mobile-commands.js +56 -14
  370. package/dist/gateway/mobile-commands.js.map +1 -1
  371. package/dist/gateway/presenter.d.ts +12 -2
  372. package/dist/gateway/presenter.d.ts.map +1 -1
  373. package/dist/gateway/presenter.js +73 -38
  374. package/dist/gateway/presenter.js.map +1 -1
  375. package/dist/gateway/router.d.ts +2 -2
  376. package/dist/gateway/router.d.ts.map +1 -1
  377. package/dist/gateway/router.js +17 -2
  378. package/dist/gateway/router.js.map +1 -1
  379. package/dist/gateway/service.d.ts +2 -0
  380. package/dist/gateway/service.d.ts.map +1 -1
  381. package/dist/gateway/service.js +153 -0
  382. package/dist/gateway/service.js.map +1 -1
  383. package/dist/main.d.ts.map +1 -1
  384. package/dist/main.js +86 -33
  385. package/dist/main.js.map +1 -1
  386. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/assistant-message.js +6 -4
  388. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  389. package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/copyable-text.js +3 -2
  391. package/dist/modes/interactive/components/copyable-text.js.map +1 -1
  392. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/extension-editor.js +1 -1
  394. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  395. package/dist/modes/interactive/interactive-mode.d.ts +2 -2
  396. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  397. package/dist/modes/interactive/interactive-mode.js +96 -109
  398. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  399. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  400. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  401. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  402. package/dist/modes/rpc/rpc-mode.js +2 -1
  403. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  404. package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
  405. package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
  406. package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
  407. package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-OX6KELNX.js} +8 -7
  408. package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
  409. package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
  410. package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
  411. package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
  412. package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
  413. package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
  414. package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
  415. package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
  416. package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
  417. package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
  418. package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-AYHH7YSD.js} +177 -144
  419. package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
  420. package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
  421. package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
  422. package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-C3P6Q3B2.js} +455 -278
  423. package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
  424. package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
  425. package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
  426. package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
  427. package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
  428. package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
  429. package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
  430. package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
  431. package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
  432. package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
  433. package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
  434. package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
  435. package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
  436. package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
  437. package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
  438. package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
  439. package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
  440. package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
  441. package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
  442. package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
  443. package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
  444. package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
  445. package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
  446. package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JWEVT363.js} +37 -29
  447. package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
  448. package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
  449. package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-MQPKUHRN.js} +162 -82
  450. package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
  451. package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
  452. package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
  453. package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-QNVMKNNN.js} +18 -14
  454. package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
  455. package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
  456. package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
  457. package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
  458. package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
  459. package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
  460. package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
  461. package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
  462. package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
  463. package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
  464. package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
  465. package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
  466. package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
  467. package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
  468. package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
  469. package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
  470. package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-ZDJUR45D.js} +427 -126
  471. package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
  472. package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
  473. package/dist/node-runtime/cli-runtime.js +4 -4
  474. package/dist/node-runtime/{command-FSBU2EVZ.js → command-3HMSY6W2.js} +5 -5
  475. package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
  476. package/dist/node-runtime/{commands-NAAL7KNT.js → commands-OCSSEEWJ.js} +16 -16
  477. package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
  478. package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
  479. package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
  480. package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
  481. package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.js} +4 -4
  482. package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
  483. package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
  484. package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
  485. package/dist/node-runtime/{gateway-DQM333YV.js → gateway-6623C2U3.js} +51 -42
  486. package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
  487. package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
  488. package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
  489. package/dist/node-runtime/{goal-NJEE5CTU.js → goal-AIHEDUYA.js} +5 -5
  490. package/dist/node-runtime/index.js +50 -48
  491. package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
  492. package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
  493. package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -0
  494. package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
  495. package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
  496. package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
  497. package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
  498. package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-LKVA5CZZ.js} +93 -80
  499. package/dist/node-runtime/main.js +42 -41
  500. package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-XT3NQXLD.js} +12 -12
  501. package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
  502. package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
  503. package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
  504. package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
  505. package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
  506. package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
  507. package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
  508. package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
  509. package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
  510. package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-LT3CTO4E.js} +98 -106
  511. package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
  512. package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
  513. package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
  514. package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
  515. package/dist/node-runtime/rpc-entry.js +42 -41
  516. package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
  517. package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
  518. package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
  519. package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
  520. package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
  521. package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
  522. package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
  523. package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
  524. package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
  525. package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
  526. package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
  527. package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
  528. package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-Q5F7DBVM.js} +119 -58
  529. package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
  530. package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
  531. package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
  532. package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
  533. package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
  534. package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
  535. package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
  536. package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
  537. package/dist/utils/clipboard-image.d.ts.map +1 -1
  538. package/dist/utils/clipboard-image.js.map +1 -1
  539. package/dist/utils/exif-orientation.d.ts.map +1 -1
  540. package/dist/utils/exif-orientation.js +25 -10
  541. package/dist/utils/exif-orientation.js.map +1 -1
  542. package/dist/utils/image-resize-core.d.ts.map +1 -1
  543. package/dist/utils/image-resize-core.js +2 -0
  544. package/dist/utils/image-resize-core.js.map +1 -1
  545. package/dist/utils/paths.d.ts +4 -0
  546. package/dist/utils/paths.d.ts.map +1 -1
  547. package/dist/utils/paths.js +33 -2
  548. package/dist/utils/paths.js.map +1 -1
  549. package/dist/utils/sanitize-terminal-text.d.ts +7 -0
  550. package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
  551. package/dist/utils/sanitize-terminal-text.js +67 -0
  552. package/dist/utils/sanitize-terminal-text.js.map +1 -0
  553. package/docs/browser.md +1 -1
  554. package/docs/custom-provider.md +3 -3
  555. package/docs/extensions.md +8 -4
  556. package/docs/features.md +15 -7
  557. package/docs/keybindings.md +1 -1
  558. package/docs/models.md +5 -5
  559. package/docs/packages.md +1 -1
  560. package/docs/providers.md +2 -2
  561. package/docs/rpc.md +7 -7
  562. package/docs/security.md +10 -5
  563. package/docs/sessions.md +2 -2
  564. package/docs/skills.md +1 -1
  565. package/docs/terminal-setup.md +7 -7
  566. package/docs/themes.md +1 -1
  567. package/docs/usage.md +1 -1
  568. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  569. package/examples/extensions/bash-spawn-hook.ts +1 -1
  570. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  571. package/examples/extensions/claude-rules.ts +1 -1
  572. package/examples/extensions/commands.ts +1 -1
  573. package/examples/extensions/custom-compaction.ts +1 -1
  574. package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
  575. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  576. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  577. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  578. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  579. package/examples/extensions/doom-overlay/index.ts +1 -1
  580. package/examples/extensions/git-merge-and-resolve.ts +2 -2
  581. package/examples/extensions/gondolin/index.ts +1 -1
  582. package/examples/extensions/gondolin/package-lock.json +2 -2
  583. package/examples/extensions/gondolin/package.json +1 -1
  584. package/examples/extensions/hidden-thinking-label.ts +1 -1
  585. package/examples/extensions/inline-bash.ts +2 -2
  586. package/examples/extensions/input-transform-streaming.ts +2 -2
  587. package/examples/extensions/input-transform.ts +2 -2
  588. package/examples/extensions/interactive-shell.ts +1 -1
  589. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  590. package/examples/extensions/mac-system-theme.ts +1 -1
  591. package/examples/extensions/minimal-mode.ts +1 -1
  592. package/examples/extensions/modal-editor.ts +1 -1
  593. package/examples/extensions/model-status.ts +1 -1
  594. package/examples/extensions/notify.ts +3 -3
  595. package/examples/extensions/overlay-qa-tests.ts +1 -1
  596. package/examples/extensions/overlay-test.ts +1 -1
  597. package/examples/extensions/pirate.ts +1 -1
  598. package/examples/extensions/preset.ts +3 -3
  599. package/examples/extensions/project-trust.ts +4 -4
  600. package/examples/extensions/prompt-customizer.ts +1 -1
  601. package/examples/extensions/rainbow-editor.ts +1 -1
  602. package/examples/extensions/rpc-demo.ts +1 -1
  603. package/examples/extensions/sandbox/index.ts +7 -7
  604. package/examples/extensions/sandbox/package-lock.json +2 -2
  605. package/examples/extensions/sandbox/package.json +1 -1
  606. package/examples/extensions/shutdown-command.ts +5 -5
  607. package/examples/extensions/ssh.ts +2 -2
  608. package/examples/extensions/subagent/README.md +3 -3
  609. package/examples/extensions/subagent/index.ts +2 -2
  610. package/examples/extensions/titlebar-spinner.ts +1 -1
  611. package/examples/extensions/tool-override.ts +1 -1
  612. package/examples/extensions/tools.ts +1 -1
  613. package/examples/extensions/with-deps/package-lock.json +2 -2
  614. package/examples/extensions/with-deps/package.json +1 -1
  615. package/examples/extensions/working-indicator.ts +1 -1
  616. package/examples/extensions/working-message-test.ts +1 -1
  617. package/examples/sdk/01-minimal.ts +1 -1
  618. package/examples/sdk/03-custom-prompt.ts +1 -1
  619. package/examples/sdk/04-skills.ts +1 -1
  620. package/examples/sdk/06-extensions.ts +2 -2
  621. package/examples/sdk/08-prompt-templates.ts +1 -1
  622. package/install-lock/package-lock.json +24 -24
  623. package/install-lock/package.json +2 -2
  624. package/npm-shrinkwrap.json +60 -24
  625. package/package.json +9 -9
  626. package/dist/modes/interactive/components/exit-card.d.ts +0 -22
  627. package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
  628. package/dist/modes/interactive/components/exit-card.js +0 -69
  629. package/dist/modes/interactive/components/exit-card.js.map +0 -1
  630. package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
  631. package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
  632. package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
  633. package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
  634. package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
  635. package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
  636. package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
  637. package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
  638. package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
  639. package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
@@ -10,18 +10,23 @@
10
10
  * Raw keys are never logged or embedded in error messages — errors refer to
11
11
  * provider and key ids only.
12
12
  */
13
+ import { createHash } from "node:crypto";
13
14
  import { join } from "path";
14
15
  import { getAgentDir } from "../config.js";
15
16
  import { FileAuthStorageBackend, InMemoryAuthStorageBackend } from "./auth-storage.js";
16
17
  import { parseResetTimeMs } from "./usage-limit.js";
18
+ import { clearPlanUsageCache } from "./usage-service.js";
19
+ /** Request identities are non-secret digests kept outside messages and storage. */
20
+ export function subscriptionKeyFingerprint(providerId, key) {
21
+ return createHash("sha256").update(providerId).update("\0").update(key).digest("hex");
22
+ }
17
23
  export class SubscriptionManager {
18
24
  data = {};
19
- loaded = false;
20
25
  storage;
21
26
  authStorage;
22
27
  // In-process write queue so mutations serialize even before the file lock.
23
28
  queue = Promise.resolve();
24
- // Per-provider ids of keys exhausted without a parseable reset time.
29
+ // Per-provider fingerprints exhausted without a parseable reset time.
25
30
  // Process-local on purpose: infinite exhaustion is never persisted.
26
31
  memoryExhausted = new Map();
27
32
  constructor(authStorage, storage) {
@@ -40,38 +45,61 @@ export class SubscriptionManager {
40
45
  return SubscriptionManager.fromStorage(authStorage, storage);
41
46
  }
42
47
  parseStorageData(content) {
43
- if (!content) {
48
+ if (content === undefined)
44
49
  return {};
45
- }
46
- return JSON.parse(content);
47
- }
48
- ensureLoaded() {
49
- if (this.loaded)
50
- return;
51
- this.loaded = true;
52
- let content;
53
50
  try {
54
- this.storage.withLock((current) => {
55
- content = current;
56
- return { result: undefined };
57
- });
58
- this.data = this.parseStorageData(content);
51
+ const parsed = JSON.parse(content);
52
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
53
+ throw new Error();
54
+ for (const value of Object.values(parsed)) {
55
+ if (!value || typeof value !== "object")
56
+ throw new Error();
57
+ const pool = value;
58
+ if (typeof pool.active !== "string" || !Array.isArray(pool.keys))
59
+ throw new Error();
60
+ const ids = new Set();
61
+ for (const value of pool.keys) {
62
+ if (!value || typeof value !== "object")
63
+ throw new Error();
64
+ const entry = value;
65
+ if (typeof entry.id !== "string" ||
66
+ ids.has(entry.id) ||
67
+ typeof entry.name !== "string" ||
68
+ typeof entry.key !== "string" ||
69
+ typeof entry.addedAt !== "number" ||
70
+ !Number.isFinite(entry.addedAt) ||
71
+ (entry.exhaustedUntil !== undefined &&
72
+ (typeof entry.exhaustedUntil !== "number" || !Number.isFinite(entry.exhaustedUntil))) ||
73
+ (entry.lastError !== undefined && typeof entry.lastError !== "string"))
74
+ throw new Error();
75
+ ids.add(entry.id);
76
+ }
77
+ if (!ids.has(pool.active))
78
+ throw new Error();
79
+ }
80
+ return parsed;
59
81
  }
60
82
  catch {
61
- // Preserve the empty snapshot; a malformed file is never overwritten by a load.
83
+ throw new Error("Subscription storage is malformed. Repair it before changing subscriptions.");
62
84
  }
63
85
  }
86
+ /**
87
+ * Lock order is subscriptions then auth. Keep selection and its auth mirror
88
+ * under the subscription lock so later selectors cannot overtake a mirror.
89
+ * These files are not an atomic commit: a failed mirror aborts the pool write;
90
+ * a later pool write failure can leave auth ahead and a repeated selection repairs it.
91
+ */
64
92
  enqueue(fn) {
65
- const next = this.queue.then(fn);
93
+ const next = this.queue.then(() => this.storage.withLockAsync(async (current) => {
94
+ this.data = this.parseStorageData(current);
95
+ const before = JSON.stringify(this.data, null, 2);
96
+ const result = await fn();
97
+ const after = JSON.stringify(this.data, null, 2);
98
+ return { result, next: before === after ? undefined : after };
99
+ }));
66
100
  this.queue = next.catch(() => { });
67
101
  return next;
68
102
  }
69
- persist() {
70
- return this.storage.withLockAsync(async () => ({
71
- result: undefined,
72
- next: JSON.stringify(this.data, null, 2),
73
- }));
74
- }
75
103
  /** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */
76
104
  async readRawCredential(providerId) {
77
105
  let current;
@@ -86,7 +114,6 @@ export class SubscriptionManager {
86
114
  * as "Sub 1" on first access. OAuth credentials are not imported.
87
115
  */
88
116
  async ensurePool(providerId) {
89
- this.ensureLoaded();
90
117
  const existing = this.data[providerId];
91
118
  if (existing)
92
119
  return existing;
@@ -98,7 +125,6 @@ export class SubscriptionManager {
98
125
  keys: [{ id: "1", name: "Sub 1", key: credential.key, addedAt: Date.now() }],
99
126
  };
100
127
  this.data = { ...this.data, [providerId]: pool };
101
- await this.persist();
102
128
  return pool;
103
129
  }
104
130
  nextId(pool) {
@@ -111,7 +137,17 @@ export class SubscriptionManager {
111
137
  return String(max + 1);
112
138
  }
113
139
  async mirrorActive(providerId, key) {
114
- await this.authStorage.modify(providerId, async () => ({ type: "api_key", key }));
140
+ clearPlanUsageCache();
141
+ try {
142
+ await this.authStorage.modify(providerId, async (current) => ({
143
+ type: "api_key",
144
+ key,
145
+ ...(current?.type === "api_key" && current.env ? { env: current.env } : {}),
146
+ }));
147
+ }
148
+ finally {
149
+ clearPlanUsageCache();
150
+ }
115
151
  }
116
152
  async list(providerId) {
117
153
  return this.enqueue(async () => {
@@ -127,13 +163,11 @@ export class SubscriptionManager {
127
163
  }
128
164
  async addKey(providerId, key, name) {
129
165
  return this.enqueue(async () => {
130
- this.ensureLoaded();
131
166
  const pool = await this.ensurePool(providerId);
132
167
  const id = pool ? this.nextId(pool) : "1";
133
168
  const entry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };
134
169
  const keys = [...(pool?.keys ?? []), entry];
135
170
  this.data = { ...this.data, [providerId]: { active: id, keys } };
136
- await this.persist();
137
171
  await this.mirrorActive(providerId, key);
138
172
  return entry;
139
173
  });
@@ -144,27 +178,33 @@ export class SubscriptionManager {
144
178
  if (!pool || !pool.keys.some((entry) => entry.id === id))
145
179
  return;
146
180
  const keys = pool.keys.filter((entry) => entry.id !== id);
147
- this.memoryExhausted.get(providerId)?.delete(id);
181
+ const removed = pool.keys.find((entry) => entry.id === id);
182
+ if (!keys.some((entry) => entry.key === removed.key)) {
183
+ this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));
184
+ }
148
185
  if (keys.length === 0) {
149
186
  const data = { ...this.data };
150
187
  delete data[providerId];
151
188
  this.data = data;
152
- await this.persist();
153
- await this.authStorage.delete(providerId);
189
+ clearPlanUsageCache();
190
+ try {
191
+ await this.authStorage.delete(providerId);
192
+ }
193
+ finally {
194
+ clearPlanUsageCache();
195
+ }
154
196
  return;
155
197
  }
156
198
  let active = pool.active;
157
199
  if (active === id) {
158
200
  active = keys[0]?.id ?? active;
159
201
  this.data = { ...this.data, [providerId]: { active, keys } };
160
- await this.persist();
161
202
  const promoted = keys.find((entry) => entry.id === active);
162
203
  if (promoted)
163
204
  await this.mirrorActive(providerId, promoted.key);
164
205
  return;
165
206
  }
166
207
  this.data = { ...this.data, [providerId]: { active, keys } };
167
- await this.persist();
168
208
  });
169
209
  }
170
210
  async renameKey(providerId, id, name) {
@@ -174,7 +214,6 @@ export class SubscriptionManager {
174
214
  return;
175
215
  const keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));
176
216
  this.data = { ...this.data, [providerId]: { ...pool, keys } };
177
- await this.persist();
178
217
  });
179
218
  }
180
219
  async setActive(providerId, id) {
@@ -184,65 +223,68 @@ export class SubscriptionManager {
184
223
  if (!pool || !entry)
185
224
  return;
186
225
  this.data = { ...this.data, [providerId]: { ...pool, active: id } };
187
- await this.persist();
188
226
  await this.mirrorActive(providerId, entry.key);
189
227
  });
190
228
  }
191
229
  /**
192
- * Mark the active key exhausted and rotate to the next non-exhausted key in
230
+ * Mark the request's key exhausted and rotate to the next non-exhausted key in
193
231
  * round-robin order. A parseable reset time is persisted as exhaustedUntil;
194
- * otherwise the exhaustion is process-local only. Returns the new active key,
195
- * or null when no non-exhausted alternative exists (current key stays active).
232
+ * otherwise the exhaustion is process-local only. A later selection is preserved.
233
+ * Returns the usable active key for retry, or null when no alternative exists.
196
234
  */
197
- async rotateOnFailure(providerId, errorMessage, now = Date.now()) {
235
+ async rotateOnFailure(providerId, errorMessage, requestFingerprint, now = Date.now()) {
236
+ if (!requestFingerprint)
237
+ return null;
198
238
  return this.enqueue(async () => {
199
- const pool = await this.ensurePool(providerId);
200
- if (!pool)
239
+ const pool = this.data[providerId];
240
+ if (!pool || pool.keys.length < 2)
201
241
  return null;
202
242
  const currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);
203
243
  if (currentIndex === -1)
204
244
  return null;
205
245
  const current = pool.keys[currentIndex];
206
- if (!current)
246
+ const matchesRequest = (entry) => subscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;
247
+ if (!current || !pool.keys.some(matchesRequest))
207
248
  return null;
208
249
  const resetAt = parseResetTimeMs(errorMessage);
209
250
  let keys = pool.keys;
210
251
  if (resetAt !== undefined && resetAt > now) {
211
- keys = pool.keys.map((entry) => entry.id === current.id ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry);
252
+ keys = pool.keys.map((entry) => matchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry);
212
253
  }
213
254
  else {
214
255
  const set = this.memoryExhausted.get(providerId) ?? new Set();
215
- set.add(current.id);
256
+ set.add(requestFingerprint);
216
257
  this.memoryExhausted.set(providerId, set);
217
258
  }
218
- const isExhausted = (entry) => this.memoryExhausted.get(providerId)?.has(entry.id) === true ||
259
+ const isExhausted = (entry) => this.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||
219
260
  (entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);
261
+ if (keys !== pool.keys)
262
+ this.data = { ...this.data, [providerId]: { ...pool, keys } };
263
+ // Another owner already selected a different credential. Retry it without
264
+ // replacing its selection or mirroring a stale failure over its auth.
265
+ if (!matchesRequest(current))
266
+ return isExhausted(current) ? null : current;
220
267
  for (let offset = 1; offset < keys.length; offset++) {
221
268
  const candidate = keys[(currentIndex + offset) % keys.length];
222
269
  if (!candidate || isExhausted(candidate))
223
270
  continue;
224
271
  this.data = { ...this.data, [providerId]: { active: candidate.id, keys } };
225
- await this.persist();
226
272
  await this.mirrorActive(providerId, candidate.key);
227
273
  return candidate;
228
274
  }
229
- // No alternative: keep the (now-exhausted) current key active, but still
230
- // persist its exhaustion state when a reset time was recorded.
231
- if (keys !== pool.keys) {
232
- this.data = { ...this.data, [providerId]: { ...pool, keys } };
233
- await this.persist();
234
- }
275
+ // No alternative: keep the now-exhausted current key active.
235
276
  return null;
236
277
  });
237
278
  }
238
279
  /** Manual reactivate: clears persisted and process-local exhaustion for a key. */
239
280
  async clearExhaustion(providerId, id) {
240
281
  return this.enqueue(async () => {
241
- this.memoryExhausted.get(providerId)?.delete(id);
242
282
  const pool = await this.ensurePool(providerId);
243
283
  if (!pool)
244
284
  return;
245
285
  const target = pool.keys.find((entry) => entry.id === id);
286
+ if (target)
287
+ this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));
246
288
  if (!target || (target.exhaustedUntil === undefined && target.lastError === undefined))
247
289
  return;
248
290
  const keys = pool.keys.map((entry) => {
@@ -252,7 +294,6 @@ export class SubscriptionManager {
252
294
  return rest;
253
295
  });
254
296
  this.data = { ...this.data, [providerId]: { ...pool, keys } };
255
- await this.persist();
256
297
  });
257
298
  }
258
299
  }
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAA2B,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAkBpD,MAAM,OAAO,mBAAmB;IACvB,IAAI,GAAqB,EAAE,CAAC;IAC5B,MAAM,GAAG,KAAK,CAAC;IACf,OAAO,CAAqB;IAC5B,WAAW,CAAkB;IACrC,2EAA2E;IACnE,KAAK,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IACpD,qEAAqE;IACrE,oEAAoE;IAC5D,eAAe,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEzD,YAAoB,WAA4B,EAAE,OAA2B,EAAE;QAC9E,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;IAED,MAAM,CAAC,MAAM,CAAC,WAA4B,EAAE,IAAa,EAAuB;QAC/E,OAAO,IAAI,mBAAmB,CAC7B,WAAW,EACX,IAAI,sBAAsB,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAC7E,CAAC;IAAA,CACF;IAED,MAAM,CAAC,WAAW,CAAC,WAA4B,EAAE,OAA2B,EAAuB;QAClG,OAAO,IAAI,mBAAmB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAAA,CACrD;IAED,MAAM,CAAC,QAAQ,CAAC,WAA4B,EAAE,IAAI,GAAqB,EAAE,EAAuB;QAC/F,MAAM,OAAO,GAAG,IAAI,0BAA0B,EAAE,CAAC;QACjD,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrF,OAAO,mBAAmB,CAAC,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAAA,CAC7D;IAEO,gBAAgB,CAAC,OAA2B,EAAoB;QACvE,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,EAAE,CAAC;QACX,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqB,CAAC;IAAA,CAC/C;IAEO,YAAY,GAAS;QAC5B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,OAA2B,CAAC;QAChC,IAAI,CAAC;YACJ,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;gBAClC,OAAO,GAAG,OAAO,CAAC;gBAClB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAAA,CAC7B,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACR,gFAAgF;QACjF,CAAC;IAAA,CACD;IAEO,OAAO,CAAI,EAAoB,EAAc;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IAAA,CACZ;IAEO,OAAO,GAAkB;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,EAAE,SAAS;YACjB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;SACxC,CAAC,CAAC,CAAC;IAAA,CACJ;IAED,gGAAgG;IACxF,KAAK,CAAC,iBAAiB,CAAC,UAAkB,EAAmC;QACpF,IAAI,OAA+B,CAAC;QACpC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC;YAC/D,OAAO,GAAG,UAAU,CAAC;YACrB,OAAO,SAAS,CAAC;QAAA,CACjB,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IAAA,CACf;IAED;;;OAGG;IACK,KAAK,CAAC,UAAU,CAAC,UAAkB,EAAqC;QAC/E,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,UAAU,EAAE,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAErF,MAAM,IAAI,GAAiB;YAC1B,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;SAC5E,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,CAAC;QACjD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IAAA,CACZ;IAEO,MAAM,CAAC,IAAkB,EAAU;QAC1C,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,GAAG;gBAAE,GAAG,GAAG,OAAO,CAAC;QAC9D,CAAC;QACD,OAAO,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAAA,CACvB;IAEO,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,GAAW,EAAiB;QAC1E,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAAA,CAClF;IAED,KAAK,CAAC,IAAI,CAAC,UAAkB,EAAuB;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAAA,CAClC,CAAC,CAAC;IAAA,CACH;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAiC;QAClE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,OAAO,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;QAAA,CAC5D,CAAC,CAAC;IAAA,CACH;IAED,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,GAAW,EAAE,IAAa,EAAqB;QAC/E,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC1C,MAAM,KAAK,GAAa,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACpF,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YACjE,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YACzC,OAAO,KAAK,CAAC;QAAA,CACb,CAAC,CAAC;IAAA,CACH;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,EAAU,EAAiB;QAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;gBAAE,OAAO;YAEjE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAEjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC;gBACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBAC1C,OAAO;YACR,CAAC;YAED,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YACzB,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;gBACnB,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,MAAM,CAAC;gBAC/B,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC7D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;gBAC3D,IAAI,QAAQ;oBAAE,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAChE,OAAO;YACR,CAAC;YAED,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;YAC7D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,CACrB,CAAC,CAAC;IAAA,CACH;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,EAAU,EAAE,IAAY,EAAiB;QAC5E,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACtF,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,CACrB,CAAC,CAAC;IAAA,CACH;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,EAAU,EAAiB;QAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,KAAK,GAAG,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAClE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO;YAC5B,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;YACpE,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAAA,CAC/C,CAAC,CAAC;IAAA,CACH;IAED;;;;;OAKG;IACH,KAAK,CAAC,eAAe,CAAC,UAAkB,EAAE,YAAoB,EAAE,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE,EAA4B;QACnH,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9E,IAAI,YAAY,KAAK,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YAE1B,MAAM,OAAO,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;YAC/C,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACrB,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;gBAC5C,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9B,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,CAChG,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;gBACtE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACpB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC3C,CAAC;YAED,MAAM,WAAW,GAAG,CAAC,KAAe,EAAW,EAAE,CAChD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,IAAI;gBAC5D,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS,IAAI,KAAK,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC;YAEpE,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;gBACrD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,YAAY,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9D,IAAI,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,CAAC;oBAAE,SAAS;gBACnD,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC3E,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnD,OAAO,SAAS,CAAC;YAClB,CAAC;YAED,yEAAyE;YACzE,+DAA+D;YAC/D,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC9D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,IAAI,CAAC;QAAA,CACZ,CAAC,CAAC;IAAA,CACH;IAED,kFAAkF;IAClF,KAAK,CAAC,eAAe,CAAC,UAAkB,EAAE,EAAU,EAAiB;QACpE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC;gBAAE,OAAO;YAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBACrC,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE;oBAAE,OAAO,KAAK,CAAC;gBAClC,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;gBAClF,OAAO,IAAI,CAAC;YAAA,CACZ,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,CACrB,CAAC,CAAC;IAAA,CACH;CACD","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate loaded = false;\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider ids of keys exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as SubscriptionData;\n\t}\n\n\tprivate ensureLoaded(): void {\n\t\tif (this.loaded) return;\n\t\tthis.loaded = true;\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t} catch {\n\t\t\t// Preserve the empty snapshot; a malformed file is never overwritten by a load.\n\t\t}\n\t}\n\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(fn);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\tprivate persist(): Promise<void> {\n\t\treturn this.storage.withLockAsync(async () => ({\n\t\t\tresult: undefined,\n\t\t\tnext: JSON.stringify(this.data, null, 2),\n\t\t}));\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tthis.ensureLoaded();\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\tawait this.persist();\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tawait this.authStorage.modify(providerId, async () => ({ type: \"api_key\", key }));\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.ensureLoaded();\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the active key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. Returns the new active key,\n\t * or null when no non-exhausted alternative exists (current key stays active).\n\t */\n\tasync rotateOnFailure(providerId: string, errorMessage: string, now: number = Date.now()): Promise<SubEntry | null> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tif (!current) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tentry.id === current.id ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(current.id);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(entry.id) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the (now-exhausted) current key active, but still\n\t\t\t// persist its exhaustion state when a reset time was recorded.\n\t\t\tif (keys !== pool.keys) {\n\t\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\nimport { clearPlanUsageCache } from \"./usage-service.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport interface RequestSubscriptionKey {\n\tproviderId: string;\n\tfingerprint: string;\n}\n\n/** Request identities are non-secret digests kept outside messages and storage. */\nexport function subscriptionKeyFingerprint(providerId: string, key: string): string {\n\treturn createHash(\"sha256\").update(providerId).update(\"\\0\").update(key).digest(\"hex\");\n}\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider fingerprints exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (content === undefined) return {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(content);\n\t\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error();\n\t\t\tfor (const value of Object.values(parsed as Record<string, unknown>)) {\n\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\tconst pool = value as Record<string, unknown>;\n\t\t\t\tif (typeof pool.active !== \"string\" || !Array.isArray(pool.keys)) throw new Error();\n\t\t\t\tconst ids = new Set<string>();\n\t\t\t\tfor (const value of pool.keys as unknown[]) {\n\t\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\t\tconst entry = value as Record<string, unknown>;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof entry.id !== \"string\" ||\n\t\t\t\t\t\tids.has(entry.id) ||\n\t\t\t\t\t\ttypeof entry.name !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.key !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.addedAt !== \"number\" ||\n\t\t\t\t\t\t!Number.isFinite(entry.addedAt) ||\n\t\t\t\t\t\t(entry.exhaustedUntil !== undefined &&\n\t\t\t\t\t\t\t(typeof entry.exhaustedUntil !== \"number\" || !Number.isFinite(entry.exhaustedUntil))) ||\n\t\t\t\t\t\t(entry.lastError !== undefined && typeof entry.lastError !== \"string\")\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error();\n\t\t\t\t\tids.add(entry.id);\n\t\t\t\t}\n\t\t\t\tif (!ids.has(pool.active)) throw new Error();\n\t\t\t}\n\t\t\treturn parsed as SubscriptionData;\n\t\t} catch {\n\t\t\tthrow new Error(\"Subscription storage is malformed. Repair it before changing subscriptions.\");\n\t\t}\n\t}\n\n\t/**\n\t * Lock order is subscriptions then auth. Keep selection and its auth mirror\n\t * under the subscription lock so later selectors cannot overtake a mirror.\n\t * These files are not an atomic commit: a failed mirror aborts the pool write;\n\t * a later pool write failure can leave auth ahead and a repeated selection repairs it.\n\t */\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(() =>\n\t\t\tthis.storage.withLockAsync(async (current) => {\n\t\t\t\tthis.data = this.parseStorageData(current);\n\t\t\t\tconst before = JSON.stringify(this.data, null, 2);\n\t\t\t\tconst result = await fn();\n\t\t\t\tconst after = JSON.stringify(this.data, null, 2);\n\t\t\t\treturn { result, next: before === after ? undefined : after };\n\t\t\t}),\n\t\t);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tclearPlanUsageCache();\n\t\ttry {\n\t\t\tawait this.authStorage.modify(providerId, async (current) => ({\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey,\n\t\t\t\t...(current?.type === \"api_key\" && current.env ? { env: current.env } : {}),\n\t\t\t}));\n\t\t} finally {\n\t\t\tclearPlanUsageCache();\n\t\t}\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tconst removed = pool.keys.find((entry) => entry.id === id)!;\n\t\t\tif (!keys.some((entry) => entry.key === removed.key)) {\n\t\t\t\tthis.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));\n\t\t\t}\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tclearPlanUsageCache();\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\t} finally {\n\t\t\t\t\tclearPlanUsageCache();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the request's key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. A later selection is preserved.\n\t * Returns the usable active key for retry, or null when no alternative exists.\n\t */\n\tasync rotateOnFailure(\n\t\tproviderId: string,\n\t\terrorMessage: string,\n\t\trequestFingerprint: string | undefined,\n\t\tnow: number = Date.now(),\n\t): Promise<SubEntry | null> {\n\t\tif (!requestFingerprint) return null;\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = this.data[providerId];\n\t\t\tif (!pool || pool.keys.length < 2) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tconst matchesRequest = (entry: SubEntry): boolean =>\n\t\t\t\tsubscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;\n\t\t\tif (!current || !pool.keys.some(matchesRequest)) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tmatchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(requestFingerprint);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tif (keys !== pool.keys) this.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t// Another owner already selected a different credential. Retry it without\n\t\t\t// replacing its selection or mirroring a stale failure over its auth.\n\t\t\tif (!matchesRequest(current)) return isExhausted(current) ? null : current;\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the now-exhausted current key active.\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (target) this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n}\n"]}
@@ -19,7 +19,8 @@ export declare function getProjectTrustOptions(cwd: string, options?: {
19
19
  }): ProjectTrustOption[];
20
20
  /**
21
21
  * Returns true when cwd has project-local resources that must be gated by
22
- * project trust: trust-requiring entries under cwd/.pi, or .agents/skills in
22
+ * project trust: settings/resources under cwd/.lunr, executable integration
23
+ * configuration, or .agents/skills in
23
24
  * cwd or one of its ancestors. Returns false when no such project resources
24
25
  * exist. The user/global ~/.agents/skills directory is always treated as a
25
26
  * trusted user resource and is ignored here, even when cwd is $HOME.
@@ -1 +1 @@
1
- {"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAkCD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,EAAE,CA8BpH;AAkFD;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAsBtE;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAE3B;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAErC;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAKnD;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAErD;IAED,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAa7C;CACD","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n\tpath: string;\n\tdecision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n\tpath: string;\n\tdecision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n\tlabel: string;\n\ttrusted: boolean;\n\tupdates: ProjectTrustUpdate[];\n\tsavedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [\n\t\"settings.json\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n] as const;\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n\tlet currentDir = normalizeCwd(cwd);\n\twhile (true) {\n\t\tconst value = data[currentDir];\n\t\tif (value === true || value === false) {\n\t\t\treturn { path: currentDir, decision: value };\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst parentDir = dirname(trustPath);\n\treturn parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(cwd: string, options?: { includeSessionOnly?: boolean }): ProjectTrustOption[] {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst trustOptions: ProjectTrustOption[] = [\n\t\t{ label: \"Trust\", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n\t];\n\tconst parentPath = getProjectTrustParentPath(cwd);\n\tif (parentPath !== undefined) {\n\t\ttrustOptions.push({\n\t\t\tlabel: `Trust parent folder (${parentPath})`,\n\t\t\ttrusted: true,\n\t\t\tupdates: [\n\t\t\t\t{ path: parentPath, decision: true },\n\t\t\t\t{ path: trustPath, decision: null },\n\t\t\t],\n\t\t\tsavedPath: parentPath,\n\t\t});\n\t}\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Trust (this session only)\", trusted: true, updates: [] });\n\t}\n\ttrustOptions.push({\n\t\tlabel: \"Do not trust\",\n\t\ttrusted: false,\n\t\tupdates: [{ path: trustPath, decision: false }],\n\t\tsavedPath: trustPath,\n\t});\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Do not trust (this session only)\", trusted: false, updates: [] });\n\t}\n\treturn trustOptions;\n}\n\nfunction readTrustFile(path: string): TrustFile {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to read trust store ${path}: ${message}`);\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Returns true when cwd has project-local resources that must be gated by\n * project trust: trust-requiring entries under cwd/.pi, or .agents/skills in\n * cwd or one of its ancestors. Returns false when no such project resources\n * exist. The user/global ~/.agents/skills directory is always treated as a\n * trusted user resource and is ignored here, even when cwd is $HOME.\n */\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n\tconst homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));\n\tconst userAgentsSkillsDir = join(homeDir, \".agents\", \"skills\");\n\tlet currentDir = canonicalizePath(resolvePath(cwd));\n\n\tconst configDir = join(currentDir, CONFIG_DIR_NAME);\n\tif (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tconst agentsSkillsDir = join(currentDir, \".agents\", \"skills\");\n\t\tif (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn this.getEntry(cwd)?.decision ?? null;\n\t}\n\n\tgetEntry(cwd: string): ProjectTrustStoreEntry | null {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\treturn findNearestTrustEntry(data, cwd);\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tthis.setMany([{ path: cwd, decision }]);\n\t}\n\n\tsetMany(decisions: ProjectTrustUpdate[]): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tfor (const { path, decision } of decisions) {\n\t\t\t\tconst key = normalizeCwd(path);\n\t\t\t\tif (decision === null) {\n\t\t\t\t\tdelete data[key];\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = decision;\n\t\t\t\t}\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAmCD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,EAAE,CA8BpH;AAkFD;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA6BtE;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAE3B;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAErC;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAKnD;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAErD;IAED,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAa7C;CACD","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n\tpath: string;\n\tdecision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n\tpath: string;\n\tdecision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n\tlabel: string;\n\ttrusted: boolean;\n\tupdates: ProjectTrustUpdate[];\n\tsavedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [\n\t\"settings.json\",\n\t\"mcp.json\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n] as const;\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n\tlet currentDir = normalizeCwd(cwd);\n\twhile (true) {\n\t\tconst value = data[currentDir];\n\t\tif (value === true || value === false) {\n\t\t\treturn { path: currentDir, decision: value };\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst parentDir = dirname(trustPath);\n\treturn parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(cwd: string, options?: { includeSessionOnly?: boolean }): ProjectTrustOption[] {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst trustOptions: ProjectTrustOption[] = [\n\t\t{ label: \"Trust\", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n\t];\n\tconst parentPath = getProjectTrustParentPath(cwd);\n\tif (parentPath !== undefined) {\n\t\ttrustOptions.push({\n\t\t\tlabel: `Trust parent folder (${parentPath})`,\n\t\t\ttrusted: true,\n\t\t\tupdates: [\n\t\t\t\t{ path: parentPath, decision: true },\n\t\t\t\t{ path: trustPath, decision: null },\n\t\t\t],\n\t\t\tsavedPath: parentPath,\n\t\t});\n\t}\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Trust (this session only)\", trusted: true, updates: [] });\n\t}\n\ttrustOptions.push({\n\t\tlabel: \"Do not trust\",\n\t\ttrusted: false,\n\t\tupdates: [{ path: trustPath, decision: false }],\n\t\tsavedPath: trustPath,\n\t});\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Do not trust (this session only)\", trusted: false, updates: [] });\n\t}\n\treturn trustOptions;\n}\n\nfunction readTrustFile(path: string): TrustFile {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to read trust store ${path}: ${message}`);\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Returns true when cwd has project-local resources that must be gated by\n * project trust: settings/resources under cwd/.lunr, executable integration\n * configuration, or .agents/skills in\n * cwd or one of its ancestors. Returns false when no such project resources\n * exist. The user/global ~/.agents/skills directory is always treated as a\n * trusted user resource and is ignored here, even when cwd is $HOME.\n */\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n\tconst homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));\n\tconst userAgentsSkillsDir = join(homeDir, \".agents\", \"skills\");\n\tlet currentDir = canonicalizePath(resolvePath(cwd));\n\n\tconst configDir = join(currentDir, CONFIG_DIR_NAME);\n\tif (\n\t\t[\".mcp.json\", \".pi/mcp.json\", \".vscode/mcp.json\", \".pi-lsp.json\"].some((entry) =>\n\t\t\texistsSync(join(currentDir, entry)),\n\t\t)\n\t) {\n\t\treturn true;\n\t}\n\tif (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tconst agentsSkillsDir = join(currentDir, \".agents\", \"skills\");\n\t\tif (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn this.getEntry(cwd)?.decision ?? null;\n\t}\n\n\tgetEntry(cwd: string): ProjectTrustStoreEntry | null {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\treturn findNearestTrustEntry(data, cwd);\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tthis.setMany([{ path: cwd, decision }]);\n\t}\n\n\tsetMany(decisions: ProjectTrustUpdate[]): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tfor (const { path, decision } of decisions) {\n\t\t\t\tconst key = normalizeCwd(path);\n\t\t\t\tif (decision === null) {\n\t\t\t\t\tdelete data[key];\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = decision;\n\t\t\t\t}\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
@@ -6,6 +6,7 @@ import { CONFIG_DIR_NAME } from "../config.js";
6
6
  import { canonicalizePath, resolvePath } from "../utils/paths.js";
7
7
  const TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [
8
8
  "settings.json",
9
+ "mcp.json",
9
10
  "extensions",
10
11
  "skills",
11
12
  "prompts",
@@ -141,7 +142,8 @@ function withTrustFileLock(path, fn) {
141
142
  }
142
143
  /**
143
144
  * Returns true when cwd has project-local resources that must be gated by
144
- * project trust: trust-requiring entries under cwd/.pi, or .agents/skills in
145
+ * project trust: settings/resources under cwd/.lunr, executable integration
146
+ * configuration, or .agents/skills in
145
147
  * cwd or one of its ancestors. Returns false when no such project resources
146
148
  * exist. The user/global ~/.agents/skills directory is always treated as a
147
149
  * trusted user resource and is ignored here, even when cwd is $HOME.
@@ -151,6 +153,9 @@ export function hasTrustRequiringProjectResources(cwd) {
151
153
  const userAgentsSkillsDir = join(homeDir, ".agents", "skills");
152
154
  let currentDir = canonicalizePath(resolvePath(cwd));
153
155
  const configDir = join(currentDir, CONFIG_DIR_NAME);
156
+ if ([".mcp.json", ".pi/mcp.json", ".vscode/mcp.json", ".pi-lsp.json"].some((entry) => existsSync(join(currentDir, entry)))) {
157
+ return true;
158
+ }
154
159
  if (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {
155
160
  return true;
156
161
  }
@@ -1 +1 @@
1
- {"version":3,"file":"trust-manager.js","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n\tpath: string;\n\tdecision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n\tpath: string;\n\tdecision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n\tlabel: string;\n\ttrusted: boolean;\n\tupdates: ProjectTrustUpdate[];\n\tsavedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [\n\t\"settings.json\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n] as const;\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n\tlet currentDir = normalizeCwd(cwd);\n\twhile (true) {\n\t\tconst value = data[currentDir];\n\t\tif (value === true || value === false) {\n\t\t\treturn { path: currentDir, decision: value };\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst parentDir = dirname(trustPath);\n\treturn parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(cwd: string, options?: { includeSessionOnly?: boolean }): ProjectTrustOption[] {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst trustOptions: ProjectTrustOption[] = [\n\t\t{ label: \"Trust\", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n\t];\n\tconst parentPath = getProjectTrustParentPath(cwd);\n\tif (parentPath !== undefined) {\n\t\ttrustOptions.push({\n\t\t\tlabel: `Trust parent folder (${parentPath})`,\n\t\t\ttrusted: true,\n\t\t\tupdates: [\n\t\t\t\t{ path: parentPath, decision: true },\n\t\t\t\t{ path: trustPath, decision: null },\n\t\t\t],\n\t\t\tsavedPath: parentPath,\n\t\t});\n\t}\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Trust (this session only)\", trusted: true, updates: [] });\n\t}\n\ttrustOptions.push({\n\t\tlabel: \"Do not trust\",\n\t\ttrusted: false,\n\t\tupdates: [{ path: trustPath, decision: false }],\n\t\tsavedPath: trustPath,\n\t});\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Do not trust (this session only)\", trusted: false, updates: [] });\n\t}\n\treturn trustOptions;\n}\n\nfunction readTrustFile(path: string): TrustFile {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to read trust store ${path}: ${message}`);\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Returns true when cwd has project-local resources that must be gated by\n * project trust: trust-requiring entries under cwd/.pi, or .agents/skills in\n * cwd or one of its ancestors. Returns false when no such project resources\n * exist. The user/global ~/.agents/skills directory is always treated as a\n * trusted user resource and is ignored here, even when cwd is $HOME.\n */\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n\tconst homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));\n\tconst userAgentsSkillsDir = join(homeDir, \".agents\", \"skills\");\n\tlet currentDir = canonicalizePath(resolvePath(cwd));\n\n\tconst configDir = join(currentDir, CONFIG_DIR_NAME);\n\tif (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tconst agentsSkillsDir = join(currentDir, \".agents\", \"skills\");\n\t\tif (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn this.getEntry(cwd)?.decision ?? null;\n\t}\n\n\tgetEntry(cwd: string): ProjectTrustStoreEntry | null {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\treturn findNearestTrustEntry(data, cwd);\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tthis.setMany([{ path: cwd, decision }]);\n\t}\n\n\tsetMany(decisions: ProjectTrustUpdate[]): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tfor (const { path, decision } of decisions) {\n\t\t\t\tconst key = normalizeCwd(path);\n\t\t\t\tif (decision === null) {\n\t\t\t\t\tdelete data[key];\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = decision;\n\t\t\t\t}\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"trust-manager.js","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n\tpath: string;\n\tdecision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n\tpath: string;\n\tdecision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n\tlabel: string;\n\ttrusted: boolean;\n\tupdates: ProjectTrustUpdate[];\n\tsavedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [\n\t\"settings.json\",\n\t\"mcp.json\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n] as const;\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n\tlet currentDir = normalizeCwd(cwd);\n\twhile (true) {\n\t\tconst value = data[currentDir];\n\t\tif (value === true || value === false) {\n\t\t\treturn { path: currentDir, decision: value };\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst parentDir = dirname(trustPath);\n\treturn parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(cwd: string, options?: { includeSessionOnly?: boolean }): ProjectTrustOption[] {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst trustOptions: ProjectTrustOption[] = [\n\t\t{ label: \"Trust\", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n\t];\n\tconst parentPath = getProjectTrustParentPath(cwd);\n\tif (parentPath !== undefined) {\n\t\ttrustOptions.push({\n\t\t\tlabel: `Trust parent folder (${parentPath})`,\n\t\t\ttrusted: true,\n\t\t\tupdates: [\n\t\t\t\t{ path: parentPath, decision: true },\n\t\t\t\t{ path: trustPath, decision: null },\n\t\t\t],\n\t\t\tsavedPath: parentPath,\n\t\t});\n\t}\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Trust (this session only)\", trusted: true, updates: [] });\n\t}\n\ttrustOptions.push({\n\t\tlabel: \"Do not trust\",\n\t\ttrusted: false,\n\t\tupdates: [{ path: trustPath, decision: false }],\n\t\tsavedPath: trustPath,\n\t});\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Do not trust (this session only)\", trusted: false, updates: [] });\n\t}\n\treturn trustOptions;\n}\n\nfunction readTrustFile(path: string): TrustFile {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to read trust store ${path}: ${message}`);\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Returns true when cwd has project-local resources that must be gated by\n * project trust: settings/resources under cwd/.lunr, executable integration\n * configuration, or .agents/skills in\n * cwd or one of its ancestors. Returns false when no such project resources\n * exist. The user/global ~/.agents/skills directory is always treated as a\n * trusted user resource and is ignored here, even when cwd is $HOME.\n */\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n\tconst homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));\n\tconst userAgentsSkillsDir = join(homeDir, \".agents\", \"skills\");\n\tlet currentDir = canonicalizePath(resolvePath(cwd));\n\n\tconst configDir = join(currentDir, CONFIG_DIR_NAME);\n\tif (\n\t\t[\".mcp.json\", \".pi/mcp.json\", \".vscode/mcp.json\", \".pi-lsp.json\"].some((entry) =>\n\t\t\texistsSync(join(currentDir, entry)),\n\t\t)\n\t) {\n\t\treturn true;\n\t}\n\tif (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tconst agentsSkillsDir = join(currentDir, \".agents\", \"skills\");\n\t\tif (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn this.getEntry(cwd)?.decision ?? null;\n\t}\n\n\tgetEntry(cwd: string): ProjectTrustStoreEntry | null {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\treturn findNearestTrustEntry(data, cwd);\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tthis.setMany([{ path: cwd, decision }]);\n\t}\n\n\tsetMany(decisions: ProjectTrustUpdate[]): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tfor (const { path, decision } of decisions) {\n\t\t\t\tconst key = normalizeCwd(path);\n\t\t\t\tif (decision === null) {\n\t\t\t\t\tdelete data[key];\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = decision;\n\t\t\t\t}\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../../src/core/update-check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,eAAO,MAAM,mBAAmB,QAAsB,CAAC;AACvD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,cAAc,mDAAyD,CAAC;AAErF,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CACjC,WAAW,GAAE,MAAqB,EAClC,OAAO,GAAE,MAAwB,GAC/B,OAAO,CAET;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAyCD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAqBzC;AAmBD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAUzE","sourcesContent":["/**\n * Published-install version check against the npm registry for @ashx-j/lunr.\n * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { gt } from \"semver\";\nimport { NPM_CLI_PACKAGE, PACKAGE_NAME } from \"../config.ts\";\n\nexport const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;\nexport const UPDATE_CHECK_TIMEOUT_MS = 4000;\nexport const NPM_LATEST_URL = `https://registry.npmjs.org/${NPM_CLI_PACKAGE}/latest`;\n\nexport interface UpdateCheckRecord {\n\tcheckedAt: number;\n\tlatest: string;\n\tnotifiedVersion?: string;\n}\n\nexport interface UpdateCheckResult {\n\tlatest: string;\n\tcurrent: string;\n\tnewer: boolean;\n\tnotice?: string;\n}\n\nexport function isPublishedInstall(\n\tpackageName: string = PACKAGE_NAME,\n\tnpmName: string = NPM_CLI_PACKAGE,\n): boolean {\n\treturn packageName === npmName;\n}\n\nexport function updateCheckPath(agentDir: string): string {\n\treturn join(agentDir, \"update-check.json\");\n}\n\nfunction readRecord(path: string): UpdateCheckRecord | undefined {\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(path, \"utf8\")) as UpdateCheckRecord;\n\t\tif (typeof raw.checkedAt !== \"number\" || typeof raw.latest !== \"string\" || !raw.latest.trim()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tcheckedAt: raw.checkedAt,\n\t\t\tlatest: raw.latest.trim(),\n\t\t\tnotifiedVersion: typeof raw.notifiedVersion === \"string\" ? raw.notifiedVersion : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeRecord(path: string, record: UpdateCheckRecord): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(record)}\\n`, \"utf8\");\n}\n\nasync function fetchLatest(fetchImpl: typeof fetch): Promise<string | undefined> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetchImpl(NPM_LATEST_URL, {\n\t\t\theaders: { Accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return undefined;\n\t\tconst payload = (await response.json()) as { version?: unknown };\n\t\treturn typeof payload.version === \"string\" && payload.version.trim() ? payload.version.trim() : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport async function checkForUpdate(options: {\n\tcurrentVersion: string;\n\tagentDir: string;\n\tnow?: number;\n\tfetchImpl?: typeof fetch;\n\tpublished?: boolean;\n\toffline?: boolean;\n\tforce?: boolean;\n}): Promise<UpdateCheckResult | undefined> {\n\tif (options.offline) return undefined;\n\tif (!(options.published ?? isPublishedInstall())) return undefined;\n\n\tconst now = options.now ?? Date.now();\n\tconst path = updateCheckPath(options.agentDir);\n\tlet record = existsSync(path) ? readRecord(path) : undefined;\n\tconst stale = !record || now - record.checkedAt >= UPDATE_CHECK_TTL_MS || options.force;\n\tif (stale) {\n\t\tconst latest = await fetchLatest(options.fetchImpl ?? fetch);\n\t\tif (!latest) return record ? toResult(options.currentVersion, record, false) : undefined;\n\t\trecord = { checkedAt: now, latest, notifiedVersion: record?.notifiedVersion };\n\t\ttry {\n\t\t\twriteRecord(path, record);\n\t\t} catch {\n\t\t\t// Cache write is best-effort.\n\t\t}\n\t}\n\n\tif (!record) return undefined;\n\treturn toResult(options.currentVersion, record, true);\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n\ttry {\n\t\treturn gt(latest, current);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction toResult(current: string, record: UpdateCheckRecord, allowNotice: boolean): UpdateCheckResult {\n\tconst newer = isNewer(record.latest, current);\n\tconst notice =\n\t\tallowNotice && newer && record.notifiedVersion !== record.latest\n\t\t\t? `lunR ${record.latest} is available. Run lunr update.`\n\t\t\t: undefined;\n\treturn { latest: record.latest, current, newer, notice };\n}\n\n/** Persist that we already showed the TUI notice for this latest version. */\nexport function markUpdateNotified(agentDir: string, latest: string): void {\n\tconst path = updateCheckPath(agentDir);\n\tconst record = existsSync(path) ? readRecord(path) : undefined;\n\tif (!record) return;\n\trecord.notifiedVersion = latest;\n\ttry {\n\t\twriteRecord(path, record);\n\t} catch {\n\t\t// ignore\n\t}\n}\n"]}
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../../src/core/update-check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,eAAO,MAAM,mBAAmB,QAAsB,CAAC;AACvD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,cAAc,mDAAyD,CAAC;AAErF,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,WAAW,GAAE,MAAqB,EAAE,OAAO,GAAE,MAAwB,GAAG,OAAO,CAEjH;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAyCD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAqBzC;AAmBD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAUzE","sourcesContent":["/**\n * Published-install version check against the npm registry for @ashx-j/lunr.\n * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { gt } from \"semver\";\nimport { NPM_CLI_PACKAGE, PACKAGE_NAME } from \"../config.ts\";\n\nexport const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;\nexport const UPDATE_CHECK_TIMEOUT_MS = 4000;\nexport const NPM_LATEST_URL = `https://registry.npmjs.org/${NPM_CLI_PACKAGE}/latest`;\n\nexport interface UpdateCheckRecord {\n\tcheckedAt: number;\n\tlatest: string;\n\tnotifiedVersion?: string;\n}\n\nexport interface UpdateCheckResult {\n\tlatest: string;\n\tcurrent: string;\n\tnewer: boolean;\n\tnotice?: string;\n}\n\nexport function isPublishedInstall(packageName: string = PACKAGE_NAME, npmName: string = NPM_CLI_PACKAGE): boolean {\n\treturn packageName === npmName;\n}\n\nexport function updateCheckPath(agentDir: string): string {\n\treturn join(agentDir, \"update-check.json\");\n}\n\nfunction readRecord(path: string): UpdateCheckRecord | undefined {\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(path, \"utf8\")) as UpdateCheckRecord;\n\t\tif (typeof raw.checkedAt !== \"number\" || typeof raw.latest !== \"string\" || !raw.latest.trim()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tcheckedAt: raw.checkedAt,\n\t\t\tlatest: raw.latest.trim(),\n\t\t\tnotifiedVersion: typeof raw.notifiedVersion === \"string\" ? raw.notifiedVersion : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeRecord(path: string, record: UpdateCheckRecord): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(record)}\\n`, \"utf8\");\n}\n\nasync function fetchLatest(fetchImpl: typeof fetch): Promise<string | undefined> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetchImpl(NPM_LATEST_URL, {\n\t\t\theaders: { Accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return undefined;\n\t\tconst payload = (await response.json()) as { version?: unknown };\n\t\treturn typeof payload.version === \"string\" && payload.version.trim() ? payload.version.trim() : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport async function checkForUpdate(options: {\n\tcurrentVersion: string;\n\tagentDir: string;\n\tnow?: number;\n\tfetchImpl?: typeof fetch;\n\tpublished?: boolean;\n\toffline?: boolean;\n\tforce?: boolean;\n}): Promise<UpdateCheckResult | undefined> {\n\tif (options.offline) return undefined;\n\tif (!(options.published ?? isPublishedInstall())) return undefined;\n\n\tconst now = options.now ?? Date.now();\n\tconst path = updateCheckPath(options.agentDir);\n\tlet record = existsSync(path) ? readRecord(path) : undefined;\n\tconst stale = !record || now - record.checkedAt >= UPDATE_CHECK_TTL_MS || options.force;\n\tif (stale) {\n\t\tconst latest = await fetchLatest(options.fetchImpl ?? fetch);\n\t\tif (!latest) return record ? toResult(options.currentVersion, record, false) : undefined;\n\t\trecord = { checkedAt: now, latest, notifiedVersion: record?.notifiedVersion };\n\t\ttry {\n\t\t\twriteRecord(path, record);\n\t\t} catch {\n\t\t\t// Cache write is best-effort.\n\t\t}\n\t}\n\n\tif (!record) return undefined;\n\treturn toResult(options.currentVersion, record, true);\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n\ttry {\n\t\treturn gt(latest, current);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction toResult(current: string, record: UpdateCheckRecord, allowNotice: boolean): UpdateCheckResult {\n\tconst newer = isNewer(record.latest, current);\n\tconst notice =\n\t\tallowNotice && newer && record.notifiedVersion !== record.latest\n\t\t\t? `lunR ${record.latest} is available. Run lunr update.`\n\t\t\t: undefined;\n\treturn { latest: record.latest, current, newer, notice };\n}\n\n/** Persist that we already showed the TUI notice for this latest version. */\nexport function markUpdateNotified(agentDir: string, latest: string): void {\n\tconst path = updateCheckPath(agentDir);\n\tconst record = existsSync(path) ? readRecord(path) : undefined;\n\tif (!record) return;\n\trecord.notifiedVersion = latest;\n\ttry {\n\t\twriteRecord(path, record);\n\t} catch {\n\t\t// ignore\n\t}\n}\n"]}