@ashx-j/lunr 0.2.12 → 0.2.14

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 (225) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +6 -3
  3. package/dist/builtin-extensions/ashxj-tui.d.ts +16 -1
  4. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-tui.js +44 -20
  6. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  7. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +2 -0
  8. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  9. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +27 -22
  10. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  11. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +5 -2
  13. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  14. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +12 -1
  16. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts +1 -0
  18. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts.map +1 -1
  19. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js +3 -2
  20. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.d.ts.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js +1 -0
  23. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  25. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +3 -0
  26. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js +3 -3
  29. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js +2 -2
  32. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +3 -0
  35. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +7 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +2 -0
  41. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +1 -0
  43. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  44. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +19 -3
  45. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +10 -1
  47. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +19 -0
  49. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
  51. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +2 -1
  54. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +17 -0
  56. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +3 -1
  58. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +14 -3
  60. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +14 -0
  62. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
  65. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +2 -2
  66. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
  67. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  68. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +12 -10
  69. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  70. package/dist/catalog/models.json +1 -1
  71. package/dist/catalog/providers/amazon-bedrock.json +1 -1
  72. package/dist/catalog/providers/anthropic.json +1 -1
  73. package/dist/catalog/providers/azure-openai-responses.json +1 -1
  74. package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
  75. package/dist/catalog/providers/cloudflare-workers-ai.json +1 -1
  76. package/dist/catalog/providers/fireworks.json +1 -1
  77. package/dist/catalog/providers/github-copilot.json +1 -1
  78. package/dist/catalog/providers/google-vertex.json +1 -1
  79. package/dist/catalog/providers/google.json +1 -1
  80. package/dist/catalog/providers/groq.json +1 -1
  81. package/dist/catalog/providers/huggingface.json +1 -1
  82. package/dist/catalog/providers/nvidia.json +1 -1
  83. package/dist/catalog/providers/openai-codex.json +1 -1
  84. package/dist/catalog/providers/openai.json +1 -1
  85. package/dist/catalog/providers/opencode-go.json +1 -1
  86. package/dist/catalog/providers/opencode.json +1 -1
  87. package/dist/catalog/providers/openrouter.json +1 -1
  88. package/dist/catalog/providers/together.json +1 -1
  89. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  90. package/dist/catalog/publication.json +131 -3
  91. package/dist/cli/list-models.d.ts.map +1 -1
  92. package/dist/cli/list-models.js +1 -0
  93. package/dist/cli/list-models.js.map +1 -1
  94. package/dist/cli.d.ts.map +1 -1
  95. package/dist/cli.js +52 -27
  96. package/dist/cli.js.map +1 -1
  97. package/dist/core/catalog-cache-scope.d.ts +3 -0
  98. package/dist/core/catalog-cache-scope.d.ts.map +1 -0
  99. package/dist/core/catalog-cache-scope.js +12 -0
  100. package/dist/core/catalog-cache-scope.js.map +1 -0
  101. package/dist/core/catalog-merge.d.ts +2 -0
  102. package/dist/core/catalog-merge.d.ts.map +1 -1
  103. package/dist/core/catalog-merge.js +29 -2
  104. package/dist/core/catalog-merge.js.map +1 -1
  105. package/dist/core/catalog-refresh-polling.d.ts +8 -0
  106. package/dist/core/catalog-refresh-polling.d.ts.map +1 -0
  107. package/dist/core/catalog-refresh-polling.js +30 -0
  108. package/dist/core/catalog-refresh-polling.js.map +1 -0
  109. package/dist/core/codex-catalog.d.ts +3 -0
  110. package/dist/core/codex-catalog.d.ts.map +1 -0
  111. package/dist/core/codex-catalog.js +15 -0
  112. package/dist/core/codex-catalog.js.map +1 -0
  113. package/dist/core/context-breakdown.d.ts +3 -1
  114. package/dist/core/context-breakdown.d.ts.map +1 -1
  115. package/dist/core/context-breakdown.js +5 -2
  116. package/dist/core/context-breakdown.js.map +1 -1
  117. package/dist/core/customize.d.ts +2 -4
  118. package/dist/core/customize.d.ts.map +1 -1
  119. package/dist/core/customize.js +2 -8
  120. package/dist/core/customize.js.map +1 -1
  121. package/dist/core/footer-data-provider.d.ts +3 -4
  122. package/dist/core/footer-data-provider.d.ts.map +1 -1
  123. package/dist/core/footer-data-provider.js +54 -82
  124. package/dist/core/footer-data-provider.js.map +1 -1
  125. package/dist/core/live-catalog.d.ts +5 -2
  126. package/dist/core/live-catalog.d.ts.map +1 -1
  127. package/dist/core/live-catalog.js +108 -27
  128. package/dist/core/live-catalog.js.map +1 -1
  129. package/dist/core/model-runtime.d.ts +8 -0
  130. package/dist/core/model-runtime.d.ts.map +1 -1
  131. package/dist/core/model-runtime.js +64 -12
  132. package/dist/core/model-runtime.js.map +1 -1
  133. package/dist/core/official-catalog.d.ts +4 -0
  134. package/dist/core/official-catalog.d.ts.map +1 -1
  135. package/dist/core/official-catalog.js +86 -21
  136. package/dist/core/official-catalog.js.map +1 -1
  137. package/dist/core/settings-manager.d.ts +7 -6
  138. package/dist/core/settings-manager.d.ts.map +1 -1
  139. package/dist/core/settings-manager.js +13 -17
  140. package/dist/core/settings-manager.js.map +1 -1
  141. package/dist/core/slash-commands.d.ts.map +1 -1
  142. package/dist/core/slash-commands.js +1 -0
  143. package/dist/core/slash-commands.js.map +1 -1
  144. package/dist/index.d.ts +1 -1
  145. package/dist/index.d.ts.map +1 -1
  146. package/dist/index.js.map +1 -1
  147. package/dist/main.d.ts +2 -0
  148. package/dist/main.d.ts.map +1 -1
  149. package/dist/main.js +106 -50
  150. package/dist/main.js.map +1 -1
  151. package/dist/migrations.d.ts +2 -0
  152. package/dist/migrations.d.ts.map +1 -1
  153. package/dist/migrations.js +14 -4
  154. package/dist/migrations.js.map +1 -1
  155. package/dist/modes/interactive/components/assistant-message.d.ts +1 -3
  156. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/assistant-message.js +4 -32
  158. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  159. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  160. package/dist/modes/interactive/components/custom-editor.js +2 -1
  161. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  162. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/model-selector.js +4 -1
  164. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  165. package/dist/modes/interactive/components/plan-message.d.ts +1 -3
  166. package/dist/modes/interactive/components/plan-message.d.ts.map +1 -1
  167. package/dist/modes/interactive/components/plan-message.js +1 -16
  168. package/dist/modes/interactive/components/plan-message.js.map +1 -1
  169. package/dist/modes/interactive/components/settings-selector.d.ts +3 -5
  170. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  171. package/dist/modes/interactive/components/settings-selector.js +113 -117
  172. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  173. package/dist/modes/interactive/components/user-message.d.ts +1 -3
  174. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/user-message.js +2 -19
  176. package/dist/modes/interactive/components/user-message.js.map +1 -1
  177. package/dist/modes/interactive/interactive-mode.d.ts +12 -1
  178. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-mode.js +153 -130
  180. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  181. package/dist/modes/interactive/skill-tag-autocomplete.d.ts +13 -0
  182. package/dist/modes/interactive/skill-tag-autocomplete.d.ts.map +1 -0
  183. package/dist/modes/interactive/skill-tag-autocomplete.js +72 -0
  184. package/dist/modes/interactive/skill-tag-autocomplete.js.map +1 -0
  185. package/dist/modes/interactive/theme/moon.json +0 -1
  186. package/dist/modes/interactive/theme/theme-schema.json +3 -3
  187. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  188. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  189. package/dist/modes/interactive/theme/theme.js +95 -90
  190. package/dist/modes/interactive/theme/theme.js.map +1 -1
  191. package/dist/startup/interactive-view.d.ts +55 -0
  192. package/dist/startup/interactive-view.d.ts.map +1 -0
  193. package/dist/startup/interactive-view.js +332 -0
  194. package/dist/startup/interactive-view.js.map +1 -0
  195. package/dist/startup/launch-routing.d.ts +8 -0
  196. package/dist/startup/launch-routing.d.ts.map +1 -0
  197. package/dist/startup/launch-routing.js +29 -0
  198. package/dist/startup/launch-routing.js.map +1 -0
  199. package/dist/startup/startup-milestones.d.ts +4 -0
  200. package/dist/startup/startup-milestones.d.ts.map +1 -0
  201. package/dist/startup/startup-milestones.js +14 -0
  202. package/dist/startup/startup-milestones.js.map +1 -0
  203. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  204. package/dist/utils/syntax-highlight.js +10 -4
  205. package/dist/utils/syntax-highlight.js.map +1 -1
  206. package/docs/features.md +2 -2
  207. package/docs/interactive-startup.md +58 -0
  208. package/docs/keybindings.md +1 -1
  209. package/docs/model-catalog.md +29 -0
  210. package/docs/quickstart.md +1 -1
  211. package/docs/settings.md +2 -3
  212. package/docs/skills.md +10 -0
  213. package/docs/terminal-setup.md +2 -2
  214. package/docs/usage.md +2 -1
  215. package/docs/windows.md +1 -1
  216. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  217. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  218. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  219. package/examples/extensions/gondolin/package-lock.json +2 -2
  220. package/examples/extensions/gondolin/package.json +1 -1
  221. package/examples/extensions/sandbox/package-lock.json +2 -2
  222. package/examples/extensions/sandbox/package.json +1 -1
  223. package/examples/extensions/with-deps/package-lock.json +2 -2
  224. package/examples/extensions/with-deps/package.json +1 -1
  225. package/package.json +16 -16
