@ashx-j/lunr 0.1.1 → 0.1.2

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 (35) hide show
  1. package/dist/catalog/models.json +1 -1
  2. package/dist/catalog/providers/opencode-go.json +1 -1
  3. package/dist/catalog/providers/openrouter.json +1 -1
  4. package/dist/catalog/providers/qwen-token-plan-cn.json +1 -1
  5. package/dist/catalog/providers/qwen-token-plan.json +1 -1
  6. package/dist/catalog/publication.json +3 -3
  7. package/dist/core/catalog-merge.d.ts.map +1 -1
  8. package/dist/core/catalog-merge.js +7 -1
  9. package/dist/core/catalog-merge.js.map +1 -1
  10. package/dist/core/grok-cli-auth.d.ts +32 -0
  11. package/dist/core/grok-cli-auth.d.ts.map +1 -0
  12. package/dist/core/grok-cli-auth.js +218 -0
  13. package/dist/core/grok-cli-auth.js.map +1 -0
  14. package/dist/core/model-runtime.d.ts +9 -0
  15. package/dist/core/model-runtime.d.ts.map +1 -1
  16. package/dist/core/model-runtime.js +22 -3
  17. package/dist/core/model-runtime.js.map +1 -1
  18. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  19. package/dist/core/usage-adapters/xai.js +4 -0
  20. package/dist/core/usage-adapters/xai.js.map +1 -1
  21. package/dist/core/usage-service.d.ts +11 -1
  22. package/dist/core/usage-service.d.ts.map +1 -1
  23. package/dist/core/usage-service.js +45 -9
  24. package/dist/core/usage-service.js.map +1 -1
  25. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  26. package/dist/modes/interactive/interactive-mode.js +28 -3
  27. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  28. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  29. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  30. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  31. package/examples/extensions/gondolin/package-lock.json +2 -2
  32. package/examples/extensions/gondolin/package.json +1 -1
  33. package/examples/extensions/with-deps/package-lock.json +2 -2
  34. package/examples/extensions/with-deps/package.json +1 -1
  35. package/package.json +4 -4