@@ -1 +1 @@
1
- {"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ExecFileException, execFile, spawnSync } from \"child_process\";\nimport { existsSync, type FSWatcher, readFileSync, type Stats, statSync, unwatchFile, watchFile } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitPaths(cwd: string): GitPaths | null {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\t\tconst commonDirPath = join(gitDir, \"commondir\");\n\t\t\t\t\t\tconst commonGitDir = existsSync(commonDirPath)\n\t\t\t\t\t\t\t? resolve(gitDir, readFileSync(commonDirPath, \"utf8\").trim())\n\t\t\t\t\t\t\t: gitDir;\n\t\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitSync(repoDir: string): string | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tconst branch = result.status === 0 ? result.stdout.trim() : \"\";\n\treturn branch || null;\n}\n\n/** Sum added/removed columns from `git diff --numstat`. Binary rows (`-`) are ignored. */\nexport function parseGitNumstat(stdout: string): { added: number; removed: number } {\n\tlet added = 0;\n\tlet removed = 0;\n\tfor (const line of stdout.split(/\\r?\\n/)) {\n\t\tif (!line) continue;\n\t\tconst tab1 = line.indexOf(\"\\t\");\n\t\tif (tab1 <= 0) continue;\n\t\tconst tab2 = line.indexOf(\"\\t\", tab1 + 1);\n\t\tif (tab2 < 0) continue;\n\t\tconst addTok = line.slice(0, tab1);\n\t\tconst delTok = line.slice(tab1 + 1, tab2);\n\t\tif (addTok !== \"-\") {\n\t\t\tconst n = Number(addTok);\n\t\t\tif (Number.isFinite(n)) added += n;\n\t\t}\n\t\tif (delTok !== \"-\") {\n\t\t\tconst n = Number(delTok);\n\t\t\tif (Number.isFinite(n)) removed += n;\n\t\t}\n\t}\n\treturn { added, removed };\n}\n\nfunction resolveDiffstatWithGitSync(repoDir: string): { added: number; removed: number } | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"diff\", \"--numstat\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tif (result.status !== 0) return null;\n\treturn parseGitNumstat(result.stdout ?? \"\");\n}\n\nfunction resolveDiffstatWithGitAsync(repoDir: string): Promise<{ added: number; removed: number } | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"diff\", \"--numstat\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tresolvePromise(parseGitNumstat(stdout));\n\t\t\t},\n\t\t);\n\t});\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate cachedDiffstat: { added: number; removed: number } | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined) {\n\t\t\tthis.cachedBranch = this.resolveGitBranchSync();\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Staged + unstaged added/removed vs HEAD. Null when not a repo or git failed. */\n\tgetGitDiffstat(): { added: number; removed: number } | null {\n\t\tif (this.cachedDiffstat === undefined) {\n\t\t\tthis.cachedDiffstat = this.gitPaths ? resolveDiffstatWithGitSync(this.gitPaths.repoDir) : null;\n\t\t}\n\t\treturn this.cachedDiffstat;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.cachedDiffstat = undefined;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitBranchAsync();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitBranchAsync(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\ttry {\n\t\t\tconst nextBranch = await this.resolveGitBranchAsync();\n\t\t\tconst nextDiff = this.gitPaths ? await resolveDiffstatWithGitAsync(this.gitPaths.repoDir) : null;\n\t\t\tif (this.disposed) return;\n\t\t\tconst branchChanged = this.cachedBranch !== undefined && this.cachedBranch !== nextBranch;\n\t\t\tconst prevDiff = this.cachedDiffstat;\n\t\t\tconst diffChanged =\n\t\t\t\tprevDiff !== undefined &&\n\t\t\t\t(prevDiff?.added !== nextDiff?.added || prevDiff?.removed !== nextDiff?.removed);\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t\tthis.cachedDiffstat = nextDiff;\n\t\t\tif (branchChanged || diffChanged) {\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveGitBranchSync(): string | null {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? \"detached\") : branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\" || filename === \"index\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t\"getGitBranch\" | \"getGitDiffstat\" | \"getExtensionStatuses\" | \"getAvailableProviderCount\" | \"onBranchChange\"\n>;\n"]}
1
+ {"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ExecFileException, execFile } from \"child_process\";\nimport { existsSync, type FSWatcher, type Stats, unwatchFile, watchFile } from \"fs\";\nimport { readFile, stat } from \"fs/promises\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nasync function findGitPaths(cwd: string): Promise<GitPaths | null> {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\ttry {\n\t\t\tconst gitStat = await stat(gitPath);\n\t\t\tif (gitStat.isFile()) {\n\t\t\t\tconst content = (await readFile(gitPath, \"utf8\")).trim();\n\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\tawait stat(headPath);\n\t\t\t\t\tlet commonGitDir = gitDir;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcommonGitDir = resolve(gitDir, (await readFile(join(gitDir, \"commondir\"), \"utf8\")).trim());\n\t\t\t\t\t} catch {}\n\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t}\n\t\t\t} else if (gitStat.isDirectory()) {\n\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\tawait stat(headPath);\n\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t}\n\t\t} catch {}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Sum added/removed columns from `git diff --numstat`. Binary rows (`-`) are ignored. */\nexport function parseGitNumstat(stdout: string): { added: number; removed: number } {\n\tlet added = 0;\n\tlet removed = 0;\n\tfor (const line of stdout.split(/\\r?\\n/)) {\n\t\tif (!line) continue;\n\t\tconst tab1 = line.indexOf(\"\\t\");\n\t\tif (tab1 <= 0) continue;\n\t\tconst tab2 = line.indexOf(\"\\t\", tab1 + 1);\n\t\tif (tab2 < 0) continue;\n\t\tconst addTok = line.slice(0, tab1);\n\t\tconst delTok = line.slice(tab1 + 1, tab2);\n\t\tif (addTok !== \"-\") {\n\t\t\tconst n = Number(addTok);\n\t\t\tif (Number.isFinite(n)) added += n;\n\t\t}\n\t\tif (delTok !== \"-\") {\n\t\t\tconst n = Number(delTok);\n\t\t\tif (Number.isFinite(n)) removed += n;\n\t\t}\n\t}\n\treturn { added, removed };\n}\n\nfunction resolveDiffstatWithGitAsync(repoDir: string): Promise<{ added: number; removed: number } | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"diff\", \"--numstat\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t\ttimeout: 3000,\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tresolvePromise(parseGitNumstat(stdout));\n\t\t\t},\n\t\t);\n\t});\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t\ttimeout: 3000,\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate cachedDiffstat: { added: number; removed: number } | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t}\n\n\t/** Current git branch, null until the asynchronous lookup completes or when not in a repo. */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined && !this.refreshInFlight) {\n\t\t\tvoid this.refreshGitData();\n\t\t}\n\t\treturn this.cachedBranch ?? null;\n\t}\n\n\t/** Staged + unstaged added/removed vs HEAD. Null until the asynchronous lookup completes. */\n\tgetGitDiffstat(): { added: number; removed: number } | null {\n\t\tif (this.cachedDiffstat === undefined && !this.refreshInFlight) {\n\t\t\tvoid this.refreshGitData();\n\t\t}\n\t\treturn this.cachedDiffstat ?? null;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.cachedDiffstat = undefined;\n\t\tthis.gitPaths = undefined;\n\t\tif (this.refreshInFlight) this.refreshPending = true;\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitData();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitData(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\tconst cwd = this.cwd;\n\t\ttry {\n\t\t\tconst gitPaths = this.gitPaths === undefined ? await findGitPaths(cwd) : this.gitPaths;\n\t\t\tif (this.disposed || this.cwd !== cwd) return;\n\t\t\tif (this.gitPaths === undefined) {\n\t\t\t\tthis.gitPaths = gitPaths;\n\t\t\t\tthis.setupGitWatcher();\n\t\t\t}\n\t\t\tconst [nextBranch, nextDiff] = await Promise.all([\n\t\t\t\tthis.resolveGitBranchAsync(),\n\t\t\t\tgitPaths ? resolveDiffstatWithGitAsync(gitPaths.repoDir) : null,\n\t\t\t]);\n\t\t\tif (this.disposed || this.cwd !== cwd) return;\n\t\t\tconst prevBranch = this.cachedBranch;\n\t\t\tconst prevDiff = this.cachedDiffstat;\n\t\t\tconst branchChanged = prevBranch !== nextBranch;\n\t\t\tconst diffChanged = prevDiff?.added !== nextDiff?.added || prevDiff?.removed !== nextDiff?.removed;\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t\tthis.cachedDiffstat = nextDiff;\n\t\t\tif (branchChanged || diffChanged) {\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = (await readFile(this.gitPaths.headPath, \"utf8\")).trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\" || filename === \"index\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t\"getGitBranch\" | \"getGitDiffstat\" | \"getExtensionStatuses\" | \"getAvailableProviderCount\" | \"onBranchChange\"\n>;\n"]}
@@ -1,9 +1,9 @@
1
- import type { Api, Credential, Model } from "@ashx-j/lunr-ai";
1
+ import { type Api, type Credential, type Model } from "@ashx-j/lunr-ai";
2
2
  export declare const LIVE_CATALOG_TIMEOUT_MS = 4000;