@@ -1 +1 @@
1
- {"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":32768},"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-flash-0731":{"id":"deepseek-v4-flash-0731","name":"DeepSeek V4 Flash 0731","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":98304},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.8-max":{"id":"qwen3.8-max","name":"Qwen3.8 Max","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}}
1
+ {"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":32768},"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-flash-0731":{"id":"deepseek-v4-flash-0731","name":"DeepSeek V4 Flash 0731","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro-0813":{"id":"deepseek-v4-pro-0813","name":"DeepSeek V4 Pro 0813","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":98304},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.8-max":{"id":"qwen3.8-max","name":"Qwen3.8 Max","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}}
@@ -1 +1 @@
1
- {"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":32768},"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-flash-0731":{"id":"deepseek-v4-flash-0731","name":"DeepSeek V4 Flash 0731","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":98304},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.8-max":{"id":"qwen3.8-max","name":"Qwen3.8 Max","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}}
1
+ {"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":32768},"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-flash-0731":{"id":"deepseek-v4-flash-0731","name":"DeepSeek V4 Flash 0731","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro-0813":{"id":"deepseek-v4-pro-0813","name":"DeepSeek V4 Pro 0813","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":98304},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.8-max":{"id":"qwen3.8-max","name":"Qwen3.8 Max","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://dashscope-intl.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"thinkingFormat":"qwen","maxTokensField":"max_tokens","supportsReasoningEffort":true},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}}
@@ -1,6 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-08-15T14:25:56.997Z",
3
- "sourceCommit": "154b7afe9c4bbccb44d4df7ed26f199ad01c1cfc",
2
+ "generatedAt": "2026-08-16T13:01:53.176Z",
3
+ "sourceCommit": "c944d341e1873eaaa4b64383358d11c903217115",
4
4
  "providerCount": 39,
5
- "modelCount": 1383
5
+ "modelCount": 1385
6
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"catalog-merge.d.ts","sourceRoot":"","sources":["../../src/core/catalog-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAY,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,MAAM,4BAA4B,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;CACjC,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAuBf;AAED,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,IAAI,CAAmC;IAE/C,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE/C;IAED,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE3C;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE/D;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAE5C;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAExC;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAExC;CACD;AAYD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAA;CAAE,EAClG,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,oBAAoB,GAC3B,CAAC,CAYH;AAED,MAAM,WAAW,6BAA6B;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,4BAA4B,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,qBAAqB,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,SAAS,EAAE,SAAS,6BAA6B,EAAE,CAAC;IACpD,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CA2CrF;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,EAC9B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GACpB,GAAG,CAAC,MAAM,CAAC,CAKb","sourcesContent":["import type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { modelKey, type OfficialCatalogSource } from \"./official-catalog.ts\";\n\nexport type CatalogProviderRefreshStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\" | \"static\";\n\nexport function mergeCatalogLayers(args: {\n\tbakedIn: readonly Model<Api>[];\n\tlive?: readonly Model<Api>[];\n\tuser?: readonly Model<Api>[];\n\tofficial?: readonly Model<Api>[];\n}): Model<Api>[] {\n\tconst byId = new Map<string, Model<Api>>();\n\tfor (const model of args.bakedIn) byId.set(model.id, model);\n\tfor (const model of args.live ?? []) byId.set(model.id, model);\n\tfor (const model of args.user ?? []) byId.set(model.id, model);\n\tfor (const model of args.official ?? []) byId.set(model.id, model);\n\n\tconst seen = new Set<string>();\n\tconst result: Model<Api>[] = [];\n\tfor (const baked of args.bakedIn) {\n\t\tconst merged = byId.get(baked.id);\n\t\tif (!merged) continue;\n\t\tresult.push(merged);\n\t\tseen.add(baked.id);\n\t}\n\tfor (const model of [...(args.live ?? []), ...(args.user ?? []), ...(args.official ?? [])]) {\n\t\tif (seen.has(model.id)) continue;\n\t\tconst winner = byId.get(model.id);\n\t\tif (!winner) continue;\n\t\tresult.push(winner);\n\t\tseen.add(model.id);\n\t}\n\treturn result;\n}\n\nexport class CatalogOverlaySource {\n\tprivate official = new Map<string, Model<Api>[]>();\n\tprivate user = new Map<string, Model<Api>[]>();\n\tprivate live = new Map<string, Model<Api>[]>();\n\n\tsetOfficial(models: readonly Model<Api>[]): void {\n\t\tthis.official = groupByProvider(models);\n\t}\n\n\tsetUser(models: readonly Model<Api>[]): void {\n\t\tthis.user = groupByProvider(models);\n\t}\n\n\tsetLive(providerId: string, models: readonly Model<Api>[]): void {\n\t\tthis.live.set(providerId, [...models]);\n\t}\n\n\tofficialFor(providerId: string): Model<Api>[] {\n\t\treturn this.official.get(providerId) ?? [];\n\t}\n\n\tuserFor(providerId: string): Model<Api>[] {\n\t\treturn this.user.get(providerId) ?? [];\n\t}\n\n\tliveFor(providerId: string): Model<Api>[] {\n\t\treturn this.live.get(providerId) ?? [];\n\t}\n}\n\nfunction groupByProvider(models: readonly Model<Api>[]): Map<string, Model<Api>[]> {\n\tconst byProvider = new Map<string, Model<Api>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\treturn byProvider;\n}\n\nexport function withCatalogOverlay<T extends { id: string; getModels: () => readonly Model<Api>[] }>(\n\tprovider: T,\n\toverlay: CatalogOverlaySource,\n): T {\n\tconst bakedIn = () => provider.getModels();\n\treturn {\n\t\t...provider,\n\t\tgetModels: () =>\n\t\t\tmergeCatalogLayers({\n\t\t\t\tbakedIn: bakedIn(),\n\t\t\t\tlive: overlay.liveFor(provider.id),\n\t\t\t\tuser: overlay.userFor(provider.id),\n\t\t\t\tofficial: overlay.officialFor(provider.id),\n\t\t\t}),\n\t};\n}\n\nexport interface CatalogRefreshProviderSummary {\n\tid: string;\n\tstatus: CatalogProviderRefreshStatus;\n\tadded?: number;\n\ttotal?: number;\n\terror?: string;\n}\n\nexport interface CatalogRefreshSummaryInput {\n\tofficial?: { source: OfficialCatalogSource; modelCount: number; evictedUserRows: number };\n\tproviders: readonly CatalogRefreshProviderSummary[];\n\tasked?: readonly string[];\n\textraDefaults?: number;\n\tollama?: string;\n}\n\n/** Build the /refresh toast. One hung provider must not become a global timeout line. */\nexport function formatCatalogRefreshSummary(input: CatalogRefreshSummaryInput): string {\n\tconst parts: string[] = [];\n\tif (input.official?.source === \"github\") {\n\t\tconst evicted =\n\t\t\tinput.official.evictedUserRows > 0 ? `; replaced ${input.official.evictedUserRows} user-filled row` : \"\";\n\t\tconst plural = input.official.evictedUserRows === 1 ? \"\" : input.official.evictedUserRows > 0 ? \"s\" : \"\";\n\t\tparts.push(`Official catalog updated (${input.official.modelCount} models)${evicted}${plural}.`);\n\t}\n\n\tconst refreshed: string[] = [];\n\tconst failed: string[] = [];\n\tfor (const provider of input.providers) {\n\t\tif (provider.status === \"ok\") {\n\t\t\tconst added = provider.added ?? 0;\n\t\t\tconst total = provider.total ?? 0;\n\t\t\trefreshed.push(added > 0 ? `${provider.id} (+${added})` : `${provider.id} (${total})`);\n\t\t} else if (provider.status === \"timeout\") {\n\t\t\tfailed.push(`${provider.id} timed out (cached)`);\n\t\t} else if (provider.status === \"error\") {\n\t\t\tfailed.push(provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`);\n\t\t}\n\t}\n\n\tif (refreshed.length > 0) {\n\t\tparts.push(`Refreshed ${refreshed.join(\", \")}.`);\n\t} else if (input.providers.some((provider) => provider.status === \"ok\")) {\n\t\tparts.push(\"Refreshed model catalogs.\");\n\t}\n\n\tif (failed.length > 0) {\n\t\tparts.push(`${failed.join(\"; \")}.`);\n\t}\n\n\tif (input.asked && input.asked.length > 0) {\n\t\tparts.push(`Asked for missing metadata on ${input.asked.join(\", \")}.`);\n\t}\n\tif (input.extraDefaults && input.extraDefaults > 0) {\n\t\tparts.push(`+${input.extraDefaults} more using defaults.`);\n\t}\n\tif (input.ollama) parts.push(input.ollama);\n\n\tif (parts.length === 0) return \"Model catalogs refreshed.\";\n\treturn parts.join(\" \");\n}\n\nexport function knownModelKeys(\n\tbakedIn: readonly Model<Api>[],\n\tofficial: Iterable<string>,\n\tuser: Iterable<string>,\n): Set<string> {\n\tconst keys = new Set<string>(official);\n\tfor (const key of user) keys.add(key);\n\tfor (const model of bakedIn) keys.add(modelKey(model.provider, model.id));\n\treturn keys;\n}\n"]}
1
+ {"version":3,"file":"catalog-merge.d.ts","sourceRoot":"","sources":["../../src/core/catalog-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAY,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,MAAM,4BAA4B,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;CACjC,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAuBf;AAED,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,IAAI,CAAmC;IAE/C,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE/C;IAED,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE3C;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAE/D;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAE5C;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAExC;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAExC;CACD;AAYD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAA;CAAE,EAClG,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,oBAAoB,GAC3B,CAAC,CAYH;AAED,MAAM,WAAW,6BAA6B;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,4BAA4B,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,qBAAqB,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,SAAS,EAAE,SAAS,6BAA6B,EAAE,CAAC;IACpD,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CA2CrF;AASD,wBAAgB,cAAc,CAC7B,OAAO,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,EAC9B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GACpB,GAAG,CAAC,MAAM,CAAC,CAKb","sourcesContent":["import type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { modelKey, type OfficialCatalogSource } from \"./official-catalog.ts\";\n\nexport type CatalogProviderRefreshStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\" | \"static\";\n\nexport function mergeCatalogLayers(args: {\n\tbakedIn: readonly Model<Api>[];\n\tlive?: readonly Model<Api>[];\n\tuser?: readonly Model<Api>[];\n\tofficial?: readonly Model<Api>[];\n}): Model<Api>[] {\n\tconst byId = new Map<string, Model<Api>>();\n\tfor (const model of args.bakedIn) byId.set(model.id, model);\n\tfor (const model of args.live ?? []) byId.set(model.id, model);\n\tfor (const model of args.user ?? []) byId.set(model.id, model);\n\tfor (const model of args.official ?? []) byId.set(model.id, model);\n\n\tconst seen = new Set<string>();\n\tconst result: Model<Api>[] = [];\n\tfor (const baked of args.bakedIn) {\n\t\tconst merged = byId.get(baked.id);\n\t\tif (!merged) continue;\n\t\tresult.push(merged);\n\t\tseen.add(baked.id);\n\t}\n\tfor (const model of [...(args.live ?? []), ...(args.user ?? []), ...(args.official ?? [])]) {\n\t\tif (seen.has(model.id)) continue;\n\t\tconst winner = byId.get(model.id);\n\t\tif (!winner) continue;\n\t\tresult.push(winner);\n\t\tseen.add(model.id);\n\t}\n\treturn result;\n}\n\nexport class CatalogOverlaySource {\n\tprivate official = new Map<string, Model<Api>[]>();\n\tprivate user = new Map<string, Model<Api>[]>();\n\tprivate live = new Map<string, Model<Api>[]>();\n\n\tsetOfficial(models: readonly Model<Api>[]): void {\n\t\tthis.official = groupByProvider(models);\n\t}\n\n\tsetUser(models: readonly Model<Api>[]): void {\n\t\tthis.user = groupByProvider(models);\n\t}\n\n\tsetLive(providerId: string, models: readonly Model<Api>[]): void {\n\t\tthis.live.set(providerId, [...models]);\n\t}\n\n\tofficialFor(providerId: string): Model<Api>[] {\n\t\treturn this.official.get(providerId) ?? [];\n\t}\n\n\tuserFor(providerId: string): Model<Api>[] {\n\t\treturn this.user.get(providerId) ?? [];\n\t}\n\n\tliveFor(providerId: string): Model<Api>[] {\n\t\treturn this.live.get(providerId) ?? [];\n\t}\n}\n\nfunction groupByProvider(models: readonly Model<Api>[]): Map<string, Model<Api>[]> {\n\tconst byProvider = new Map<string, Model<Api>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\treturn byProvider;\n}\n\nexport function withCatalogOverlay<T extends { id: string; getModels: () => readonly Model<Api>[] }>(\n\tprovider: T,\n\toverlay: CatalogOverlaySource,\n): T {\n\tconst bakedIn = () => provider.getModels();\n\treturn {\n\t\t...provider,\n\t\tgetModels: () =>\n\t\t\tmergeCatalogLayers({\n\t\t\t\tbakedIn: bakedIn(),\n\t\t\t\tlive: overlay.liveFor(provider.id),\n\t\t\t\tuser: overlay.userFor(provider.id),\n\t\t\t\tofficial: overlay.officialFor(provider.id),\n\t\t\t}),\n\t};\n}\n\nexport interface CatalogRefreshProviderSummary {\n\tid: string;\n\tstatus: CatalogProviderRefreshStatus;\n\tadded?: number;\n\ttotal?: number;\n\terror?: string;\n}\n\nexport interface CatalogRefreshSummaryInput {\n\tofficial?: { source: OfficialCatalogSource; modelCount: number; evictedUserRows: number };\n\tproviders: readonly CatalogRefreshProviderSummary[];\n\tasked?: readonly string[];\n\textraDefaults?: number;\n\tollama?: string;\n}\n\n/** Build the /refresh toast. One hung provider must not become a global timeout line. */\nexport function formatCatalogRefreshSummary(input: CatalogRefreshSummaryInput): string {\n\tconst parts: string[] = [];\n\tif (input.official?.source === \"github\") {\n\t\tconst evicted =\n\t\t\tinput.official.evictedUserRows > 0 ? `; replaced ${input.official.evictedUserRows} user-filled row` : \"\";\n\t\tconst plural = input.official.evictedUserRows === 1 ? \"\" : input.official.evictedUserRows > 0 ? \"s\" : \"\";\n\t\tparts.push(`Official catalog updated (${input.official.modelCount} models)${evicted}${plural}.`);\n\t}\n\n\tconst refreshed: string[] = [];\n\tconst failed: string[] = [];\n\tfor (const provider of input.providers) {\n\t\tif (provider.status === \"ok\") {\n\t\t\tconst added = provider.added ?? 0;\n\t\t\tconst total = provider.total ?? 0;\n\t\t\trefreshed.push(added > 0 ? `${provider.id} (+${added})` : `${provider.id} (${total})`);\n\t\t} else if (provider.status === \"timeout\") {\n\t\t\tfailed.push(`${provider.id} timed out (cached)`);\n\t\t} else if (provider.status === \"error\") {\n\t\t\tfailed.push(formatLiveProviderError(provider));\n\t\t}\n\t}\n\n\tif (refreshed.length > 0) {\n\t\tparts.push(`Refreshed ${refreshed.join(\", \")}.`);\n\t} else if (input.providers.some((provider) => provider.status === \"ok\")) {\n\t\tparts.push(\"Refreshed model catalogs.\");\n\t}\n\n\tif (failed.length > 0) {\n\t\tparts.push(`${failed.join(\"; \")}.`);\n\t}\n\n\tif (input.asked && input.asked.length > 0) {\n\t\tparts.push(`Asked for missing metadata on ${input.asked.join(\", \")}.`);\n\t}\n\tif (input.extraDefaults && input.extraDefaults > 0) {\n\t\tparts.push(`+${input.extraDefaults} more using defaults.`);\n\t}\n\tif (input.ollama) parts.push(input.ollama);\n\n\tif (parts.length === 0) return \"Model catalogs refreshed.\";\n\treturn parts.join(\" \");\n}\n\nfunction formatLiveProviderError(provider: CatalogRefreshProviderSummary): string {\n\tif (provider.id === \"xai\" && provider.error && /invalid_grant|oauth refresh failed/i.test(provider.error)) {\n\t\treturn \"xai login expired (run /login xai)\";\n\t}\n\treturn provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`;\n}\n\nexport function knownModelKeys(\n\tbakedIn: readonly Model<Api>[],\n\tofficial: Iterable<string>,\n\tuser: Iterable<string>,\n): Set<string> {\n\tconst keys = new Set<string>(official);\n\tfor (const key of user) keys.add(key);\n\tfor (const model of bakedIn) keys.add(modelKey(model.provider, model.id));\n\treturn keys;\n}\n"]}
@@ -93,7 +93,7 @@ export function formatCatalogRefreshSummary(input) {
93
93
  failed.push(`${provider.id} timed out (cached)`);
94
94
  }
95
95
  else if (provider.status === "error") {
96
- failed.push(provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`);
96
+ failed.push(formatLiveProviderError(provider));
97
97
  }
98
98
  }
99
99
  if (refreshed.length > 0) {
@@ -117,6 +117,12 @@ export function formatCatalogRefreshSummary(input) {
117
117
  return "Model catalogs refreshed.";
118
118
  return parts.join(" ");
119
119
  }
120
+ function formatLiveProviderError(provider) {
121
+ if (provider.id === "xai" && provider.error && /invalid_grant|oauth refresh failed/i.test(provider.error)) {
122
+ return "xai login expired (run /login xai)";
123
+ }
124
+ return provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`;
125
+ }
120
126
  export function knownModelKeys(bakedIn, official, user) {
121
127
  const keys = new Set(official);
122
128
  for (const key of user)
@@ -1 +1 @@
1
- {"version":3,"file":"catalog-merge.js","sourceRoot":"","sources":["../../src/core/catalog-merge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { modelKey, type OfficialCatalogSource } from \"./official-catalog.ts\";\n\nexport type CatalogProviderRefreshStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\" | \"static\";\n\nexport function mergeCatalogLayers(args: {\n\tbakedIn: readonly Model<Api>[];\n\tlive?: readonly Model<Api>[];\n\tuser?: readonly Model<Api>[];\n\tofficial?: readonly Model<Api>[];\n}): Model<Api>[] {\n\tconst byId = new Map<string, Model<Api>>();\n\tfor (const model of args.bakedIn) byId.set(model.id, model);\n\tfor (const model of args.live ?? []) byId.set(model.id, model);\n\tfor (const model of args.user ?? []) byId.set(model.id, model);\n\tfor (const model of args.official ?? []) byId.set(model.id, model);\n\n\tconst seen = new Set<string>();\n\tconst result: Model<Api>[] = [];\n\tfor (const baked of args.bakedIn) {\n\t\tconst merged = byId.get(baked.id);\n\t\tif (!merged) continue;\n\t\tresult.push(merged);\n\t\tseen.add(baked.id);\n\t}\n\tfor (const model of [...(args.live ?? []), ...(args.user ?? []), ...(args.official ?? [])]) {\n\t\tif (seen.has(model.id)) continue;\n\t\tconst winner = byId.get(model.id);\n\t\tif (!winner) continue;\n\t\tresult.push(winner);\n\t\tseen.add(model.id);\n\t}\n\treturn result;\n}\n\nexport class CatalogOverlaySource {\n\tprivate official = new Map<string, Model<Api>[]>();\n\tprivate user = new Map<string, Model<Api>[]>();\n\tprivate live = new Map<string, Model<Api>[]>();\n\n\tsetOfficial(models: readonly Model<Api>[]): void {\n\t\tthis.official = groupByProvider(models);\n\t}\n\n\tsetUser(models: readonly Model<Api>[]): void {\n\t\tthis.user = groupByProvider(models);\n\t}\n\n\tsetLive(providerId: string, models: readonly Model<Api>[]): void {\n\t\tthis.live.set(providerId, [...models]);\n\t}\n\n\tofficialFor(providerId: string): Model<Api>[] {\n\t\treturn this.official.get(providerId) ?? [];\n\t}\n\n\tuserFor(providerId: string): Model<Api>[] {\n\t\treturn this.user.get(providerId) ?? [];\n\t}\n\n\tliveFor(providerId: string): Model<Api>[] {\n\t\treturn this.live.get(providerId) ?? [];\n\t}\n}\n\nfunction groupByProvider(models: readonly Model<Api>[]): Map<string, Model<Api>[]> {\n\tconst byProvider = new Map<string, Model<Api>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\treturn byProvider;\n}\n\nexport function withCatalogOverlay<T extends { id: string; getModels: () => readonly Model<Api>[] }>(\n\tprovider: T,\n\toverlay: CatalogOverlaySource,\n): T {\n\tconst bakedIn = () => provider.getModels();\n\treturn {\n\t\t...provider,\n\t\tgetModels: () =>\n\t\t\tmergeCatalogLayers({\n\t\t\t\tbakedIn: bakedIn(),\n\t\t\t\tlive: overlay.liveFor(provider.id),\n\t\t\t\tuser: overlay.userFor(provider.id),\n\t\t\t\tofficial: overlay.officialFor(provider.id),\n\t\t\t}),\n\t};\n}\n\nexport interface CatalogRefreshProviderSummary {\n\tid: string;\n\tstatus: CatalogProviderRefreshStatus;\n\tadded?: number;\n\ttotal?: number;\n\terror?: string;\n}\n\nexport interface CatalogRefreshSummaryInput {\n\tofficial?: { source: OfficialCatalogSource; modelCount: number; evictedUserRows: number };\n\tproviders: readonly CatalogRefreshProviderSummary[];\n\tasked?: readonly string[];\n\textraDefaults?: number;\n\tollama?: string;\n}\n\n/** Build the /refresh toast. One hung provider must not become a global timeout line. */\nexport function formatCatalogRefreshSummary(input: CatalogRefreshSummaryInput): string {\n\tconst parts: string[] = [];\n\tif (input.official?.source === \"github\") {\n\t\tconst evicted =\n\t\t\tinput.official.evictedUserRows > 0 ? `; replaced ${input.official.evictedUserRows} user-filled row` : \"\";\n\t\tconst plural = input.official.evictedUserRows === 1 ? \"\" : input.official.evictedUserRows > 0 ? \"s\" : \"\";\n\t\tparts.push(`Official catalog updated (${input.official.modelCount} models)${evicted}${plural}.`);\n\t}\n\n\tconst refreshed: string[] = [];\n\tconst failed: string[] = [];\n\tfor (const provider of input.providers) {\n\t\tif (provider.status === \"ok\") {\n\t\t\tconst added = provider.added ?? 0;\n\t\t\tconst total = provider.total ?? 0;\n\t\t\trefreshed.push(added > 0 ? `${provider.id} (+${added})` : `${provider.id} (${total})`);\n\t\t} else if (provider.status === \"timeout\") {\n\t\t\tfailed.push(`${provider.id} timed out (cached)`);\n\t\t} else if (provider.status === \"error\") {\n\t\t\tfailed.push(provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`);\n\t\t}\n\t}\n\n\tif (refreshed.length > 0) {\n\t\tparts.push(`Refreshed ${refreshed.join(\", \")}.`);\n\t} else if (input.providers.some((provider) => provider.status === \"ok\")) {\n\t\tparts.push(\"Refreshed model catalogs.\");\n\t}\n\n\tif (failed.length > 0) {\n\t\tparts.push(`${failed.join(\"; \")}.`);\n\t}\n\n\tif (input.asked && input.asked.length > 0) {\n\t\tparts.push(`Asked for missing metadata on ${input.asked.join(\", \")}.`);\n\t}\n\tif (input.extraDefaults && input.extraDefaults > 0) {\n\t\tparts.push(`+${input.extraDefaults} more using defaults.`);\n\t}\n\tif (input.ollama) parts.push(input.ollama);\n\n\tif (parts.length === 0) return \"Model catalogs refreshed.\";\n\treturn parts.join(\" \");\n}\n\nexport function knownModelKeys(\n\tbakedIn: readonly Model<Api>[],\n\tofficial: Iterable<string>,\n\tuser: Iterable<string>,\n): Set<string> {\n\tconst keys = new Set<string>(official);\n\tfor (const key of user) keys.add(key);\n\tfor (const model of bakedIn) keys.add(modelKey(model.provider, model.id));\n\treturn keys;\n}\n"]}
1
+ {"version":3,"file":"catalog-merge.js","sourceRoot":"","sources":["../../src/core/catalog-merge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { modelKey, type OfficialCatalogSource } from \"./official-catalog.ts\";\n\nexport type CatalogProviderRefreshStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\" | \"static\";\n\nexport function mergeCatalogLayers(args: {\n\tbakedIn: readonly Model<Api>[];\n\tlive?: readonly Model<Api>[];\n\tuser?: readonly Model<Api>[];\n\tofficial?: readonly Model<Api>[];\n}): Model<Api>[] {\n\tconst byId = new Map<string, Model<Api>>();\n\tfor (const model of args.bakedIn) byId.set(model.id, model);\n\tfor (const model of args.live ?? []) byId.set(model.id, model);\n\tfor (const model of args.user ?? []) byId.set(model.id, model);\n\tfor (const model of args.official ?? []) byId.set(model.id, model);\n\n\tconst seen = new Set<string>();\n\tconst result: Model<Api>[] = [];\n\tfor (const baked of args.bakedIn) {\n\t\tconst merged = byId.get(baked.id);\n\t\tif (!merged) continue;\n\t\tresult.push(merged);\n\t\tseen.add(baked.id);\n\t}\n\tfor (const model of [...(args.live ?? []), ...(args.user ?? []), ...(args.official ?? [])]) {\n\t\tif (seen.has(model.id)) continue;\n\t\tconst winner = byId.get(model.id);\n\t\tif (!winner) continue;\n\t\tresult.push(winner);\n\t\tseen.add(model.id);\n\t}\n\treturn result;\n}\n\nexport class CatalogOverlaySource {\n\tprivate official = new Map<string, Model<Api>[]>();\n\tprivate user = new Map<string, Model<Api>[]>();\n\tprivate live = new Map<string, Model<Api>[]>();\n\n\tsetOfficial(models: readonly Model<Api>[]): void {\n\t\tthis.official = groupByProvider(models);\n\t}\n\n\tsetUser(models: readonly Model<Api>[]): void {\n\t\tthis.user = groupByProvider(models);\n\t}\n\n\tsetLive(providerId: string, models: readonly Model<Api>[]): void {\n\t\tthis.live.set(providerId, [...models]);\n\t}\n\n\tofficialFor(providerId: string): Model<Api>[] {\n\t\treturn this.official.get(providerId) ?? [];\n\t}\n\n\tuserFor(providerId: string): Model<Api>[] {\n\t\treturn this.user.get(providerId) ?? [];\n\t}\n\n\tliveFor(providerId: string): Model<Api>[] {\n\t\treturn this.live.get(providerId) ?? [];\n\t}\n}\n\nfunction groupByProvider(models: readonly Model<Api>[]): Map<string, Model<Api>[]> {\n\tconst byProvider = new Map<string, Model<Api>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\treturn byProvider;\n}\n\nexport function withCatalogOverlay<T extends { id: string; getModels: () => readonly Model<Api>[] }>(\n\tprovider: T,\n\toverlay: CatalogOverlaySource,\n): T {\n\tconst bakedIn = () => provider.getModels();\n\treturn {\n\t\t...provider,\n\t\tgetModels: () =>\n\t\t\tmergeCatalogLayers({\n\t\t\t\tbakedIn: bakedIn(),\n\t\t\t\tlive: overlay.liveFor(provider.id),\n\t\t\t\tuser: overlay.userFor(provider.id),\n\t\t\t\tofficial: overlay.officialFor(provider.id),\n\t\t\t}),\n\t};\n}\n\nexport interface CatalogRefreshProviderSummary {\n\tid: string;\n\tstatus: CatalogProviderRefreshStatus;\n\tadded?: number;\n\ttotal?: number;\n\terror?: string;\n}\n\nexport interface CatalogRefreshSummaryInput {\n\tofficial?: { source: OfficialCatalogSource; modelCount: number; evictedUserRows: number };\n\tproviders: readonly CatalogRefreshProviderSummary[];\n\tasked?: readonly string[];\n\textraDefaults?: number;\n\tollama?: string;\n}\n\n/** Build the /refresh toast. One hung provider must not become a global timeout line. */\nexport function formatCatalogRefreshSummary(input: CatalogRefreshSummaryInput): string {\n\tconst parts: string[] = [];\n\tif (input.official?.source === \"github\") {\n\t\tconst evicted =\n\t\t\tinput.official.evictedUserRows > 0 ? `; replaced ${input.official.evictedUserRows} user-filled row` : \"\";\n\t\tconst plural = input.official.evictedUserRows === 1 ? \"\" : input.official.evictedUserRows > 0 ? \"s\" : \"\";\n\t\tparts.push(`Official catalog updated (${input.official.modelCount} models)${evicted}${plural}.`);\n\t}\n\n\tconst refreshed: string[] = [];\n\tconst failed: string[] = [];\n\tfor (const provider of input.providers) {\n\t\tif (provider.status === \"ok\") {\n\t\t\tconst added = provider.added ?? 0;\n\t\t\tconst total = provider.total ?? 0;\n\t\t\trefreshed.push(added > 0 ? `${provider.id} (+${added})` : `${provider.id} (${total})`);\n\t\t} else if (provider.status === \"timeout\") {\n\t\t\tfailed.push(`${provider.id} timed out (cached)`);\n\t\t} else if (provider.status === \"error\") {\n\t\t\tfailed.push(formatLiveProviderError(provider));\n\t\t}\n\t}\n\n\tif (refreshed.length > 0) {\n\t\tparts.push(`Refreshed ${refreshed.join(\", \")}.`);\n\t} else if (input.providers.some((provider) => provider.status === \"ok\")) {\n\t\tparts.push(\"Refreshed model catalogs.\");\n\t}\n\n\tif (failed.length > 0) {\n\t\tparts.push(`${failed.join(\"; \")}.`);\n\t}\n\n\tif (input.asked && input.asked.length > 0) {\n\t\tparts.push(`Asked for missing metadata on ${input.asked.join(\", \")}.`);\n\t}\n\tif (input.extraDefaults && input.extraDefaults > 0) {\n\t\tparts.push(`+${input.extraDefaults} more using defaults.`);\n\t}\n\tif (input.ollama) parts.push(input.ollama);\n\n\tif (parts.length === 0) return \"Model catalogs refreshed.\";\n\treturn parts.join(\" \");\n}\n\nfunction formatLiveProviderError(provider: CatalogRefreshProviderSummary): string {\n\tif (provider.id === \"xai\" && provider.error && /invalid_grant|oauth refresh failed/i.test(provider.error)) {\n\t\treturn \"xai login expired (run /login xai)\";\n\t}\n\treturn provider.error ? `${provider.id} failed (${provider.error})` : `${provider.id} failed`;\n}\n\nexport function knownModelKeys(\n\tbakedIn: readonly Model<Api>[],\n\tofficial: Iterable<string>,\n\tuser: Iterable<string>,\n): Set<string> {\n\tconst keys = new Set<string>(official);\n\tfor (const key of user) keys.add(key);\n\tfor (const model of bakedIn) keys.add(modelKey(model.provider, model.id));\n\treturn keys;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Official Grok CLI SuperGrok session (`~/.grok/auth.json`).
3
+ *
4
+ * lunR and Grok CLI share xAI client `b1a00492-073a-47ea-816f-4c329264a828`.
5
+ * A later `grok login` revokes lunR's refresh token; this module reads the
6
+ * still-live Grok CLI entry and write-throughs rotated tokens so the two
7
+ * CLIs do not fight.
8
+ */
9
+ import type { Credential, CredentialStore, OAuthCredential } from "@ashx-j/lunr-ai";
10
+ export declare const XAI_PROVIDER_ID = "xai";
11
+ /** Same client as packages/ai/src/auth/oauth/xai.ts `XAI_CLIENT_ID`. */
12
+ export declare const XAI_GROK_CLI_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
13
+ export declare function grokHomeDir(grokHome?: string): string;
14
+ export declare function grokAuthPath(grokHome?: string): string;
15
+ export declare function grokOidcEntryKey(clientId?: string): string;
16
+ export declare function parseGrokExpiresAt(value: unknown): number | undefined;
17
+ export declare function grokEntryToOAuth(entry: unknown): OAuthCredential | undefined;
18
+ export declare function readGrokCliXaiOAuth(options?: {
19
+ grokHome?: string;
20
+ }): OAuthCredential | undefined;
21
+ export declare function hasGrokCliXaiAuth(options?: {
22
+ grokHome?: string;
23
+ }): boolean;
24
+ export declare function chooseXaiOAuth(lunr: Credential | undefined, grok: OAuthCredential | undefined): Credential | undefined;
25
+ export declare function writeGrokCliXaiOAuth(credential: OAuthCredential, options?: {
26
+ grokHome?: string;
27
+ onlyIfFileExists?: boolean;
28
+ }): boolean;
29
+ export declare function wrapXaiGrokCliCredentials(store: CredentialStore, options?: {
30
+ grokHome?: string;
31
+ }): CredentialStore;
32
+ //# sourceMappingURL=grok-cli-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grok-cli-auth.d.ts","sourceRoot":"","sources":["../../src/core/grok-cli-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAkB,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG1G,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,yCAAyC,CAAC;AAI7E,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAA+B,GAAG,MAAM,CAElF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAarE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAQ5E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,eAAe,GAAG,SAAS,CASpG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAE9E;AAED,wBAAgB,cAAc,CAC7B,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,IAAI,EAAE,eAAe,GAAG,SAAS,GAC/B,UAAU,GAAG,SAAS,CAKxB;AAwBD,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7D,OAAO,CA8CT;AAED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,eAAe,CAyDjB","sourcesContent":["/**\n * Official Grok CLI SuperGrok session (`~/.grok/auth.json`).\n *\n * lunR and Grok CLI share xAI client `b1a00492-073a-47ea-816f-4c329264a828`.\n * A later `grok login` revokes lunR's refresh token; this module reads the\n * still-live Grok CLI entry and write-throughs rotated tokens so the two\n * CLIs do not fight.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { Credential, CredentialInfo, CredentialStore, OAuthCredential } from \"@ashx-j/lunr-ai\";\nimport lockfile from \"proper-lockfile\";\n\nexport const XAI_PROVIDER_ID = \"xai\";\n/** Same client as packages/ai/src/auth/oauth/xai.ts `XAI_CLIENT_ID`. */\nexport const XAI_GROK_CLI_CLIENT_ID = \"b1a00492-073a-47ea-816f-4c329264a828\";\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport function grokHomeDir(grokHome?: string): string {\n\tif (grokHome) return grokHome;\n\tconst fromEnv = process.env.GROK_HOME?.trim();\n\tif (fromEnv) return fromEnv;\n\treturn join(homedir(), \".grok\");\n}\n\nexport function grokAuthPath(grokHome?: string): string {\n\treturn join(grokHomeDir(grokHome), \"auth.json\");\n}\n\nexport function grokOidcEntryKey(clientId: string = XAI_GROK_CLI_CLIENT_ID): string {\n\treturn `https://auth.x.ai::${clientId}`;\n}\n\nexport function parseGrokExpiresAt(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n\t\treturn value < 1e12 ? Math.round(value * 1000) : Math.round(value);\n\t}\n\tif (typeof value === \"string\" && value.trim()) {\n\t\tconst numeric = Number(value);\n\t\tif (Number.isFinite(numeric) && numeric > 0) {\n\t\t\treturn numeric < 1e12 ? Math.round(numeric * 1000) : Math.round(numeric);\n\t\t}\n\t\tconst parsed = Date.parse(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn undefined;\n}\n\nexport function grokEntryToOAuth(entry: unknown): OAuthCredential | undefined {\n\tif (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return undefined;\n\tconst record = entry as Record<string, unknown>;\n\tconst access = typeof record.key === \"string\" ? record.key : undefined;\n\tconst refresh = typeof record.refresh_token === \"string\" ? record.refresh_token : undefined;\n\tconst expires = parseGrokExpiresAt(record.expires_at);\n\tif (!access || !refresh || expires === undefined) return undefined;\n\treturn { type: \"oauth\", access, refresh, expires };\n}\n\nexport function readGrokCliXaiOAuth(options: { grokHome?: string } = {}): OAuthCredential | undefined {\n\ttry {\n\t\tconst raw = readFileSync(grokAuthPath(options.grokHome), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\treturn grokEntryToOAuth((parsed as Record<string, unknown>)[grokOidcEntryKey()]);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function hasGrokCliXaiAuth(options: { grokHome?: string } = {}): boolean {\n\treturn readGrokCliXaiOAuth(options) !== undefined;\n}\n\nexport function chooseXaiOAuth(\n\tlunr: Credential | undefined,\n\tgrok: OAuthCredential | undefined,\n): Credential | undefined {\n\tif (lunr?.type === \"api_key\") return lunr;\n\tconst lunrOAuth = lunr?.type === \"oauth\" ? lunr : undefined;\n\tif (lunrOAuth && grok) return grok.expires >= lunrOAuth.expires ? grok : lunrOAuth;\n\treturn grok ?? lunrOAuth ?? lunr;\n}\n\nfunction oauthRefreshFailed(error: unknown): boolean {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn /invalid_grant|oauth refresh failed/i.test(parts.join(\" \"));\n}\n\nfunction shouldAdopt(lunr: Credential | undefined, chosen: Credential | undefined): boolean {\n\tif (!chosen || chosen.type !== \"oauth\") return false;\n\tif (!lunr || lunr.type !== \"oauth\") return true;\n\treturn lunr.access !== chosen.access || lunr.refresh !== chosen.refresh;\n}\n\nexport function writeGrokCliXaiOAuth(\n\tcredential: OAuthCredential,\n\toptions: { grokHome?: string; onlyIfFileExists?: boolean } = {},\n): boolean {\n\tconst authPath = grokAuthPath(options.grokHome);\n\tif (options.onlyIfFileExists !== false && !existsSync(authPath)) return false;\n\n\tconst dir = dirname(authPath);\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n\tif (!existsSync(authPath)) {\n\t\twriteFileSync(authPath, \"{}\\n\", AUTH_FILE_WRITE_OPTIONS);\n\t\tchmodSync(authPath, 0o600);\n\t}\n\n\tlet release: (() => void) | undefined;\n\ttry {\n\t\trelease = lockfile.lockSync(authPath, { realpath: false });\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(readFileSync(authPath, \"utf-8\")) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\tdata = {};\n\t\t}\n\t\tconst key = grokOidcEntryKey();\n\t\tconst previous =\n\t\t\tdata[key] && typeof data[key] === \"object\" && !Array.isArray(data[key])\n\t\t\t\t? (data[key] as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tdata[key] = {\n\t\t\t...previous,\n\t\t\tkey: credential.access,\n\t\t\trefresh_token: credential.refresh,\n\t\t\texpires_at: credential.expires,\n\t\t};\n\t\twriteFileSync(authPath, `${JSON.stringify(data, null, 2)}\\n`, AUTH_FILE_WRITE_OPTIONS);\n\t\tchmodSync(authPath, 0o600);\n\t\treturn true;\n\t} finally {\n\t\tif (release) {\n\t\t\ttry {\n\t\t\t\trelease();\n\t\t\t} catch {\n\t\t\t\t// Unlock is best-effort.\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function wrapXaiGrokCliCredentials(\n\tstore: CredentialStore,\n\toptions: { grokHome?: string } = {},\n): CredentialStore {\n\tconst grokHome = options.grokHome;\n\treturn {\n\t\tasync read(providerId: string): Promise<Credential | undefined> {\n\t\t\tconst stored = await store.read(providerId);\n\t\t\tif (providerId !== XAI_PROVIDER_ID) return stored;\n\t\t\tif (stored?.type === \"api_key\") return stored;\n\t\t\tconst grok = readGrokCliXaiOAuth({ grokHome });\n\t\t\tconst chosen = chooseXaiOAuth(stored, grok);\n\t\t\tif (shouldAdopt(stored, chosen) && chosen?.type === \"oauth\") {\n\t\t\t\ttry {\n\t\t\t\t\tawait store.modify(providerId, async () => chosen);\n\t\t\t\t} catch {\n\t\t\t\t\t// Read still returns the live Grok token if persist fails.\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chosen;\n\t\t},\n\t\tasync list(): Promise<readonly CredentialInfo[]> {\n\t\t\tconst entries = new Map((await store.list()).map((entry) => [entry.providerId, entry]));\n\t\t\tif (!entries.has(XAI_PROVIDER_ID) && hasGrokCliXaiAuth({ grokHome })) {\n\t\t\t\tentries.set(XAI_PROVIDER_ID, { providerId: XAI_PROVIDER_ID, type: \"oauth\" });\n\t\t\t}\n\t\t\treturn [...entries.values()];\n\t\t},\n\t\tmodify(providerId, fn) {\n\t\t\tif (providerId !== XAI_PROVIDER_ID) return store.modify(providerId, fn);\n\t\t\treturn store.modify(providerId, async (lunrCurrent) => {\n\t\t\t\tif (lunrCurrent?.type === \"api_key\") return fn(lunrCurrent);\n\t\t\t\tconst grok = readGrokCliXaiOAuth({ grokHome });\n\t\t\t\tconst chosen = chooseXaiOAuth(lunrCurrent, grok);\n\t\t\t\tlet next: Credential | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tnext = await fn(chosen);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!oauthRefreshFailed(error) || !grok || chosen === grok) throw error;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnext = await fn(grok);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst persist = next ?? (shouldAdopt(lunrCurrent, chosen) ? chosen : undefined);\n\t\t\t\tif (persist?.type === \"oauth\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twriteGrokCliXaiOAuth(persist, { grokHome, onlyIfFileExists: true });\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// lunR persist still happens; Grok file update is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn persist;\n\t\t\t});\n\t\t},\n\t\tdelete(providerId: string): Promise<void> {\n\t\t\treturn store.delete(providerId);\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Official Grok CLI SuperGrok session (`~/.grok/auth.json`).
3
+ *
4
+ * lunR and Grok CLI share xAI client `b1a00492-073a-47ea-816f-4c329264a828`.
5
+ * A later `grok login` revokes lunR's refresh token; this module reads the
6
+ * still-live Grok CLI entry and write-throughs rotated tokens so the two
7
+ * CLIs do not fight.
8
+ */
9
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ import lockfile from "proper-lockfile";
13
+ export const XAI_PROVIDER_ID = "xai";
14
+ /** Same client as packages/ai/src/auth/oauth/xai.ts `XAI_CLIENT_ID`. */
15
+ export const XAI_GROK_CLI_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
16
+ const AUTH_FILE_WRITE_OPTIONS = { encoding: "utf-8", mode: 0o600 };
17
+ export function grokHomeDir(grokHome) {
18
+ if (grokHome)
19
+ return grokHome;
20
+ const fromEnv = process.env.GROK_HOME?.trim();
21
+ if (fromEnv)
22
+ return fromEnv;
23
+ return join(homedir(), ".grok");
24
+ }
25
+ export function grokAuthPath(grokHome) {
26
+ return join(grokHomeDir(grokHome), "auth.json");
27
+ }
28
+ export function grokOidcEntryKey(clientId = XAI_GROK_CLI_CLIENT_ID) {
29
+ return `https://auth.x.ai::${clientId}`;
30
+ }
31
+ export function parseGrokExpiresAt(value) {
32
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
33
+ return value < 1e12 ? Math.round(value * 1000) : Math.round(value);
34
+ }
35
+ if (typeof value === "string" && value.trim()) {
36
+ const numeric = Number(value);
37
+ if (Number.isFinite(numeric) && numeric > 0) {
38
+ return numeric < 1e12 ? Math.round(numeric * 1000) : Math.round(numeric);
39
+ }
40
+ const parsed = Date.parse(value);
41
+ if (Number.isFinite(parsed))
42
+ return parsed;
43
+ }
44
+ return undefined;
45
+ }
46
+ export function grokEntryToOAuth(entry) {
47
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
48
+ return undefined;
49
+ const record = entry;
50
+ const access = typeof record.key === "string" ? record.key : undefined;
51
+ const refresh = typeof record.refresh_token === "string" ? record.refresh_token : undefined;
52
+ const expires = parseGrokExpiresAt(record.expires_at);
53
+ if (!access || !refresh || expires === undefined)
54
+ return undefined;
55
+ return { type: "oauth", access, refresh, expires };
56
+ }
57
+ export function readGrokCliXaiOAuth(options = {}) {
58
+ try {
59
+ const raw = readFileSync(grokAuthPath(options.grokHome), "utf-8");
60
+ const parsed = JSON.parse(raw);
61
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
62
+ return undefined;
63
+ return grokEntryToOAuth(parsed[grokOidcEntryKey()]);
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ export function hasGrokCliXaiAuth(options = {}) {
70
+ return readGrokCliXaiOAuth(options) !== undefined;
71
+ }
72
+ export function chooseXaiOAuth(lunr, grok) {
73
+ if (lunr?.type === "api_key")
74
+ return lunr;
75
+ const lunrOAuth = lunr?.type === "oauth" ? lunr : undefined;
76
+ if (lunrOAuth && grok)
77
+ return grok.expires >= lunrOAuth.expires ? grok : lunrOAuth;
78
+ return grok ?? lunrOAuth ?? lunr;
79
+ }
80
+ function oauthRefreshFailed(error) {
81
+ const parts = [];
82
+ let current = error;
83
+ const seen = new Set();
84
+ while (current && typeof current === "object" && !seen.has(current)) {
85
+ seen.add(current);
86
+ if (current instanceof Error) {
87
+ parts.push(current.message);
88
+ current = current.cause;
89
+ continue;
90
+ }
91
+ break;
92
+ }
93
+ return /invalid_grant|oauth refresh failed/i.test(parts.join(" "));
94
+ }
95
+ function shouldAdopt(lunr, chosen) {
96
+ if (!chosen || chosen.type !== "oauth")
97
+ return false;
98
+ if (!lunr || lunr.type !== "oauth")
99
+ return true;
100
+ return lunr.access !== chosen.access || lunr.refresh !== chosen.refresh;
101
+ }
102
+ export function writeGrokCliXaiOAuth(credential, options = {}) {
103
+ const authPath = grokAuthPath(options.grokHome);
104
+ if (options.onlyIfFileExists !== false && !existsSync(authPath))
105
+ return false;
106
+ const dir = dirname(authPath);
107
+ if (!existsSync(dir))
108
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
109
+ if (!existsSync(authPath)) {
110
+ writeFileSync(authPath, "{}\n", AUTH_FILE_WRITE_OPTIONS);
111
+ chmodSync(authPath, 0o600);
112
+ }
113
+ let release;
114
+ try {
115
+ release = lockfile.lockSync(authPath, { realpath: false });
116
+ let data = {};
117
+ try {
118
+ const parsed = JSON.parse(readFileSync(authPath, "utf-8"));
119
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
120
+ data = parsed;
121
+ }
122
+ }
123
+ catch {
124
+ data = {};
125
+ }
126
+ const key = grokOidcEntryKey();
127
+ const previous = data[key] && typeof data[key] === "object" && !Array.isArray(data[key])
128
+ ? data[key]
129
+ : {};
130
+ data[key] = {
131
+ ...previous,
132
+ key: credential.access,
133
+ refresh_token: credential.refresh,
134
+ expires_at: credential.expires,
135
+ };
136
+ writeFileSync(authPath, `${JSON.stringify(data, null, 2)}\n`, AUTH_FILE_WRITE_OPTIONS);
137
+ chmodSync(authPath, 0o600);
138
+ return true;
139
+ }
140
+ finally {
141
+ if (release) {
142
+ try {
143
+ release();
144
+ }
145
+ catch {
146
+ // Unlock is best-effort.
147
+ }
148
+ }
149
+ }
150
+ }
151
+ export function wrapXaiGrokCliCredentials(store, options = {}) {
152
+ const grokHome = options.grokHome;
153
+ return {
154
+ async read(providerId) {
155
+ const stored = await store.read(providerId);
156
+ if (providerId !== XAI_PROVIDER_ID)
157
+ return stored;
158
+ if (stored?.type === "api_key")
159
+ return stored;
160
+ const grok = readGrokCliXaiOAuth({ grokHome });
161
+ const chosen = chooseXaiOAuth(stored, grok);
162
+ if (shouldAdopt(stored, chosen) && chosen?.type === "oauth") {
163
+ try {
164
+ await store.modify(providerId, async () => chosen);
165
+ }
166
+ catch {
167
+ // Read still returns the live Grok token if persist fails.
168
+ }
169
+ }
170
+ return chosen;
171
+ },
172
+ async list() {
173
+ const entries = new Map((await store.list()).map((entry) => [entry.providerId, entry]));
174
+ if (!entries.has(XAI_PROVIDER_ID) && hasGrokCliXaiAuth({ grokHome })) {
175
+ entries.set(XAI_PROVIDER_ID, { providerId: XAI_PROVIDER_ID, type: "oauth" });
176
+ }
177
+ return [...entries.values()];
178
+ },
179
+ modify(providerId, fn) {
180
+ if (providerId !== XAI_PROVIDER_ID)
181
+ return store.modify(providerId, fn);
182
+ return store.modify(providerId, async (lunrCurrent) => {
183
+ if (lunrCurrent?.type === "api_key")
184
+ return fn(lunrCurrent);
185
+ const grok = readGrokCliXaiOAuth({ grokHome });
186
+ const chosen = chooseXaiOAuth(lunrCurrent, grok);
187
+ let next;
188
+ try {
189
+ next = await fn(chosen);
190
+ }
191
+ catch (error) {
192
+ if (!oauthRefreshFailed(error) || !grok || chosen === grok)
193
+ throw error;
194
+ try {
195
+ next = await fn(grok);
196
+ }
197
+ catch {
198
+ throw error;
199
+ }
200
+ }
201
+ const persist = next ?? (shouldAdopt(lunrCurrent, chosen) ? chosen : undefined);
202
+ if (persist?.type === "oauth") {
203
+ try {
204
+ writeGrokCliXaiOAuth(persist, { grokHome, onlyIfFileExists: true });
205
+ }
206
+ catch {
207
+ // lunR persist still happens; Grok file update is best-effort.
208
+ }
209
+ }
210
+ return persist;
211
+ });
212
+ },
213
+ delete(providerId) {
214
+ return store.delete(providerId);
215
+ },
216
+ };
217
+ }
218
+ //# sourceMappingURL=grok-cli-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grok-cli-auth.js","sourceRoot":"","sources":["../../src/core/grok-cli-auth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Official Grok CLI SuperGrok session (`~/.grok/auth.json`).\n *\n * lunR and Grok CLI share xAI client `b1a00492-073a-47ea-816f-4c329264a828`.\n * A later `grok login` revokes lunR's refresh token; this module reads the\n * still-live Grok CLI entry and write-throughs rotated tokens so the two\n * CLIs do not fight.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { Credential, CredentialInfo, CredentialStore, OAuthCredential } from \"@ashx-j/lunr-ai\";\nimport lockfile from \"proper-lockfile\";\n\nexport const XAI_PROVIDER_ID = \"xai\";\n/** Same client as packages/ai/src/auth/oauth/xai.ts `XAI_CLIENT_ID`. */\nexport const XAI_GROK_CLI_CLIENT_ID = \"b1a00492-073a-47ea-816f-4c329264a828\";\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport function grokHomeDir(grokHome?: string): string {\n\tif (grokHome) return grokHome;\n\tconst fromEnv = process.env.GROK_HOME?.trim();\n\tif (fromEnv) return fromEnv;\n\treturn join(homedir(), \".grok\");\n}\n\nexport function grokAuthPath(grokHome?: string): string {\n\treturn join(grokHomeDir(grokHome), \"auth.json\");\n}\n\nexport function grokOidcEntryKey(clientId: string = XAI_GROK_CLI_CLIENT_ID): string {\n\treturn `https://auth.x.ai::${clientId}`;\n}\n\nexport function parseGrokExpiresAt(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n\t\treturn value < 1e12 ? Math.round(value * 1000) : Math.round(value);\n\t}\n\tif (typeof value === \"string\" && value.trim()) {\n\t\tconst numeric = Number(value);\n\t\tif (Number.isFinite(numeric) && numeric > 0) {\n\t\t\treturn numeric < 1e12 ? Math.round(numeric * 1000) : Math.round(numeric);\n\t\t}\n\t\tconst parsed = Date.parse(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn undefined;\n}\n\nexport function grokEntryToOAuth(entry: unknown): OAuthCredential | undefined {\n\tif (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return undefined;\n\tconst record = entry as Record<string, unknown>;\n\tconst access = typeof record.key === \"string\" ? record.key : undefined;\n\tconst refresh = typeof record.refresh_token === \"string\" ? record.refresh_token : undefined;\n\tconst expires = parseGrokExpiresAt(record.expires_at);\n\tif (!access || !refresh || expires === undefined) return undefined;\n\treturn { type: \"oauth\", access, refresh, expires };\n}\n\nexport function readGrokCliXaiOAuth(options: { grokHome?: string } = {}): OAuthCredential | undefined {\n\ttry {\n\t\tconst raw = readFileSync(grokAuthPath(options.grokHome), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\treturn grokEntryToOAuth((parsed as Record<string, unknown>)[grokOidcEntryKey()]);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function hasGrokCliXaiAuth(options: { grokHome?: string } = {}): boolean {\n\treturn readGrokCliXaiOAuth(options) !== undefined;\n}\n\nexport function chooseXaiOAuth(\n\tlunr: Credential | undefined,\n\tgrok: OAuthCredential | undefined,\n): Credential | undefined {\n\tif (lunr?.type === \"api_key\") return lunr;\n\tconst lunrOAuth = lunr?.type === \"oauth\" ? lunr : undefined;\n\tif (lunrOAuth && grok) return grok.expires >= lunrOAuth.expires ? grok : lunrOAuth;\n\treturn grok ?? lunrOAuth ?? lunr;\n}\n\nfunction oauthRefreshFailed(error: unknown): boolean {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn /invalid_grant|oauth refresh failed/i.test(parts.join(\" \"));\n}\n\nfunction shouldAdopt(lunr: Credential | undefined, chosen: Credential | undefined): boolean {\n\tif (!chosen || chosen.type !== \"oauth\") return false;\n\tif (!lunr || lunr.type !== \"oauth\") return true;\n\treturn lunr.access !== chosen.access || lunr.refresh !== chosen.refresh;\n}\n\nexport function writeGrokCliXaiOAuth(\n\tcredential: OAuthCredential,\n\toptions: { grokHome?: string; onlyIfFileExists?: boolean } = {},\n): boolean {\n\tconst authPath = grokAuthPath(options.grokHome);\n\tif (options.onlyIfFileExists !== false && !existsSync(authPath)) return false;\n\n\tconst dir = dirname(authPath);\n\tif (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n\tif (!existsSync(authPath)) {\n\t\twriteFileSync(authPath, \"{}\\n\", AUTH_FILE_WRITE_OPTIONS);\n\t\tchmodSync(authPath, 0o600);\n\t}\n\n\tlet release: (() => void) | undefined;\n\ttry {\n\t\trelease = lockfile.lockSync(authPath, { realpath: false });\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(readFileSync(authPath, \"utf-8\")) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\tdata = {};\n\t\t}\n\t\tconst key = grokOidcEntryKey();\n\t\tconst previous =\n\t\t\tdata[key] && typeof data[key] === \"object\" && !Array.isArray(data[key])\n\t\t\t\t? (data[key] as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tdata[key] = {\n\t\t\t...previous,\n\t\t\tkey: credential.access,\n\t\t\trefresh_token: credential.refresh,\n\t\t\texpires_at: credential.expires,\n\t\t};\n\t\twriteFileSync(authPath, `${JSON.stringify(data, null, 2)}\\n`, AUTH_FILE_WRITE_OPTIONS);\n\t\tchmodSync(authPath, 0o600);\n\t\treturn true;\n\t} finally {\n\t\tif (release) {\n\t\t\ttry {\n\t\t\t\trelease();\n\t\t\t} catch {\n\t\t\t\t// Unlock is best-effort.\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function wrapXaiGrokCliCredentials(\n\tstore: CredentialStore,\n\toptions: { grokHome?: string } = {},\n): CredentialStore {\n\tconst grokHome = options.grokHome;\n\treturn {\n\t\tasync read(providerId: string): Promise<Credential | undefined> {\n\t\t\tconst stored = await store.read(providerId);\n\t\t\tif (providerId !== XAI_PROVIDER_ID) return stored;\n\t\t\tif (stored?.type === \"api_key\") return stored;\n\t\t\tconst grok = readGrokCliXaiOAuth({ grokHome });\n\t\t\tconst chosen = chooseXaiOAuth(stored, grok);\n\t\t\tif (shouldAdopt(stored, chosen) && chosen?.type === \"oauth\") {\n\t\t\t\ttry {\n\t\t\t\t\tawait store.modify(providerId, async () => chosen);\n\t\t\t\t} catch {\n\t\t\t\t\t// Read still returns the live Grok token if persist fails.\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chosen;\n\t\t},\n\t\tasync list(): Promise<readonly CredentialInfo[]> {\n\t\t\tconst entries = new Map((await store.list()).map((entry) => [entry.providerId, entry]));\n\t\t\tif (!entries.has(XAI_PROVIDER_ID) && hasGrokCliXaiAuth({ grokHome })) {\n\t\t\t\tentries.set(XAI_PROVIDER_ID, { providerId: XAI_PROVIDER_ID, type: \"oauth\" });\n\t\t\t}\n\t\t\treturn [...entries.values()];\n\t\t},\n\t\tmodify(providerId, fn) {\n\t\t\tif (providerId !== XAI_PROVIDER_ID) return store.modify(providerId, fn);\n\t\t\treturn store.modify(providerId, async (lunrCurrent) => {\n\t\t\t\tif (lunrCurrent?.type === \"api_key\") return fn(lunrCurrent);\n\t\t\t\tconst grok = readGrokCliXaiOAuth({ grokHome });\n\t\t\t\tconst chosen = chooseXaiOAuth(lunrCurrent, grok);\n\t\t\t\tlet next: Credential | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tnext = await fn(chosen);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!oauthRefreshFailed(error) || !grok || chosen === grok) throw error;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnext = await fn(grok);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst persist = next ?? (shouldAdopt(lunrCurrent, chosen) ? chosen : undefined);\n\t\t\t\tif (persist?.type === \"oauth\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twriteGrokCliXaiOAuth(persist, { grokHome, onlyIfFileExists: true });\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// lunR persist still happens; Grok file update is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn persist;\n\t\t\t});\n\t\t},\n\t\tdelete(providerId: string): Promise<void> {\n\t\t\treturn store.delete(providerId);\n\t\t},\n\t};\n}\n"]}
@@ -18,6 +18,12 @@ export interface CreateModelRuntimeOptions {
18
18
  catalogBaseUrl?: string;
19
19
  /** lunr: subscription-key pool. Defaults to subscriptions.json next to authPath (in-memory for custom stores). */
20
20
  subscriptions?: SubscriptionManager;
21
+ /**
22
+ * Grok CLI home (`~/.grok`) used as a SuperGrok OAuth fallback.
23
+ * `null` disables the fallback. Custom `credentials` default to disabled
24
+ * unless this is an explicit path.
25
+ */
26
+ grokHome?: string | null;
21
27
  }
22
28
  export interface CatalogRefreshResult extends ModelsRefreshResult {
23
29
  official?: OfficialCatalogLoadResult & {
@@ -51,6 +57,7 @@ export declare class ModelRuntime implements Models {
51
57
  private readonly userModels;
52
58
  private readonly officialCachePath;
53
59
  private lastCatalogRefresh;
60
+ private readonly grokHome;
54
61
  private constructor();
55
62
  static create(options?: CreateModelRuntimeOptions): Promise<ModelRuntime>;
56
63
  private configureRadiusProviders;
@@ -101,6 +108,8 @@ export declare class ModelRuntime implements Models {
101
108
  completeSimple(model: Model<Api>, context: Context, options?: ModelsSimpleStreamOptions): Promise<AssistantMessage>;
102
109
  login(providerId: string, type: AuthType, interaction: AuthInteraction): Promise<Credential>;
103
110
  logout(providerId: string): Promise<void>;
111
+ /** Copy a live Grok CLI SuperGrok session into lunR `auth.json`. */
112
+ importGrokCliXaiAuth(): Promise<Credential | undefined>;
104
113
  reloadConfig(): Promise<void>;
105
114
  refresh(options?: ModelsRefreshOptions): Promise<CatalogRefreshResult>;
106
115
  getLastCatalogRefresh(): CatalogRefreshResult;