3
3
  export declare const INCOMPLETE_PROMPT_CAP = 8;
4
4
  export declare const DEFAULT_CONTEXT_WINDOW = 128000;
5
5
  export declare const DEFAULT_MAX_TOKENS = 8192;
6
- export declare const LIVE_LIST_PROVIDER_IDS: readonly ["xai", "openrouter", "qwen-cloud", "qwen-cloud-cn", "groq", "openai", "deepseek"];
6
+ export declare const LIVE_LIST_PROVIDER_IDS: readonly ["openai-codex", "xai", "openrouter", "qwen-cloud", "qwen-cloud-cn", "groq", "openai", "deepseek"];
7
7
  export type LiveListProviderId = (typeof LIVE_LIST_PROVIDER_IDS)[number];
8
8
  export declare function isLiveListProvider(providerId: string): providerId is LiveListProviderId;
9
9
  export interface LiveFieldSupply {
@@ -28,6 +28,7 @@ export interface IncompleteLiveModel {
28
28
  }
29
29
  export type LiveCatalogStatus = "ok" | "timeout" | "error" | "skipped";
30
30
  export interface LiveCatalogResult {
31
+ discoveryClientVersion?: string;
31
32
  providerId: string;
32
33
  status: LiveCatalogStatus;
33
34
  discoveries: LiveModelDiscovery[];
@@ -41,6 +42,7 @@ export declare function credentialAuthHeaders(credential: Credential | undefined
41
42
  /** OpenAI `{ data: [{ id, ... }] }` plus a bare array. */
42
43
  export declare function parseOpenAIModelsList(value: unknown): Record<string, unknown>[];
43
44
  export declare function firstBakedInModel(models: readonly Model<Api>[]): Model<Api> | undefined;
45
+ export declare function selectLiveTemplate(providerId: string, id: string, bakedIn: readonly Model<Api>[]): Model<Api> | undefined;
44
46
  export declare function uniqueApis(models: readonly Model<Api>[]): Api[];
45
47
  export declare function synthesizeLiveModel(id: string, template: Model<Api>, raw?: Record<string, unknown>): LiveModelDiscovery;
46
48
  /** OpenRouter field mapping (tools filter, pricing × 1e6, context) from generate-models. */
@@ -61,6 +63,7 @@ export interface IncompleteModelAnswers {
61
63
  }
62
64
  export declare function applyIncompleteAnswers(draft: Model<Api>, answers: IncompleteModelAnswers): Model<Api>;
63
65
  export interface FetchLiveProviderModelsOptions {
66
+ discoveryClientVersion?: string;
64
67
  providerId: string;
65
68
  baseUrl: string;
66
69
  bakedIn: readonly Model<Api>[];
@@ -1 +1 @@
1
- {"version":3,"file":"live-catalog.d.ts","sourceRoot":"","sources":["../../src/core/live-catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Credential, Model } from \"@ashx-j/lunr-ai\";\nimport { VERSION } from \"../config.ts\";\nimport { getPiUserAgent } from \"../utils/pi-user-agent.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const LIVE_CATALOG_TIMEOUT_MS = 4000;\nexport const INCOMPLETE_PROMPT_CAP = 8;\nexport const DEFAULT_CONTEXT_WINDOW = 128000;\nexport const DEFAULT_MAX_TOKENS = 8192;\n\nexport const LIVE_LIST_PROVIDER_IDS = [\n\t\"xai\",\n\t\"openrouter\",\n\t\"qwen-cloud\",\n\t\"qwen-cloud-cn\",\n\t\"groq\",\n\t\"openai\",\n\t\"deepseek\",\n] as const;\n\nexport type LiveListProviderId = (typeof LIVE_LIST_PROVIDER_IDS)[number];\n\nconst LIVE_LIST_PROVIDER_SET = new Set<string>(LIVE_LIST_PROVIDER_IDS);\n\nexport function isLiveListProvider(providerId: string): providerId is LiveListProviderId {\n\treturn LIVE_LIST_PROVIDER_SET.has(providerId);\n}\n\nexport interface LiveFieldSupply {\n\tcontextWindow: boolean;\n\tmaxTokens: boolean;\n\tinput: boolean;\n\tname: boolean;\n\tcost: boolean;\n\treasoning: boolean;\n}\n\nexport interface LiveModelDiscovery {\n\tmodel: Model<Api>;\n\tsupplied: LiveFieldSupply;\n}\n\nexport interface IncompleteLiveModel {\n\tprovider: string;\n\tid: string;\n\tname: string;\n\tmissing: Array<\"contextWindow\" | \"maxTokens\" | \"input\">;\n\tdraft: Model<Api>;\n\tavailableApis: Api[];\n}\n\nexport type LiveCatalogStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\";\n\nexport interface LiveCatalogResult {\n\tproviderId: string;\n\tstatus: LiveCatalogStatus;\n\tdiscoveries: LiveModelDiscovery[];\n\tadded: number;\n\ttotal: number;\n\tincomplete: IncompleteLiveModel[];\n\terror?: string;\n}\n\nconst ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\nconst NONE_SUPPLIED: LiveFieldSupply = {\n\tcontextWindow: false,\n\tmaxTokens: false,\n\tinput: false,\n\tname: false,\n\tcost: false,\n\treasoning: false,\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"string\" && value.trim()) {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn undefined;\n}\n\nfunction roundCost(value: number): number {\n\treturn Number(value.toFixed(6));\n}\n\nexport function modelsListUrl(baseUrl: string): string {\n\treturn `${baseUrl.replace(/\\/+$/u, \"\")}/models`;\n}\n\nexport function credentialAuthHeaders(credential: Credential | undefined): Record<string, string> {\n\tif (!credential) return {};\n\tif (credential.type === \"api_key\" && credential.key) {\n\t\treturn { Authorization: `Bearer ${credential.key}` };\n\t}\n\tif (credential.type === \"oauth\" && typeof credential.access === \"string\" && credential.access) {\n\t\treturn { Authorization: `Bearer ${credential.access}` };\n\t}\n\treturn {};\n}\n\n/** OpenAI `{ data: [{ id, ... }] }` plus a bare array. */\nexport function parseOpenAIModelsList(value: unknown): Record<string, unknown>[] {\n\tconst entries = isRecord(value) && Array.isArray(value.data) ? value.data : Array.isArray(value) ? value : [];\n\tconst rows: Record<string, unknown>[] = [];\n\tfor (const entry of entries) {\n\t\tif (typeof entry === \"string\" && entry.trim()) {\n\t\t\trows.push({ id: entry.trim() });\n\t\t\tcontinue;\n\t\t}\n\t\tif (isRecord(entry) && typeof entry.id === \"string\" && entry.id.trim()) rows.push(entry);\n\t}\n\treturn rows;\n}\n\nexport function firstBakedInModel(models: readonly Model<Api>[]): Model<Api> | undefined {\n\treturn models[0];\n}\n\nexport function uniqueApis(models: readonly Model<Api>[]): Api[] {\n\tconst seen = new Set<Api>();\n\tconst apis: Api[] = [];\n\tfor (const model of models) {\n\t\tif (seen.has(model.api)) continue;\n\t\tseen.add(model.api);\n\t\tapis.push(model.api);\n\t}\n\treturn apis;\n}\n\nexport function synthesizeLiveModel(\n\tid: string,\n\ttemplate: Model<Api>,\n\traw: Record<string, unknown> = {},\n): LiveModelDiscovery {\n\tconst name = typeof raw.name === \"string\" && raw.name.trim() ? raw.name.trim() : id;\n\treturn {\n\t\tmodel: {\n\t\t\tid,\n\t\t\tname,\n\t\t\tapi: template.api,\n\t\t\tprovider: template.provider,\n\t\t\tbaseUrl: template.baseUrl,\n\t\t\treasoning: template.reasoning,\n\t\t\tinput: template.input ?? [\"text\"],\n\t\t\tcost: ZERO_COST,\n\t\t\tcontextWindow: DEFAULT_CONTEXT_WINDOW,\n\t\t\tmaxTokens: DEFAULT_MAX_TOKENS,\n\t\t\tcompat: template.compat,\n\t\t\tthinkingLevelMap: template.thinkingLevelMap,\n\t\t},\n\t\tsupplied: {\n\t\t\t...NONE_SUPPLIED,\n\t\t\tname: typeof raw.name === \"string\" && raw.name.trim().length > 0,\n\t\t},\n\t};\n}\n\n/** OpenRouter field mapping (tools filter, pricing × 1e6, context) from generate-models. */\nexport function mapOpenRouterModel(raw: Record<string, unknown>, template: Model<Api>): LiveModelDiscovery | undefined {\n\tconst id = typeof raw.id === \"string\" ? raw.id.trim() : \"\";\n\tif (!id) return undefined;\n\tconst supported = Array.isArray(raw.supported_parameters) ? raw.supported_parameters : [];\n\tif (!supported.includes(\"tools\")) return undefined;\n\n\tconst architecture = isRecord(raw.architecture) ? raw.architecture : undefined;\n\tconst modality = typeof architecture?.modality === \"string\" ? architecture.modality : \"\";\n\tconst input: (\"text\" | \"image\")[] = [\"text\"];\n\tif (modality.includes(\"image\")) input.push(\"image\");\n\n\tconst pricing = isRecord(raw.pricing) ? raw.pricing : undefined;\n\tconst inputCost = roundCost((asFiniteNumber(pricing?.prompt) ?? 0) * 1_000_000);\n\tconst outputCost = roundCost((asFiniteNumber(pricing?.completion) ?? 0) * 1_000_000);\n\tconst cacheReadCost = roundCost((asFiniteNumber(pricing?.input_cache_read) ?? 0) * 1_000_000);\n\tconst cacheWriteCost = roundCost((asFiniteNumber(pricing?.input_cache_write) ?? 0) * 1_000_000);\n\n\tconst topProvider = isRecord(raw.top_provider) ? raw.top_provider : undefined;\n\tconst contextWindow = asFiniteNumber(topProvider?.context_length) ?? asFiniteNumber(raw.context_length);\n\tconst maxTokens = asFiniteNumber(topProvider?.max_completion_tokens) ?? asFiniteNumber(raw.max_completion_tokens);\n\n\tconst name = typeof raw.name === \"string\" && raw.name.trim() ? raw.name.trim() : id;\n\treturn {\n\t\tmodel: {\n\t\t\tid,\n\t\t\tname,\n\t\t\tapi: template.api,\n\t\t\tprovider: template.provider,\n\t\t\tbaseUrl: template.baseUrl,\n\t\t\treasoning: supported.includes(\"reasoning\") || template.reasoning,\n\t\t\tinput,\n\t\t\tcost: {\n\t\t\t\tinput: inputCost,\n\t\t\t\toutput: outputCost,\n\t\t\t\tcacheRead: cacheReadCost,\n\t\t\t\tcacheWrite: cacheWriteCost,\n\t\t\t},\n\t\t\tcontextWindow: contextWindow ?? DEFAULT_CONTEXT_WINDOW,\n\t\t\tmaxTokens: maxTokens ?? DEFAULT_MAX_TOKENS,\n\t\t\tcompat: template.compat,\n\t\t\tthinkingLevelMap: template.thinkingLevelMap,\n\t\t},\n\t\tsupplied: {\n\t\t\tcontextWindow: contextWindow !== undefined,\n\t\t\tmaxTokens: maxTokens !== undefined,\n\t\t\tinput: true,\n\t\t\tname: typeof raw.name === \"string\" && raw.name.trim().length > 0,\n\t\t\tcost: pricing !== undefined,\n\t\t\treasoning: supported.includes(\"reasoning\"),\n\t\t},\n\t};\n}\n\nexport function discoverLiveModels(\n\tproviderId: string,\n\tpayload: unknown,\n\tbakedIn: readonly Model<Api>[],\n): LiveModelDiscovery[] {\n\tconst template = firstBakedInModel(bakedIn);\n\tif (!template) return [];\n\tconst rows = parseOpenAIModelsList(payload);\n\tconst discoveries: LiveModelDiscovery[] = [];\n\tfor (const raw of rows) {\n\t\tconst id = typeof raw.id === \"string\" ? raw.id.trim() : \"\";\n\t\tif (!id) continue;\n\t\tif (providerId === \"openrouter\") {\n\t\t\tconst mapped = mapOpenRouterModel(raw, { ...template, provider: providerId });\n\t\t\tif (mapped) discoveries.push(mapped);\n\t\t\tcontinue;\n\t\t}\n\t\tdiscoveries.push(synthesizeLiveModel(id, { ...template, provider: providerId }, raw));\n\t}\n\treturn discoveries;\n}\n\nexport function missingLiveFields(supplied: LiveFieldSupply): Array<\"contextWindow\" | \"maxTokens\" | \"input\"> {\n\tconst missing: Array<\"contextWindow\" | \"maxTokens\" | \"input\"> = [];\n\tif (!supplied.contextWindow) missing.push(\"contextWindow\");\n\tif (!supplied.maxTokens) missing.push(\"maxTokens\");\n\tif (!supplied.input) missing.push(\"input\");\n\treturn missing;\n}\n\nexport function isLiveModelIncomplete(discovery: LiveModelDiscovery, knownKeys: Set<string>): boolean {\n\tif (knownKeys.has(modelKey(discovery.model.provider, discovery.model.id))) return false;\n\treturn missingLiveFields(discovery.supplied).length > 0;\n}\n\nexport function collectIncompleteLiveModels(\n\tdiscoveries: readonly LiveModelDiscovery[],\n\tbakedIn: readonly Model<Api>[],\n\tknownKeys: Set<string>,\n): IncompleteLiveModel[] {\n\tconst apis = uniqueApis(bakedIn);\n\tconst incomplete: IncompleteLiveModel[] = [];\n\tfor (const discovery of discoveries) {\n\t\tif (!isLiveModelIncomplete(discovery, knownKeys)) continue;\n\t\tincomplete.push({\n\t\t\tprovider: discovery.model.provider,\n\t\t\tid: discovery.model.id,\n\t\t\tname: discovery.model.name,\n\t\t\tmissing: missingLiveFields(discovery.supplied),\n\t\t\tdraft: discovery.model,\n\t\t\tavailableApis: apis,\n\t\t});\n\t}\n\treturn incomplete;\n}\n\nexport function buildLiveOverlay(\n\tbakedIn: readonly Model<Api>[],\n\tdiscoveries: readonly LiveModelDiscovery[],\n): Model<Api>[] {\n\tconst bakedById = new Map(bakedIn.map((model) => [model.id, model]));\n\tconst overlay: Model<Api>[] = [];\n\tfor (const discovery of discoveries) {\n\t\tconst baked = bakedById.get(discovery.model.id);\n\t\tif (!baked) {\n\t\t\toverlay.push(discovery.model);\n\t\t\tcontinue;\n\t\t}\n\t\toverlay.push({\n\t\t\t...baked,\n\t\t\tname: discovery.supplied.name ? discovery.model.name : baked.name,\n\t\t\tcontextWindow: discovery.supplied.contextWindow ? discovery.model.contextWindow : baked.contextWindow,\n\t\t\tmaxTokens: discovery.supplied.maxTokens ? discovery.model.maxTokens : baked.maxTokens,\n\t\t\tinput: discovery.supplied.input ? discovery.model.input : baked.input,\n\t\t\tcost: discovery.supplied.cost ? discovery.model.cost : baked.cost,\n\t\t\treasoning: discovery.supplied.reasoning ? discovery.model.reasoning : baked.reasoning,\n\t\t});\n\t}\n\treturn overlay;\n}\n\nexport function describeIncomplete(model: IncompleteLiveModel): string {\n\tconst labels: string[] = [];\n\tfor (const field of model.missing) {\n\t\tif (field === \"contextWindow\") labels.push(\"context window\");\n\t\telse if (field === \"maxTokens\") labels.push(\"max tokens\");\n\t\telse labels.push(\"input types\");\n\t}\n\tlet missingText = \"metadata\";\n\tif (labels.length === 1) missingText = labels[0] ?? missingText;\n\telse if (labels.length === 2) missingText = `${labels[0]} and ${labels[1]}`;\n\telse if (labels.length > 2) missingText = `${labels.slice(0, -1).join(\", \")}, and ${labels[labels.length - 1]}`;\n\treturn `New model ${model.provider}/${model.id} — missing ${missingText}.`;\n}\n\nexport function parsePositiveInt(value: string | undefined, fallback: number): number {\n\tif (!value?.trim()) return fallback;\n\tconst parsed = Number(value.trim());\n\tif (!Number.isInteger(parsed) || parsed <= 0) return fallback;\n\treturn parsed;\n}\n\nexport interface IncompleteModelAnswers {\n\tcontextWindow?: number;\n\tmaxTokens?: number;\n\tinput?: (\"text\" | \"image\")[];\n\treasoning?: boolean;\n\tapi?: Api;\n}\n\nexport function applyIncompleteAnswers(draft: Model<Api>, answers: IncompleteModelAnswers): Model<Api> {\n\treturn {\n\t\t...draft,\n\t\tcontextWindow: answers.contextWindow ?? draft.contextWindow,\n\t\tmaxTokens: answers.maxTokens ?? draft.maxTokens,\n\t\tinput: answers.input ?? draft.input,\n\t\treasoning: answers.reasoning ?? draft.reasoning,\n\t\tapi: answers.api ?? draft.api,\n\t};\n}\n\nexport interface FetchLiveProviderModelsOptions {\n\tproviderId: string;\n\tbaseUrl: string;\n\tbakedIn: readonly Model<Api>[];\n\tcredential?: Credential;\n\tknownKeys: Set<string>;\n\ttimeoutMs?: number;\n\tsignal?: AbortSignal;\n\tfetchImpl?: typeof fetch;\n}\n\nexport async function fetchLiveProviderModels(options: FetchLiveProviderModelsOptions): Promise<LiveCatalogResult> {\n\tconst empty: LiveCatalogResult = {\n\t\tproviderId: options.providerId,\n\t\tstatus: \"skipped\",\n\t\tdiscoveries: [],\n\t\tadded: 0,\n\t\ttotal: 0,\n\t\tincomplete: [],\n\t};\n\tconst template = firstBakedInModel(options.bakedIn);\n\tif (!template || !options.baseUrl) return empty;\n\n\tconst timeoutMs = options.timeoutMs ?? LIVE_CATALOG_TIMEOUT_MS;\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst timer = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\tconst onParentAbort = () => controller.abort();\n\toptions.signal?.addEventListener(\"abort\", onParentAbort, { once: true });\n\tif (options.signal?.aborted) controller.abort();\n\n\ttry {\n\t\tconst fetchImpl = options.fetchImpl ?? fetch;\n\t\tconst response = await Promise.race([\n\t\t\tfetchImpl(modelsListUrl(options.baseUrl), {\n\t\t\t\theaders: {\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\"User-Agent\": getPiUserAgent(VERSION),\n\t\t\t\t\t...credentialAuthHeaders(options.credential),\n\t\t\t\t},\n\t\t\t\tsignal: controller.signal,\n\t\t\t}),\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\tconst abortError = new Error(\"The operation was aborted\");\n\t\t\t\tabortError.name = \"AbortError\";\n\t\t\t\tcontroller.signal.addEventListener(\"abort\", () => reject(abortError), { once: true });\n\t\t\t\tif (controller.signal.aborted) reject(abortError);\n\t\t\t}),\n\t\t]);\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\t...empty,\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\t\tconst discoveries = discoverLiveModels(options.providerId, await response.json(), options.bakedIn);\n\t\tconst bakedIds = new Set(options.bakedIn.map((model) => model.id));\n\t\tconst added = discoveries.filter((discovery) => !bakedIds.has(discovery.model.id)).length;\n\t\treturn {\n\t\t\tproviderId: options.providerId,\n\t\t\tstatus: \"ok\",\n\t\t\tdiscoveries,\n\t\t\tadded,\n\t\t\ttotal: discoveries.length,\n\t\t\tincomplete: collectIncompleteLiveModels(discoveries, options.bakedIn, options.knownKeys),\n\t\t};\n\t} catch (error) {\n\t\tif (options.signal?.aborted && !timedOut) return { ...empty, status: \"skipped\", error: \"aborted\" };\n\t\tif (timedOut || (error instanceof Error && error.name === \"AbortError\")) {\n\t\t\treturn { ...empty, status: \"timeout\", error: \"timed out\" };\n\t\t}\n\t\treturn {\n\t\t\t...empty,\n\t\t\tstatus: \"error\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t} finally {\n\t\tclearTimeout(timer);\n\t\toptions.signal?.removeEventListener(\"abort\", onParentAbort);\n\t}\n}\n"]}
1
+ {"version":3,"file":"live-catalog.d.ts","sourceRoot":"","sources":["../../src/core/live-catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Api, type Credential, isOpenAiChatModel, type Model, parseOpenAiGptVersion } from \"@ashx-j/lunr-ai\";\nimport { codexModelsUrl, resolveCodexCatalogVersion } from \"@ashx-j/lunr-ai/catalog/codex\";\nimport { normalizeCatalogPricing } from \"@ashx-j/lunr-ai/catalog/metadata\";\nimport { VERSION } from \"../config.ts\";\nimport { getPiUserAgent } from \"../utils/pi-user-agent.ts\";\nimport { discoverCodexModels } from \"./codex-catalog.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const LIVE_CATALOG_TIMEOUT_MS = 4000;\nexport const INCOMPLETE_PROMPT_CAP = 8;\nexport const DEFAULT_CONTEXT_WINDOW = 128000;\nexport const DEFAULT_MAX_TOKENS = 8192;\n\nexport const LIVE_LIST_PROVIDER_IDS = [\n\t\"openai-codex\",\n\t\"xai\",\n\t\"openrouter\",\n\t\"qwen-cloud\",\n\t\"qwen-cloud-cn\",\n\t\"groq\",\n\t\"openai\",\n\t\"deepseek\",\n] as const;\n\nexport type LiveListProviderId = (typeof LIVE_LIST_PROVIDER_IDS)[number];\n\nconst LIVE_LIST_PROVIDER_SET = new Set<string>(LIVE_LIST_PROVIDER_IDS);\n\nexport function isLiveListProvider(providerId: string): providerId is LiveListProviderId {\n\treturn LIVE_LIST_PROVIDER_SET.has(providerId);\n}\n\nexport interface LiveFieldSupply {\n\tcontextWindow: boolean;\n\tmaxTokens: boolean;\n\tinput: boolean;\n\tname: boolean;\n\tcost: boolean;\n\treasoning: boolean;\n}\n\nexport interface LiveModelDiscovery {\n\tmodel: Model<Api>;\n\tsupplied: LiveFieldSupply;\n}\n\nexport interface IncompleteLiveModel {\n\tprovider: string;\n\tid: string;\n\tname: string;\n\tmissing: Array<\"contextWindow\" | \"maxTokens\" | \"input\">;\n\tdraft: Model<Api>;\n\tavailableApis: Api[];\n}\n\nexport type LiveCatalogStatus = \"ok\" | \"timeout\" | \"error\" | \"skipped\";\n\nexport interface LiveCatalogResult {\n\tdiscoveryClientVersion?: string;\n\tproviderId: string;\n\tstatus: LiveCatalogStatus;\n\tdiscoveries: LiveModelDiscovery[];\n\tadded: number;\n\ttotal: number;\n\tincomplete: IncompleteLiveModel[];\n\terror?: string;\n}\n\nconst ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\nconst NONE_SUPPLIED: LiveFieldSupply = {\n\tcontextWindow: false,\n\tmaxTokens: false,\n\tinput: false,\n\tname: false,\n\tcost: false,\n\treasoning: false,\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"string\" && value.trim()) {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn undefined;\n}\n\nfunction roundCost(value: number): number {\n\treturn Number(value.toFixed(6));\n}\n\nexport function modelsListUrl(baseUrl: string): string {\n\treturn `${baseUrl.replace(/\\/+$/u, \"\")}/models`;\n}\n\nexport function credentialAuthHeaders(credential: Credential | undefined): Record<string, string> {\n\tif (!credential) return {};\n\tif (credential.type === \"api_key\" && credential.key) {\n\t\treturn { Authorization: `Bearer ${credential.key}` };\n\t}\n\tif (credential.type === \"oauth\" && typeof credential.access === \"string\" && credential.access) {\n\t\treturn { Authorization: `Bearer ${credential.access}` };\n\t}\n\treturn {};\n}\n\n/** OpenAI `{ data: [{ id, ... }] }` plus a bare array. */\nexport function parseOpenAIModelsList(value: unknown): Record<string, unknown>[] {\n\tconst entries = isRecord(value) && Array.isArray(value.data) ? value.data : Array.isArray(value) ? value : [];\n\tconst rows: Record<string, unknown>[] = [];\n\tfor (const entry of entries) {\n\t\tif (typeof entry === \"string\" && entry.trim()) {\n\t\t\trows.push({ id: entry.trim() });\n\t\t\tcontinue;\n\t\t}\n\t\tif (isRecord(entry) && typeof entry.id === \"string\" && entry.id.trim()) rows.push(entry);\n\t}\n\treturn rows;\n}\n\nexport function firstBakedInModel(models: readonly Model<Api>[]): Model<Api> | undefined {\n\treturn models[0];\n}\n\nexport function selectLiveTemplate(\n\tproviderId: string,\n\tid: string,\n\tbakedIn: readonly Model<Api>[],\n): Model<Api> | undefined {\n\tconst exact = bakedIn.find((model) => model.id === id);\n\tif (exact) return exact;\n\tconst version = parseOpenAiGptVersion(id);\n\tif (providerId !== \"openai\" || !version || version.major < 5 || isOpenAiChatModel(id)) {\n\t\treturn firstBakedInModel(bakedIn);\n\t}\n\tlet reasoning: Model<Api> | undefined;\n\tfor (const model of bakedIn) {\n\t\tif (model.reasoning && parseOpenAiGptVersion(model.id)) reasoning = model;\n\t}\n\treturn reasoning ?? firstBakedInModel(bakedIn);\n}\n\nexport function uniqueApis(models: readonly Model<Api>[]): Api[] {\n\tconst seen = new Set<Api>();\n\tconst apis: Api[] = [];\n\tfor (const model of models) {\n\t\tif (seen.has(model.api)) continue;\n\t\tseen.add(model.api);\n\t\tapis.push(model.api);\n\t}\n\treturn apis;\n}\n\nexport function synthesizeLiveModel(\n\tid: string,\n\ttemplate: Model<Api>,\n\traw: Record<string, unknown> = {},\n): LiveModelDiscovery {\n\tconst name = typeof raw.name === \"string\" && raw.name.trim() ? raw.name.trim() : id;\n\tconst exact = id === template.id;\n\tconst discovery: LiveModelDiscovery = {\n\t\tmodel: {\n\t\t\tid,\n\t\t\tname,\n\t\t\tapi: template.api,\n\t\t\tprovider: template.provider,\n\t\t\tbaseUrl: template.baseUrl,\n\t\t\treasoning: template.reasoning,\n\t\t\tinput: exact ? template.input : [\"text\"],\n\t\t\tcost: ZERO_COST,\n\t\t\tcontextWindow: DEFAULT_CONTEXT_WINDOW,\n\t\t\tmaxTokens: DEFAULT_MAX_TOKENS,\n\t\t\tcompat: template.compat,\n\t\t\tthinkingLevelMap: exact ? template.thinkingLevelMap : undefined,\n\t\t\tcatalog: { source: \"provider\", supplied: typeof raw.name === \"string\" ? [\"name\"] : [], pricing: \"unknown\" },\n\t\t},\n\t\tsupplied: {\n\t\t\t...NONE_SUPPLIED,\n\t\t\tname: typeof raw.name === \"string\" && raw.name.trim().length > 0,\n\t\t},\n\t};\n\tconst context = asFiniteNumber(raw.context_window ?? raw.context_length ?? raw.max_context_length);\n\tconst output = asFiniteNumber(raw.max_output_tokens ?? raw.max_completion_tokens);\n\tif (context && context > 0) {\n\t\tdiscovery.model.contextWindow = context;\n\t\tdiscovery.supplied.contextWindow = true;\n\t}\n\tif (output && output > 0) {\n\t\tdiscovery.model.maxTokens = output;\n\t\tdiscovery.supplied.maxTokens = true;\n\t}\n\tif (typeof raw.reasoning === \"boolean\") {\n\t\tdiscovery.model.reasoning = raw.reasoning;\n\t\tdiscovery.supplied.reasoning = true;\n\t}\n\tif (Array.isArray(raw.input_modalities)) {\n\t\tconst input = raw.input_modalities.filter(\n\t\t\t(value): value is \"text\" | \"image\" => value === \"text\" || value === \"image\",\n\t\t);\n\t\tif (input.length) {\n\t\t\tdiscovery.model.input = input;\n\t\t\tdiscovery.supplied.input = true;\n\t\t}\n\t}\n\tdiscovery.model.catalog!.supplied = Object.entries(discovery.supplied)\n\t\t.filter(([, value]) => value)\n\t\t.map(([field]) => field) as NonNullable<Model<Api>[\"catalog\"]>[\"supplied\"];\n\treturn discovery;\n}\n\n/** OpenRouter field mapping (tools filter, pricing × 1e6, context) from generate-models. */\nexport function mapOpenRouterModel(raw: Record<string, unknown>, template: Model<Api>): LiveModelDiscovery | undefined {\n\tconst id = typeof raw.id === \"string\" ? raw.id.trim() : \"\";\n\tif (!id) return undefined;\n\tconst supported = Array.isArray(raw.supported_parameters) ? raw.supported_parameters : [];\n\tif (!supported.includes(\"tools\")) return undefined;\n\n\tconst architecture = isRecord(raw.architecture) ? raw.architecture : undefined;\n\tconst modality = typeof architecture?.modality === \"string\" ? architecture.modality : \"\";\n\tconst input: (\"text\" | \"image\")[] = [\"text\"];\n\tif (modality.includes(\"image\")) input.push(\"image\");\n\n\tconst pricing = isRecord(raw.pricing) ? raw.pricing : undefined;\n\tconst inputCost = roundCost((asFiniteNumber(pricing?.prompt) ?? 0) * 1_000_000);\n\tconst outputCost = roundCost((asFiniteNumber(pricing?.completion) ?? 0) * 1_000_000);\n\tconst cacheReadCost = roundCost((asFiniteNumber(pricing?.input_cache_read) ?? 0) * 1_000_000);\n\tconst cacheWriteCost = roundCost((asFiniteNumber(pricing?.input_cache_write) ?? 0) * 1_000_000);\n\n\tconst topProvider = isRecord(raw.top_provider) ? raw.top_provider : undefined;\n\tconst contextWindow = asFiniteNumber(topProvider?.context_length) ?? asFiniteNumber(raw.context_length);\n\tconst maxTokens = asFiniteNumber(topProvider?.max_completion_tokens) ?? asFiniteNumber(raw.max_completion_tokens);\n\n\tconst name = typeof raw.name === \"string\" && raw.name.trim() ? raw.name.trim() : id;\n\treturn {\n\t\tmodel: {\n\t\t\tid,\n\t\t\tname,\n\t\t\tapi: template.api,\n\t\t\tprovider: template.provider,\n\t\t\tbaseUrl: template.baseUrl,\n\t\t\treasoning: supported.includes(\"reasoning\") || template.reasoning,\n\t\t\tinput,\n\t\t\tcost: {\n\t\t\t\tinput: inputCost,\n\t\t\t\toutput: outputCost,\n\t\t\t\tcacheRead: cacheReadCost,\n\t\t\t\tcacheWrite: cacheWriteCost,\n\t\t\t},\n\t\t\tcontextWindow: contextWindow ?? DEFAULT_CONTEXT_WINDOW,\n\t\t\tmaxTokens: maxTokens ?? DEFAULT_MAX_TOKENS,\n\t\t\tcompat: template.compat,\n\t\t\tthinkingLevelMap: template.thinkingLevelMap,\n\t\t},\n\t\tsupplied: {\n\t\t\tcontextWindow: contextWindow !== undefined,\n\t\t\tmaxTokens: maxTokens !== undefined,\n\t\t\tinput: true,\n\t\t\tname: typeof raw.name === \"string\" && raw.name.trim().length > 0,\n\t\t\tcost: asFiniteNumber(pricing?.prompt) !== undefined && asFiniteNumber(pricing?.completion) !== undefined,\n\t\t\treasoning: supported.includes(\"reasoning\"),\n\t\t},\n\t};\n}\n\nexport function discoverLiveModels(\n\tproviderId: string,\n\tpayload: unknown,\n\tbakedIn: readonly Model<Api>[],\n): LiveModelDiscovery[] {\n\tif (providerId === \"openai-codex\")\n\t\treturn discoverCodexModels(payload, bakedIn[0]?.baseUrl ?? \"https://chatgpt.com/backend-api\");\n\tconst fallback = firstBakedInModel(bakedIn);\n\tif (!fallback) return [];\n\tconst rows = parseOpenAIModelsList(payload);\n\tconst discoveries: LiveModelDiscovery[] = [];\n\tfor (const raw of rows) {\n\t\tconst id = typeof raw.id === \"string\" ? raw.id.trim() : \"\";\n\t\tif (!id) continue;\n\t\tif (\n\t\t\traw.tool_call === false ||\n\t\t\traw.type === \"embedding\" ||\n\t\t\t(providerId === \"openai\" &&\n\t\t\t\t/^(?:text-embedding-|tts-|whisper-|dall-e-|gpt-image-|gpt-audio|gpt-realtime|sora-|.*moderation)/i.test(id))\n\t\t)\n\t\t\tcontinue;\n\t\tconst template = { ...(selectLiveTemplate(providerId, id, bakedIn) ?? fallback), provider: providerId };\n\t\tif (providerId === \"openrouter\") {\n\t\t\tconst mapped = mapOpenRouterModel(raw, template);\n\t\t\tif (mapped) {\n\t\t\t\tmapped.model.catalog = {\n\t\t\t\t\tsource: \"provider\",\n\t\t\t\t\t...(!mapped.supplied.cost ? { pricing: \"unknown\" as const } : {}),\n\t\t\t\t\tsupplied: Object.entries(mapped.supplied)\n\t\t\t\t\t\t.filter(([, supplied]) => supplied)\n\t\t\t\t\t\t.map(([key]) => key) as NonNullable<Model<Api>[\"catalog\"]>[\"supplied\"],\n\t\t\t\t};\n\t\t\t\tmapped.model = normalizeCatalogPricing(mapped.model);\n\t\t\t\tif (mapped.model.catalog?.pricing === \"unknown\") mapped.supplied.cost = false;\n\t\t\t\tdiscoveries.push(mapped);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tdiscoveries.push(synthesizeLiveModel(id, template, raw));\n\t}\n\treturn discoveries;\n}\n\nexport function missingLiveFields(supplied: LiveFieldSupply): Array<\"contextWindow\" | \"maxTokens\" | \"input\"> {\n\tconst missing: Array<\"contextWindow\" | \"maxTokens\" | \"input\"> = [];\n\tif (!supplied.contextWindow) missing.push(\"contextWindow\");\n\tif (!supplied.maxTokens) missing.push(\"maxTokens\");\n\tif (!supplied.input) missing.push(\"input\");\n\treturn missing;\n}\n\nexport function isLiveModelIncomplete(discovery: LiveModelDiscovery, knownKeys: Set<string>): boolean {\n\tif (knownKeys.has(modelKey(discovery.model.provider, discovery.model.id))) return false;\n\treturn missingLiveFields(discovery.supplied).length > 0;\n}\n\nexport function collectIncompleteLiveModels(\n\tdiscoveries: readonly LiveModelDiscovery[],\n\tbakedIn: readonly Model<Api>[],\n\tknownKeys: Set<string>,\n): IncompleteLiveModel[] {\n\tconst apis = uniqueApis(bakedIn);\n\tconst incomplete: IncompleteLiveModel[] = [];\n\tfor (const discovery of discoveries) {\n\t\tif (!isLiveModelIncomplete(discovery, knownKeys)) continue;\n\t\tincomplete.push({\n\t\t\tprovider: discovery.model.provider,\n\t\t\tid: discovery.model.id,\n\t\t\tname: discovery.model.name,\n\t\t\tmissing: missingLiveFields(discovery.supplied),\n\t\t\tdraft: discovery.model,\n\t\t\tavailableApis: apis,\n\t\t});\n\t}\n\treturn incomplete;\n}\n\nexport function buildLiveOverlay(\n\tbakedIn: readonly Model<Api>[],\n\tdiscoveries: readonly LiveModelDiscovery[],\n): Model<Api>[] {\n\tconst bakedById = new Map(bakedIn.map((model) => [model.id, model]));\n\tconst overlay: Model<Api>[] = [];\n\tfor (const discovery of discoveries) {\n\t\tconst baked = bakedById.get(discovery.model.id);\n\t\tif (!baked) {\n\t\t\toverlay.push(discovery.model);\n\t\t\tcontinue;\n\t\t}\n\t\toverlay.push({\n\t\t\t...baked,\n\t\t\tcatalog: discovery.model.catalog,\n\t\t\tthinkingLevelMap: discovery.model.catalog?.supplied.includes(\"thinkingLevelMap\")\n\t\t\t\t? discovery.model.thinkingLevelMap\n\t\t\t\t: baked.thinkingLevelMap,\n\t\t\tname: discovery.supplied.name ? discovery.model.name : baked.name,\n\t\t\tcontextWindow: discovery.supplied.contextWindow ? discovery.model.contextWindow : baked.contextWindow,\n\t\t\tmaxTokens: discovery.supplied.maxTokens ? discovery.model.maxTokens : baked.maxTokens,\n\t\t\tinput: discovery.supplied.input ? discovery.model.input : baked.input,\n\t\t\tcost: discovery.supplied.cost ? discovery.model.cost : baked.cost,\n\t\t\treasoning: discovery.supplied.reasoning ? discovery.model.reasoning : baked.reasoning,\n\t\t});\n\t}\n\treturn overlay;\n}\n\nexport function describeIncomplete(model: IncompleteLiveModel): string {\n\tconst labels: string[] = [];\n\tfor (const field of model.missing) {\n\t\tif (field === \"contextWindow\") labels.push(\"context window\");\n\t\telse if (field === \"maxTokens\") labels.push(\"max tokens\");\n\t\telse labels.push(\"input types\");\n\t}\n\tlet missingText = \"metadata\";\n\tif (labels.length === 1) missingText = labels[0] ?? missingText;\n\telse if (labels.length === 2) missingText = `${labels[0]} and ${labels[1]}`;\n\telse if (labels.length > 2) missingText = `${labels.slice(0, -1).join(\", \")}, and ${labels[labels.length - 1]}`;\n\treturn `New model ${model.provider}/${model.id} — missing ${missingText}.`;\n}\n\nexport function parsePositiveInt(value: string | undefined, fallback: number): number {\n\tif (!value?.trim()) return fallback;\n\tconst parsed = Number(value.trim());\n\tif (!Number.isInteger(parsed) || parsed <= 0) return fallback;\n\treturn parsed;\n}\n\nexport interface IncompleteModelAnswers {\n\tcontextWindow?: number;\n\tmaxTokens?: number;\n\tinput?: (\"text\" | \"image\")[];\n\treasoning?: boolean;\n\tapi?: Api;\n}\n\nexport function applyIncompleteAnswers(draft: Model<Api>, answers: IncompleteModelAnswers): Model<Api> {\n\treturn {\n\t\t...draft,\n\t\tcontextWindow: answers.contextWindow ?? draft.contextWindow,\n\t\tmaxTokens: answers.maxTokens ?? draft.maxTokens,\n\t\tinput: answers.input ?? draft.input,\n\t\treasoning: answers.reasoning ?? draft.reasoning,\n\t\tapi: answers.api ?? draft.api,\n\t};\n}\n\nexport interface FetchLiveProviderModelsOptions {\n\tdiscoveryClientVersion?: string;\n\tproviderId: string;\n\tbaseUrl: string;\n\tbakedIn: readonly Model<Api>[];\n\tcredential?: Credential;\n\tknownKeys: Set<string>;\n\ttimeoutMs?: number;\n\tsignal?: AbortSignal;\n\tfetchImpl?: typeof fetch;\n}\n\nexport async function fetchLiveProviderModels(options: FetchLiveProviderModelsOptions): Promise<LiveCatalogResult> {\n\tconst empty: LiveCatalogResult = {\n\t\tproviderId: options.providerId,\n\t\tstatus: \"skipped\",\n\t\tdiscoveries: [],\n\t\tadded: 0,\n\t\ttotal: 0,\n\t\tincomplete: [],\n\t};\n\tconst template = firstBakedInModel(options.bakedIn);\n\tif ((!template && options.providerId !== \"openai-codex\") || !options.baseUrl) return empty;\n\n\tconst timeoutMs = options.timeoutMs ?? LIVE_CATALOG_TIMEOUT_MS;\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst timer = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\tconst onParentAbort = () => controller.abort();\n\toptions.signal?.addEventListener(\"abort\", onParentAbort, { once: true });\n\tif (options.signal?.aborted) controller.abort();\n\n\ttry {\n\t\tconst fetchImpl = options.fetchImpl ?? fetch;\n\t\tconst codex = options.providerId === \"openai-codex\";\n\t\tconst accountId =\n\t\t\toptions.credential?.type === \"oauth\" && typeof options.credential.accountId === \"string\"\n\t\t\t\t? options.credential.accountId\n\t\t\t\t: undefined;\n\t\tlet discoveryClientVersion = options.discoveryClientVersion;\n\t\tconst payload = await Promise.race([\n\t\t\t(async () => {\n\t\t\t\tif (codex)\n\t\t\t\t\tdiscoveryClientVersion = await resolveCodexCatalogVersion(\n\t\t\t\t\t\tfetchImpl,\n\t\t\t\t\t\tcontroller.signal,\n\t\t\t\t\t\tdiscoveryClientVersion,\n\t\t\t\t\t);\n\t\t\t\tconst response = await fetchImpl(\n\t\t\t\t\tcodex ? codexModelsUrl(options.baseUrl, discoveryClientVersion) : modelsListUrl(options.baseUrl),\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\t\t\"User-Agent\": getPiUserAgent(VERSION),\n\t\t\t\t\t\t\t...credentialAuthHeaders(options.credential),\n\t\t\t\t\t\t\t...(accountId ? { \"ChatGPT-Account-ID\": accountId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\t\t\t\treturn response.json();\n\t\t\t})(),\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\tconst abortError = new Error(\"The operation was aborted\");\n\t\t\t\tabortError.name = \"AbortError\";\n\t\t\t\tcontroller.signal.addEventListener(\"abort\", () => reject(abortError), { once: true });\n\t\t\t\tif (controller.signal.aborted) reject(abortError);\n\t\t\t}),\n\t\t]);\n\t\tconst discoveries = codex\n\t\t\t? discoverCodexModels(payload, options.baseUrl)\n\t\t\t: discoverLiveModels(options.providerId, payload, options.bakedIn);\n\t\tif (!discoveries.length) throw new Error(\"No usable models returned; retaining cached models\");\n\t\tconst bakedIds = new Set(options.bakedIn.map((model) => model.id));\n\t\tconst added = discoveries.filter((discovery) => !bakedIds.has(discovery.model.id)).length;\n\t\treturn {\n\t\t\tproviderId: options.providerId,\n\t\t\tdiscoveryClientVersion,\n\t\t\tstatus: \"ok\",\n\t\t\tdiscoveries,\n\t\t\tadded,\n\t\t\ttotal: discoveries.length,\n\t\t\tincomplete: collectIncompleteLiveModels(discoveries, options.bakedIn, options.knownKeys),\n\t\t};\n\t} catch (error) {\n\t\tif (options.signal?.aborted && !timedOut) return { ...empty, status: \"skipped\", error: \"aborted\" };\n\t\tif (timedOut || (error instanceof Error && error.name === \"AbortError\")) {\n\t\t\treturn { ...empty, status: \"timeout\", error: \"timed out\" };\n\t\t}\n\t\treturn {\n\t\t\t...empty,\n\t\t\tstatus: \"error\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t} finally {\n\t\tclearTimeout(timer);\n\t\toptions.signal?.removeEventListener(\"abort\", onParentAbort);\n\t}\n}\n"]}
@@ -1,11 +1,16 @@
1
+ import { isOpenAiChatModel, parseOpenAiGptVersion } from "@ashx-j/lunr-ai";
2
+ import { codexModelsUrl, resolveCodexCatalogVersion } from "@ashx-j/lunr-ai/catalog/codex";
3
+ import { normalizeCatalogPricing } from "@ashx-j/lunr-ai/catalog/metadata";
1
4
  import { VERSION } from "../config.js";
2
5
  import { getPiUserAgent } from "../utils/pi-user-agent.js";
6
+ import { discoverCodexModels } from "./codex-catalog.js";
3
7
  import { modelKey } from "./official-catalog.js";
4
8
  export const LIVE_CATALOG_TIMEOUT_MS = 4000;
5
9
  export const INCOMPLETE_PROMPT_CAP = 8;
6
10
  export const DEFAULT_CONTEXT_WINDOW = 128000;
7
11
  export const DEFAULT_MAX_TOKENS = 8192;
8
12
  export const LIVE_LIST_PROVIDER_IDS = [
13
+ "openai-codex",
9
14
  "xai",
10
15
  "openrouter",
11
16
  "qwen-cloud",
@@ -74,6 +79,21 @@ export function parseOpenAIModelsList(value) {
74
79
  export function firstBakedInModel(models) {
75
80
  return models[0];
76
81
  }
82
+ export function selectLiveTemplate(providerId, id, bakedIn) {
83
+ const exact = bakedIn.find((model) => model.id === id);
84
+ if (exact)
85
+ return exact;
86
+ const version = parseOpenAiGptVersion(id);
87
+ if (providerId !== "openai" || !version || version.major < 5 || isOpenAiChatModel(id)) {
88
+ return firstBakedInModel(bakedIn);
89
+ }
90
+ let reasoning;
91
+ for (const model of bakedIn) {
92
+ if (model.reasoning && parseOpenAiGptVersion(model.id))
93
+ reasoning = model;
94
+ }
95
+ return reasoning ?? firstBakedInModel(bakedIn);
96
+ }
77
97
  export function uniqueApis(models) {
78
98
  const seen = new Set();
79
99
  const apis = [];
@@ -87,7 +107,8 @@ export function uniqueApis(models) {
87
107
  }
88
108
  export function synthesizeLiveModel(id, template, raw = {}) {
89
109
  const name = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : id;
90
- return {
110
+ const exact = id === template.id;
111
+ const discovery = {
91
112
  model: {
92
113
  id,
93
114
  name,
@@ -95,18 +116,44 @@ export function synthesizeLiveModel(id, template, raw = {}) {
95
116
  provider: template.provider,
96
117
  baseUrl: template.baseUrl,
97
118
  reasoning: template.reasoning,
98
- input: template.input ?? ["text"],
119
+ input: exact ? template.input : ["text"],
99
120
  cost: ZERO_COST,
100
121
  contextWindow: DEFAULT_CONTEXT_WINDOW,
101
122
  maxTokens: DEFAULT_MAX_TOKENS,
102
123
  compat: template.compat,
103
- thinkingLevelMap: template.thinkingLevelMap,
124
+ thinkingLevelMap: exact ? template.thinkingLevelMap : undefined,
125
+ catalog: { source: "provider", supplied: typeof raw.name === "string" ? ["name"] : [], pricing: "unknown" },
104
126
  },
105
127
  supplied: {
106
128
  ...NONE_SUPPLIED,
107
129
  name: typeof raw.name === "string" && raw.name.trim().length > 0,
108
130
  },
109
131
  };
132
+ const context = asFiniteNumber(raw.context_window ?? raw.context_length ?? raw.max_context_length);
133
+ const output = asFiniteNumber(raw.max_output_tokens ?? raw.max_completion_tokens);
134
+ if (context && context > 0) {
135
+ discovery.model.contextWindow = context;
136
+ discovery.supplied.contextWindow = true;
137
+ }
138
+ if (output && output > 0) {
139
+ discovery.model.maxTokens = output;
140
+ discovery.supplied.maxTokens = true;
141
+ }
142
+ if (typeof raw.reasoning === "boolean") {
143
+ discovery.model.reasoning = raw.reasoning;
144
+ discovery.supplied.reasoning = true;
145
+ }
146
+ if (Array.isArray(raw.input_modalities)) {
147
+ const input = raw.input_modalities.filter((value) => value === "text" || value === "image");
148
+ if (input.length) {
149
+ discovery.model.input = input;
150
+ discovery.supplied.input = true;
151
+ }
152
+ }
153
+ discovery.model.catalog.supplied = Object.entries(discovery.supplied)
154
+ .filter(([, value]) => value)
155
+ .map(([field]) => field);
156
+ return discovery;
110
157
  }
111
158
  /** OpenRouter field mapping (tools filter, pricing × 1e6, context) from generate-models. */
112
159
  export function mapOpenRouterModel(raw, template) {
@@ -155,14 +202,16 @@ export function mapOpenRouterModel(raw, template) {
155
202
  maxTokens: maxTokens !== undefined,
156
203
  input: true,
157
204
  name: typeof raw.name === "string" && raw.name.trim().length > 0,
158
- cost: pricing !== undefined,
205
+ cost: asFiniteNumber(pricing?.prompt) !== undefined && asFiniteNumber(pricing?.completion) !== undefined,
159
206
  reasoning: supported.includes("reasoning"),
160
207
  },
161
208
  };
162
209
  }
163
210
  export function discoverLiveModels(providerId, payload, bakedIn) {
164
- const template = firstBakedInModel(bakedIn);
165
- if (!template)
211
+ if (providerId === "openai-codex")
212
+ return discoverCodexModels(payload, bakedIn[0]?.baseUrl ?? "https://chatgpt.com/backend-api");
213
+ const fallback = firstBakedInModel(bakedIn);
214
+ if (!fallback)
166
215
  return [];
167
216
  const rows = parseOpenAIModelsList(payload);
168
217
  const discoveries = [];
@@ -170,13 +219,30 @@ export function discoverLiveModels(providerId, payload, bakedIn) {
170
219
  const id = typeof raw.id === "string" ? raw.id.trim() : "";
171
220
  if (!id)
172
221
  continue;
222
+ if (raw.tool_call === false ||
223
+ raw.type === "embedding" ||
224
+ (providerId === "openai" &&
225
+ /^(?:text-embedding-|tts-|whisper-|dall-e-|gpt-image-|gpt-audio|gpt-realtime|sora-|.*moderation)/i.test(id)))
226
+ continue;
227
+ const template = { ...(selectLiveTemplate(providerId, id, bakedIn) ?? fallback), provider: providerId };
173
228
  if (providerId === "openrouter") {
174
- const mapped = mapOpenRouterModel(raw, { ...template, provider: providerId });
175
- if (mapped)
229
+ const mapped = mapOpenRouterModel(raw, template);
230
+ if (mapped) {
231
+ mapped.model.catalog = {
232
+ source: "provider",
233
+ ...(!mapped.supplied.cost ? { pricing: "unknown" } : {}),
234
+ supplied: Object.entries(mapped.supplied)
235
+ .filter(([, supplied]) => supplied)
236
+ .map(([key]) => key),
237
+ };
238
+ mapped.model = normalizeCatalogPricing(mapped.model);
239
+ if (mapped.model.catalog?.pricing === "unknown")
240
+ mapped.supplied.cost = false;
176
241
  discoveries.push(mapped);
242
+ }
177
243
  continue;
178
244
  }
179
- discoveries.push(synthesizeLiveModel(id, { ...template, provider: providerId }, raw));
245
+ discoveries.push(synthesizeLiveModel(id, template, raw));
180
246
  }
181
247
  return discoveries;
182
248
  }
@@ -223,6 +289,10 @@ export function buildLiveOverlay(bakedIn, discoveries) {
223
289
  }
224
290
  overlay.push({
225
291
  ...baked,
292
+ catalog: discovery.model.catalog,
293
+ thinkingLevelMap: discovery.model.catalog?.supplied.includes("thinkingLevelMap")
294
+ ? discovery.model.thinkingLevelMap
295
+ : baked.thinkingLevelMap,
226
296
  name: discovery.supplied.name ? discovery.model.name : baked.name,
227
297
  contextWindow: discovery.supplied.contextWindow ? discovery.model.contextWindow : baked.contextWindow,
228
298
  maxTokens: discovery.supplied.maxTokens ? discovery.model.maxTokens : baked.maxTokens,
@@ -280,7 +350,7 @@ export async function fetchLiveProviderModels(options) {
280
350
  incomplete: [],
281
351
  };
282
352
  const template = firstBakedInModel(options.bakedIn);
283
- if (!template || !options.baseUrl)
353
+ if ((!template && options.providerId !== "openai-codex") || !options.baseUrl)
284
354
  return empty;
285
355
  const timeoutMs = options.timeoutMs ?? LIVE_CATALOG_TIMEOUT_MS;
286
356
  const controller = new AbortController();
@@ -295,15 +365,28 @@ export async function fetchLiveProviderModels(options) {
295
365
  controller.abort();
296
366
  try {
297
367
  const fetchImpl = options.fetchImpl ?? fetch;
298
- const response = await Promise.race([
299
- fetchImpl(modelsListUrl(options.baseUrl), {
300
- headers: {
301
- accept: "application/json",
302
- "User-Agent": getPiUserAgent(VERSION),
303
- ...credentialAuthHeaders(options.credential),
304
- },
305
- signal: controller.signal,
306
- }),
368
+ const codex = options.providerId === "openai-codex";
369
+ const accountId = options.credential?.type === "oauth" && typeof options.credential.accountId === "string"
370
+ ? options.credential.accountId
371
+ : undefined;
372
+ let discoveryClientVersion = options.discoveryClientVersion;
373
+ const payload = await Promise.race([
374
+ (async () => {
375
+ if (codex)
376
+ discoveryClientVersion = await resolveCodexCatalogVersion(fetchImpl, controller.signal, discoveryClientVersion);
377
+ const response = await fetchImpl(codex ? codexModelsUrl(options.baseUrl, discoveryClientVersion) : modelsListUrl(options.baseUrl), {
378
+ headers: {
379
+ accept: "application/json",
380
+ "User-Agent": getPiUserAgent(VERSION),
381
+ ...credentialAuthHeaders(options.credential),
382
+ ...(accountId ? { "ChatGPT-Account-ID": accountId } : {}),
383
+ },
384
+ signal: controller.signal,
385
+ });
386
+ if (!response.ok)
387
+ throw new Error(`HTTP ${response.status}`);
388
+ return response.json();
389
+ })(),
307
390
  new Promise((_, reject) => {
308
391
  const abortError = new Error("The operation was aborted");
309
392
  abortError.name = "AbortError";
@@ -312,18 +395,16 @@ export async function fetchLiveProviderModels(options) {
312
395
  reject(abortError);
313
396
  }),
314
397
  ]);
315
- if (!response.ok) {
316
- return {
317
- ...empty,
318
- status: "error",
319
- error: `HTTP ${response.status}`,
320
- };
321
- }
322
- const discoveries = discoverLiveModels(options.providerId, await response.json(), options.bakedIn);
398
+ const discoveries = codex
399
+ ? discoverCodexModels(payload, options.baseUrl)
400
+ : discoverLiveModels(options.providerId, payload, options.bakedIn);
401
+ if (!discoveries.length)
402
+ throw new Error("No usable models returned; retaining cached models");
323
403
  const bakedIds = new Set(options.bakedIn.map((model) => model.id));
324
404
  const added = discoveries.filter((discovery) => !bakedIds.has(discovery.model.id)).length;
325
405
  return {
326
406
  providerId: options.providerId,
407
+ discoveryClientVersion,
327
408
  status: "ok",
328
409
  discoveries,
329
410
  added,