@ashx-j/lunr 0.2.8 → 0.2.10

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 (182) hide show
  1. package/dist/builtin-extensions/ashxj-spinners.d.ts +4 -0
  2. package/dist/builtin-extensions/ashxj-spinners.d.ts.map +1 -1
  3. package/dist/builtin-extensions/ashxj-spinners.js +14 -3
  4. package/dist/builtin-extensions/ashxj-spinners.js.map +1 -1
  5. package/dist/builtin-extensions/ashxj-tui.d.ts +4 -0
  6. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.js +49 -4
  8. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  9. package/dist/builtin-extensions/lunr-behavior.d.ts +2 -1
  10. package/dist/builtin-extensions/lunr-behavior.d.ts.map +1 -1
  11. package/dist/builtin-extensions/lunr-behavior.js +26 -14
  12. package/dist/builtin-extensions/lunr-behavior.js.map +1 -1
  13. package/dist/builtin-extensions/lunr-todos.d.ts +4 -7
  14. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  15. package/dist/builtin-extensions/lunr-todos.js +18 -10
  16. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.js +3 -3
  19. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +4 -4
  22. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  23. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +2 -2
  24. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  25. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +4 -5
  26. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +5 -1
  29. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +6 -18
  32. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +13 -5
  35. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +2 -2
  37. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +2 -2
  39. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.d.ts +21 -0
  41. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.d.ts.map +1 -0
  42. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.js +22 -0
  43. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.js.map +1 -0
  44. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +2 -2
  45. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/shared/types.js +2 -2
  47. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  49. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +2 -1
  50. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  51. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +10 -18
  53. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  54. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +4 -6
  55. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  56. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +26 -18
  57. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  58. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-web-access/index.js +3 -1
  60. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  61. package/dist/builtin-extensions/pi-web-access/render-search-error.d.ts.map +1 -1
  62. package/dist/builtin-extensions/pi-web-access/render-search-error.js +1 -1
  63. package/dist/builtin-extensions/pi-web-access/render-search-error.js.map +1 -1
  64. package/dist/catalog/models.json +1 -1
  65. package/dist/catalog/providers/nvidia.json +1 -1
  66. package/dist/catalog/providers/opencode-go.json +1 -1
  67. package/dist/catalog/providers/opencode.json +1 -1
  68. package/dist/catalog/providers/openrouter.json +1 -1
  69. package/dist/catalog/providers/qwen-cloud-cn.json +1 -1
  70. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  71. package/dist/catalog/providers/xai.json +1 -1
  72. package/dist/catalog/publication.json +2 -2
  73. package/dist/cli/update-cli.d.ts +16 -0
  74. package/dist/cli/update-cli.d.ts.map +1 -0
  75. package/dist/cli/update-cli.js +83 -0
  76. package/dist/cli/update-cli.js.map +1 -0
  77. package/dist/cli.d.ts.map +1 -1
  78. package/dist/cli.js +6 -0
  79. package/dist/cli.js.map +1 -1
  80. package/dist/core/agent-session.d.ts +2 -0
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +9 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/behavior-preset.d.ts +60 -0
  85. package/dist/core/behavior-preset.d.ts.map +1 -0
  86. package/dist/core/behavior-preset.js +257 -0
  87. package/dist/core/behavior-preset.js.map +1 -0
  88. package/dist/core/customize.d.ts +4 -0
  89. package/dist/core/customize.d.ts.map +1 -1
  90. package/dist/core/customize.js +12 -0
  91. package/dist/core/customize.js.map +1 -1
  92. package/dist/core/footer-data-provider.d.ts +12 -1
  93. package/dist/core/footer-data-provider.d.ts.map +1 -1
  94. package/dist/core/footer-data-provider.js +70 -5
  95. package/dist/core/footer-data-provider.js.map +1 -1
  96. package/dist/core/keybindings.d.ts +2 -2
  97. package/dist/core/keybindings.d.ts.map +1 -1
  98. package/dist/core/keybindings.js +1 -1
  99. package/dist/core/keybindings.js.map +1 -1
  100. package/dist/core/model-tiers.d.ts +9 -0
  101. package/dist/core/model-tiers.d.ts.map +1 -1
  102. package/dist/core/model-tiers.js +16 -0
  103. package/dist/core/model-tiers.js.map +1 -1
  104. package/dist/core/settings-manager.d.ts +26 -0
  105. package/dist/core/settings-manager.d.ts.map +1 -1
  106. package/dist/core/settings-manager.js +74 -0
  107. package/dist/core/settings-manager.js.map +1 -1
  108. package/dist/core/tools/bash.d.ts.map +1 -1
  109. package/dist/core/tools/bash.js +3 -4
  110. package/dist/core/tools/bash.js.map +1 -1
  111. package/dist/core/tools/edit.d.ts.map +1 -1
  112. package/dist/core/tools/edit.js +9 -6
  113. package/dist/core/tools/edit.js.map +1 -1
  114. package/dist/core/tools/find.d.ts.map +1 -1
  115. package/dist/core/tools/find.js +2 -1
  116. package/dist/core/tools/find.js.map +1 -1
  117. package/dist/core/tools/grep.d.ts.map +1 -1
  118. package/dist/core/tools/grep.js +2 -1
  119. package/dist/core/tools/grep.js.map +1 -1
  120. package/dist/core/tools/ls.d.ts.map +1 -1
  121. package/dist/core/tools/ls.js +2 -1
  122. package/dist/core/tools/ls.js.map +1 -1
  123. package/dist/core/tools/read.d.ts.map +1 -1
  124. package/dist/core/tools/read.js +2 -1
  125. package/dist/core/tools/read.js.map +1 -1
  126. package/dist/core/tools/render-utils.d.ts +13 -3
  127. package/dist/core/tools/render-utils.d.ts.map +1 -1
  128. package/dist/core/tools/render-utils.js +10 -2
  129. package/dist/core/tools/render-utils.js.map +1 -1
  130. package/dist/core/tools/write.d.ts.map +1 -1
  131. package/dist/core/tools/write.js +5 -3
  132. package/dist/core/tools/write.js.map +1 -1
  133. package/dist/core/update-check.d.ts +32 -0
  134. package/dist/core/update-check.d.ts.map +1 -0
  135. package/dist/core/update-check.js +112 -0
  136. package/dist/core/update-check.js.map +1 -0
  137. package/dist/core/usage-service.d.ts +24 -1
  138. package/dist/core/usage-service.d.ts.map +1 -1
  139. package/dist/core/usage-service.js +91 -14
  140. package/dist/core/usage-service.js.map +1 -1
  141. package/dist/gateway/agent-bridge.d.ts +1 -0
  142. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  143. package/dist/gateway/agent-bridge.js +5 -1
  144. package/dist/gateway/agent-bridge.js.map +1 -1
  145. package/dist/gateway/cron.d.ts.map +1 -1
  146. package/dist/gateway/cron.js +5 -1
  147. package/dist/gateway/cron.js.map +1 -1
  148. package/dist/main.d.ts.map +1 -1
  149. package/dist/main.js +13 -1
  150. package/dist/main.js.map +1 -1
  151. package/dist/modes/interactive/components/assistant-message.d.ts +4 -0
  152. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  153. package/dist/modes/interactive/components/assistant-message.js +68 -10
  154. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  155. package/dist/modes/interactive/components/settings-selector.d.ts +15 -0
  156. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/settings-selector.js +168 -11
  158. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  159. package/dist/modes/interactive/components/thinking-tail.d.ts +1 -0
  160. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/thinking-tail.js +2 -6
  162. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  163. package/dist/modes/interactive/components/tool-execution.d.ts +7 -0
  164. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  165. package/dist/modes/interactive/components/tool-execution.js +84 -16
  166. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  167. package/dist/modes/interactive/interactive-mode.d.ts +19 -2
  168. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  169. package/dist/modes/interactive/interactive-mode.js +221 -37
  170. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  171. package/docs/keybindings.md +1 -1
  172. package/docs/terminal-setup.md +2 -2
  173. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  174. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  175. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  176. package/examples/extensions/gondolin/package-lock.json +2 -2
  177. package/examples/extensions/gondolin/package.json +1 -1
  178. package/examples/extensions/sandbox/package-lock.json +2 -2
  179. package/examples/extensions/sandbox/package.json +1 -1
  180. package/examples/extensions/with-deps/package-lock.json +2 -2
  181. package/examples/extensions/with-deps/package.json +1 -1
  182. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@ashx-j/lunr-agent\";\nimport { Container, Text } from \"@ashx-j/lunr-tui\";\nimport { mkdir as fsMkdir, writeFile as fsWriteFile } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tnormalizeDisplayText,\n\tformatGroupedCall,\n\trenderToolFileName,\n\trenderToolPath,\n\treplaceTabs,\n\tstr,\n\ttoolStatusDotFromContext,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n\t// lunr: compact-by-default — finished, successful, non-expanded calls render\n\t// header-only; the content preview stays while streaming and when expanded.\n\tcompact = false,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = compact ? renderToolFileName(rawPath, theme, cwd) : renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent && !compact) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst totalLines = lines.length;\n\t\tconst maxLines = options.expanded ? lines.length : 10;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ToolDefinition<typeof writeSchema, undefined> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, content }: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\t// Create parent directories if needed.\n\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Write the file contents.\n\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tconst compact = !context.isPartial && !context.expanded && !context.isError;\n\t\t\tif (compact) {\n\t\t\t\tconst pathDisplay = renderToolFileName(rawPath, theme, context.cwd);\n\t\t\t\tlet detail = pathDisplay;\n\t\t\t\tif (fileContent === null) {\n\t\t\t\t\tdetail += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t\t\t\t}\n\t\t\t\tcomponent.setText(\n\t\t\t\t\tformatGroupedCall({\n\t\t\t\t\t\trole: context.groupRole ?? \"singleton\",\n\t\t\t\t\t\tcompact: true,\n\t\t\t\t\t\tdot: toolStatusDotFromContext(context, theme),\n\t\t\t\t\t\ttitle: theme.fg(\"toolTitle\", theme.bold(\"write\")),\n\t\t\t\t\t\tdetail,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tcomponent.setText(\n\t\t\t\t\t`${toolStatusDotFromContext(context, theme)} ${formatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t\tcontext.cwd,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@ashx-j/lunr-agent\";\nimport { Container, Text } from \"@ashx-j/lunr-tui\";\nimport { mkdir as fsMkdir, writeFile as fsWriteFile } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatGroupedCall,\n\tnormalizeDisplayText,\n\trenderToolFileName,\n\trenderToolPath,\n\treplaceTabs,\n\tstr,\n\ttoolGroupTree,\n\ttoolStatusDotFromContext,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n\t// lunr: compact-by-default — finished, successful, non-expanded calls render\n\t// header-only; the content preview stays while streaming and when expanded.\n\tcompact = false,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = compact ? renderToolFileName(rawPath, theme, cwd) : renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent && !compact) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst totalLines = lines.length;\n\t\tconst maxLines = options.expanded ? lines.length : 10;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ToolDefinition<typeof writeSchema, undefined> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, content }: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\t// Create parent directories if needed.\n\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Write the file contents.\n\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tconst compact = !context.isPartial && !context.expanded && !context.isError;\n\t\t\tconst tree = toolGroupTree(context);\n\t\t\tif (compact || (tree && context.groupRole && context.groupRole !== \"singleton\")) {\n\t\t\t\tconst pathDisplay = renderToolFileName(rawPath, theme, context.cwd);\n\t\t\t\tlet detail = pathDisplay;\n\t\t\t\tif (fileContent === null) {\n\t\t\t\t\tdetail += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t\t\t\t}\n\t\t\t\tcomponent.setText(\n\t\t\t\t\tformatGroupedCall({\n\t\t\t\t\t\trole: context.groupRole ?? \"singleton\",\n\t\t\t\t\t\tcompact,\n\t\t\t\t\t\ttree,\n\t\t\t\t\t\tdot: toolStatusDotFromContext(context, theme),\n\t\t\t\t\t\ttitle: theme.fg(\"toolTitle\", theme.bold(\"write\")),\n\t\t\t\t\t\tdetail,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tcomponent.setText(\n\t\t\t\t\t`${toolStatusDotFromContext(context, theme)} ${formatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t\tcontext.cwd,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Published-install version check against the npm registry for @ashx-j/lunr.
3
+ * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.
4
+ */
5
+ export declare const UPDATE_CHECK_TTL_MS: number;
6
+ export declare const UPDATE_CHECK_TIMEOUT_MS = 4000;
7
+ export declare const NPM_LATEST_URL = "https://registry.npmjs.org/@ashx-j/lunr/latest";
8
+ export interface UpdateCheckRecord {
9
+ checkedAt: number;
10
+ latest: string;
11
+ notifiedVersion?: string;
12
+ }
13
+ export interface UpdateCheckResult {
14
+ latest: string;
15
+ current: string;
16
+ newer: boolean;
17
+ notice?: string;
18
+ }
19
+ export declare function isPublishedInstall(packageName?: string, npmName?: string): boolean;
20
+ export declare function updateCheckPath(agentDir: string): string;
21
+ export declare function checkForUpdate(options: {
22
+ currentVersion: string;
23
+ agentDir: string;
24
+ now?: number;
25
+ fetchImpl?: typeof fetch;
26
+ published?: boolean;
27
+ offline?: boolean;
28
+ force?: boolean;
29
+ }): Promise<UpdateCheckResult | undefined>;
30
+ /** Persist that we already showed the TUI notice for this latest version. */
31
+ export declare function markUpdateNotified(agentDir: string, latest: string): void;
32
+ //# sourceMappingURL=update-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../../src/core/update-check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,eAAO,MAAM,mBAAmB,QAAsB,CAAC;AACvD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,cAAc,mDAAyD,CAAC;AAErF,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CACjC,WAAW,GAAE,MAAqB,EAClC,OAAO,GAAE,MAAwB,GAC/B,OAAO,CAET;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAyCD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAqBzC;AAmBD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAUzE","sourcesContent":["/**\n * Published-install version check against the npm registry for @ashx-j/lunr.\n * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { gt } from \"semver\";\nimport { NPM_CLI_PACKAGE, PACKAGE_NAME } from \"../config.ts\";\n\nexport const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;\nexport const UPDATE_CHECK_TIMEOUT_MS = 4000;\nexport const NPM_LATEST_URL = `https://registry.npmjs.org/${NPM_CLI_PACKAGE}/latest`;\n\nexport interface UpdateCheckRecord {\n\tcheckedAt: number;\n\tlatest: string;\n\tnotifiedVersion?: string;\n}\n\nexport interface UpdateCheckResult {\n\tlatest: string;\n\tcurrent: string;\n\tnewer: boolean;\n\tnotice?: string;\n}\n\nexport function isPublishedInstall(\n\tpackageName: string = PACKAGE_NAME,\n\tnpmName: string = NPM_CLI_PACKAGE,\n): boolean {\n\treturn packageName === npmName;\n}\n\nexport function updateCheckPath(agentDir: string): string {\n\treturn join(agentDir, \"update-check.json\");\n}\n\nfunction readRecord(path: string): UpdateCheckRecord | undefined {\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(path, \"utf8\")) as UpdateCheckRecord;\n\t\tif (typeof raw.checkedAt !== \"number\" || typeof raw.latest !== \"string\" || !raw.latest.trim()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tcheckedAt: raw.checkedAt,\n\t\t\tlatest: raw.latest.trim(),\n\t\t\tnotifiedVersion: typeof raw.notifiedVersion === \"string\" ? raw.notifiedVersion : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeRecord(path: string, record: UpdateCheckRecord): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(record)}\\n`, \"utf8\");\n}\n\nasync function fetchLatest(fetchImpl: typeof fetch): Promise<string | undefined> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetchImpl(NPM_LATEST_URL, {\n\t\t\theaders: { Accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return undefined;\n\t\tconst payload = (await response.json()) as { version?: unknown };\n\t\treturn typeof payload.version === \"string\" && payload.version.trim() ? payload.version.trim() : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport async function checkForUpdate(options: {\n\tcurrentVersion: string;\n\tagentDir: string;\n\tnow?: number;\n\tfetchImpl?: typeof fetch;\n\tpublished?: boolean;\n\toffline?: boolean;\n\tforce?: boolean;\n}): Promise<UpdateCheckResult | undefined> {\n\tif (options.offline) return undefined;\n\tif (!(options.published ?? isPublishedInstall())) return undefined;\n\n\tconst now = options.now ?? Date.now();\n\tconst path = updateCheckPath(options.agentDir);\n\tlet record = existsSync(path) ? readRecord(path) : undefined;\n\tconst stale = !record || now - record.checkedAt >= UPDATE_CHECK_TTL_MS || options.force;\n\tif (stale) {\n\t\tconst latest = await fetchLatest(options.fetchImpl ?? fetch);\n\t\tif (!latest) return record ? toResult(options.currentVersion, record, false) : undefined;\n\t\trecord = { checkedAt: now, latest, notifiedVersion: record?.notifiedVersion };\n\t\ttry {\n\t\t\twriteRecord(path, record);\n\t\t} catch {\n\t\t\t// Cache write is best-effort.\n\t\t}\n\t}\n\n\tif (!record) return undefined;\n\treturn toResult(options.currentVersion, record, true);\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n\ttry {\n\t\treturn gt(latest, current);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction toResult(current: string, record: UpdateCheckRecord, allowNotice: boolean): UpdateCheckResult {\n\tconst newer = isNewer(record.latest, current);\n\tconst notice =\n\t\tallowNotice && newer && record.notifiedVersion !== record.latest\n\t\t\t? `lunR ${record.latest} is available. Run lunr update.`\n\t\t\t: undefined;\n\treturn { latest: record.latest, current, newer, notice };\n}\n\n/** Persist that we already showed the TUI notice for this latest version. */\nexport function markUpdateNotified(agentDir: string, latest: string): void {\n\tconst path = updateCheckPath(agentDir);\n\tconst record = existsSync(path) ? readRecord(path) : undefined;\n\tif (!record) return;\n\trecord.notifiedVersion = latest;\n\ttry {\n\t\twriteRecord(path, record);\n\t} catch {\n\t\t// ignore\n\t}\n}\n"]}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Published-install version check against the npm registry for @ashx-j/lunr.
3
+ * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.
4
+ */
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { dirname, join } from "node:path";
7
+ import { gt } from "semver";
8
+ import { NPM_CLI_PACKAGE, PACKAGE_NAME } from "../config.js";
9
+ export const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
10
+ export const UPDATE_CHECK_TIMEOUT_MS = 4000;
11
+ export const NPM_LATEST_URL = `https://registry.npmjs.org/${NPM_CLI_PACKAGE}/latest`;
12
+ export function isPublishedInstall(packageName = PACKAGE_NAME, npmName = NPM_CLI_PACKAGE) {
13
+ return packageName === npmName;
14
+ }
15
+ export function updateCheckPath(agentDir) {
16
+ return join(agentDir, "update-check.json");
17
+ }
18
+ function readRecord(path) {
19
+ try {
20
+ const raw = JSON.parse(readFileSync(path, "utf8"));
21
+ if (typeof raw.checkedAt !== "number" || typeof raw.latest !== "string" || !raw.latest.trim()) {
22
+ return undefined;
23
+ }
24
+ return {
25
+ checkedAt: raw.checkedAt,
26
+ latest: raw.latest.trim(),
27
+ notifiedVersion: typeof raw.notifiedVersion === "string" ? raw.notifiedVersion : undefined,
28
+ };
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ function writeRecord(path, record) {
35
+ mkdirSync(dirname(path), { recursive: true });
36
+ writeFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
37
+ }
38
+ async function fetchLatest(fetchImpl) {
39
+ const controller = new AbortController();
40
+ const timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);
41
+ try {
42
+ const response = await fetchImpl(NPM_LATEST_URL, {
43
+ headers: { Accept: "application/json" },
44
+ signal: controller.signal,
45
+ });
46
+ if (!response.ok)
47
+ return undefined;
48
+ const payload = (await response.json());
49
+ return typeof payload.version === "string" && payload.version.trim() ? payload.version.trim() : undefined;
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ finally {
55
+ clearTimeout(timeout);
56
+ }
57
+ }
58
+ export async function checkForUpdate(options) {
59
+ if (options.offline)
60
+ return undefined;
61
+ if (!(options.published ?? isPublishedInstall()))
62
+ return undefined;
63
+ const now = options.now ?? Date.now();
64
+ const path = updateCheckPath(options.agentDir);
65
+ let record = existsSync(path) ? readRecord(path) : undefined;
66
+ const stale = !record || now - record.checkedAt >= UPDATE_CHECK_TTL_MS || options.force;
67
+ if (stale) {
68
+ const latest = await fetchLatest(options.fetchImpl ?? fetch);
69
+ if (!latest)
70
+ return record ? toResult(options.currentVersion, record, false) : undefined;
71
+ record = { checkedAt: now, latest, notifiedVersion: record?.notifiedVersion };
72
+ try {
73
+ writeRecord(path, record);
74
+ }
75
+ catch {
76
+ // Cache write is best-effort.
77
+ }
78
+ }
79
+ if (!record)
80
+ return undefined;
81
+ return toResult(options.currentVersion, record, true);
82
+ }
83
+ function isNewer(latest, current) {
84
+ try {
85
+ return gt(latest, current);
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ function toResult(current, record, allowNotice) {
92
+ const newer = isNewer(record.latest, current);
93
+ const notice = allowNotice && newer && record.notifiedVersion !== record.latest
94
+ ? `lunR ${record.latest} is available. Run lunr update.`
95
+ : undefined;
96
+ return { latest: record.latest, current, newer, notice };
97
+ }
98
+ /** Persist that we already showed the TUI notice for this latest version. */
99
+ export function markUpdateNotified(agentDir, latest) {
100
+ const path = updateCheckPath(agentDir);
101
+ const record = existsSync(path) ? readRecord(path) : undefined;
102
+ if (!record)
103
+ return;
104
+ record.notifiedVersion = latest;
105
+ try {
106
+ writeRecord(path, record);
107
+ }
108
+ catch {
109
+ // ignore
110
+ }
111
+ }
112
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../../src/core/update-check.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Published-install version check against the npm registry for @ashx-j/lunr.\n * Workspace checkouts (PACKAGE_NAME !== NPM_CLI_PACKAGE) never nag or self-update.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { gt } from \"semver\";\nimport { NPM_CLI_PACKAGE, PACKAGE_NAME } from \"../config.ts\";\n\nexport const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;\nexport const UPDATE_CHECK_TIMEOUT_MS = 4000;\nexport const NPM_LATEST_URL = `https://registry.npmjs.org/${NPM_CLI_PACKAGE}/latest`;\n\nexport interface UpdateCheckRecord {\n\tcheckedAt: number;\n\tlatest: string;\n\tnotifiedVersion?: string;\n}\n\nexport interface UpdateCheckResult {\n\tlatest: string;\n\tcurrent: string;\n\tnewer: boolean;\n\tnotice?: string;\n}\n\nexport function isPublishedInstall(\n\tpackageName: string = PACKAGE_NAME,\n\tnpmName: string = NPM_CLI_PACKAGE,\n): boolean {\n\treturn packageName === npmName;\n}\n\nexport function updateCheckPath(agentDir: string): string {\n\treturn join(agentDir, \"update-check.json\");\n}\n\nfunction readRecord(path: string): UpdateCheckRecord | undefined {\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(path, \"utf8\")) as UpdateCheckRecord;\n\t\tif (typeof raw.checkedAt !== \"number\" || typeof raw.latest !== \"string\" || !raw.latest.trim()) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tcheckedAt: raw.checkedAt,\n\t\t\tlatest: raw.latest.trim(),\n\t\t\tnotifiedVersion: typeof raw.notifiedVersion === \"string\" ? raw.notifiedVersion : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeRecord(path: string, record: UpdateCheckRecord): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(record)}\\n`, \"utf8\");\n}\n\nasync function fetchLatest(fetchImpl: typeof fetch): Promise<string | undefined> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetchImpl(NPM_LATEST_URL, {\n\t\t\theaders: { Accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return undefined;\n\t\tconst payload = (await response.json()) as { version?: unknown };\n\t\treturn typeof payload.version === \"string\" && payload.version.trim() ? payload.version.trim() : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport async function checkForUpdate(options: {\n\tcurrentVersion: string;\n\tagentDir: string;\n\tnow?: number;\n\tfetchImpl?: typeof fetch;\n\tpublished?: boolean;\n\toffline?: boolean;\n\tforce?: boolean;\n}): Promise<UpdateCheckResult | undefined> {\n\tif (options.offline) return undefined;\n\tif (!(options.published ?? isPublishedInstall())) return undefined;\n\n\tconst now = options.now ?? Date.now();\n\tconst path = updateCheckPath(options.agentDir);\n\tlet record = existsSync(path) ? readRecord(path) : undefined;\n\tconst stale = !record || now - record.checkedAt >= UPDATE_CHECK_TTL_MS || options.force;\n\tif (stale) {\n\t\tconst latest = await fetchLatest(options.fetchImpl ?? fetch);\n\t\tif (!latest) return record ? toResult(options.currentVersion, record, false) : undefined;\n\t\trecord = { checkedAt: now, latest, notifiedVersion: record?.notifiedVersion };\n\t\ttry {\n\t\t\twriteRecord(path, record);\n\t\t} catch {\n\t\t\t// Cache write is best-effort.\n\t\t}\n\t}\n\n\tif (!record) return undefined;\n\treturn toResult(options.currentVersion, record, true);\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n\ttry {\n\t\treturn gt(latest, current);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction toResult(current: string, record: UpdateCheckRecord, allowNotice: boolean): UpdateCheckResult {\n\tconst newer = isNewer(record.latest, current);\n\tconst notice =\n\t\tallowNotice && newer && record.notifiedVersion !== record.latest\n\t\t\t? `lunR ${record.latest} is available. Run lunr update.`\n\t\t\t: undefined;\n\treturn { latest: record.latest, current, newer, notice };\n}\n\n/** Persist that we already showed the TUI notice for this latest version. */\nexport function markUpdateNotified(agentDir: string, latest: string): void {\n\tconst path = updateCheckPath(agentDir);\n\tconst record = existsSync(path) ? readRecord(path) : undefined;\n\tif (!record) return;\n\trecord.notifiedVersion = latest;\n\ttry {\n\t\twriteRecord(path, record);\n\t} catch {\n\t\t// ignore\n\t}\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Plan/subscription usage service. Per-provider adapters query provider quota
3
- * endpoints (5-minute cache each) so /usage can show subscription windows
3
+ * endpoints (60-second cache each) so /usage and the footer can show subscription windows
4
4
  * alongside session/context totals.
5
5
  *
6
6
  * Deliberately absent adapters:
@@ -13,6 +13,7 @@
13
13
  * Auth failures are different: they return `error` and are not cached as empty.
14
14
  */
15
15
  import type { ModelRuntime } from "./model-runtime.ts";
16
+ import type { SettingsManager } from "./settings-manager.ts";
16
17
  export interface PlanUsageWindow {
17
18
  label: string;
18
19
  usedPercent: number;
@@ -32,12 +33,34 @@ export interface PlanUsageResult {
32
33
  /** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */
33
34
  export declare function planUsageAuthError(error: unknown): string | undefined;
34
35
  export declare const PLAN_USAGE_CACHE_TTL_MS: number;
36
+ export type PlanUsageWindowPreference = "5h" | "weekly";
37
+ export declare function isWeeklyPlanWindow(window: PlanUsageWindow): boolean;
38
+ export declare function isFiveHourPlanWindow(window: PlanUsageWindow): boolean;
39
+ /** Preferred window, falling back to the other, skipping Extra/on-demand. */
40
+ export declare function pickPlanWindow(usage: PlanUsage | undefined, preferred: PlanUsageWindowPreference): PlanUsageWindow | undefined;
41
+ export declare function footerPlanLabel(window: PlanUsageWindow): string;
35
42
  /** Whether a plan-usage adapter exists for this provider. */
36
43
  export declare function hasPlanUsageAdapter(providerId: string): boolean;
37
44
  /** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */
38
45
  export declare function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined>;
39
46
  /** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */
40
47
  export declare function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult>;
48
+ /** Last fetched usage, including expired cache (footer can show stale while refetching). */
49
+ export declare function peekPlanUsage(providerId: string): PlanUsage | undefined;
41
50
  /** Clear the adapter cache (tests, /login changes). */
42
51
  export declare function clearPlanUsageCache(): void;
52
+ export declare const USAGE_SERVICE_BRIDGE_SYMBOL: unique symbol;
53
+ export interface FooterPlanSegment {
54
+ label: string;
55
+ usedPercent: number;
56
+ }
57
+ export interface UsageServiceBridge {
58
+ peek(providerId: string): PlanUsage | undefined;
59
+ prefetch(providerId: string): void;
60
+ getPreferredWindow(): PlanUsageWindowPreference;
61
+ pickForFooter(providerId: string): FooterPlanSegment | undefined;
62
+ setOnUpdate(fn: (() => void) | undefined): void;
63
+ }
64
+ export declare function registerUsageServiceBridge(runtime: ModelRuntime, settingsManager: SettingsManager): void;
65
+ export declare function getUsageServiceBridge(): UsageServiceBridge | undefined;
43
66
  //# sourceMappingURL=usage-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-service.d.ts","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMvD,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAoBD,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOrE;AAWD,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AAIrD,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAE5G;AAED,sFAAsF;AACtF,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAgB5G;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/timed out|aborted|cancelled|ECONN|ENOTFOUND|network/i.test(chain)) return undefined;\n\t// 403 on billing is often entitlement, not a dead refresh token.\n\tif (/xai billing rejected the session \\(HTTP 401\\)/i.test(chain)) return XAI_RELOGIN;\n\tif (/invalid_grant|refresh token revoked/i.test(chain)) return XAI_RELOGIN;\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\ttry {\n\t\tconst value = await adapter(runtime);\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\treturn { usage: value };\n\t} catch (error) {\n\t\tconst authError = planUsageAuthError(error);\n\t\tif (authError) return { error: authError };\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\treturn {};\n\t}\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n"]}
1
+ {"version":3,"file":"usage-service.d.ts","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAoBD,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOrE;AAWD,eAAO,MAAM,uBAAuB,QAAY,CAAC;AAEjD,MAAM,MAAM,yBAAyB,GAAG,IAAI,GAAG,QAAQ,CAAC;AAExD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAEnE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAErE;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC7B,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,SAAS,EAAE,yBAAyB,GAClC,eAAe,GAAG,SAAS,CAO7B;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAI/D;AAKD,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAE5G;AAED,sFAAsF;AACtF,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAwB5G;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEvE;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAMD,eAAO,MAAM,2BAA2B,eAAoC,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kBAAkB,IAAI,yBAAyB,CAAC;IAChD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACjE,WAAW,CAAC,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;CAChD;AA+BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,GAAG,IAAI,CAIxG;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,SAAS,CAEtE","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (60-second cache each) so /usage and the footer can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/timed out|aborted|cancelled|ECONN|ENOTFOUND|network/i.test(chain)) return undefined;\n\t// 403 on billing is often entitlement, not a dead refresh token.\n\tif (/xai billing rejected the session \\(HTTP 401\\)/i.test(chain)) return XAI_RELOGIN;\n\tif (/invalid_grant|refresh token revoked/i.test(chain)) return XAI_RELOGIN;\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 60 * 1000;\n\nexport type PlanUsageWindowPreference = \"5h\" | \"weekly\";\n\nexport function isWeeklyPlanWindow(window: PlanUsageWindow): boolean {\n\treturn /week/i.test(window.label);\n}\n\nexport function isFiveHourPlanWindow(window: PlanUsageWindow): boolean {\n\treturn /5h/i.test(window.label) || window.label === \"5h\";\n}\n\n/** Preferred window, falling back to the other, skipping Extra/on-demand. */\nexport function pickPlanWindow(\n\tusage: PlanUsage | undefined,\n\tpreferred: PlanUsageWindowPreference,\n): PlanUsageWindow | undefined {\n\tif (!usage?.windows.length) return undefined;\n\tconst usable = usage.windows.filter((w) => !/extra/i.test(w.label));\n\tconst weekly = usable.find(isWeeklyPlanWindow);\n\tconst fiveH = usable.find(isFiveHourPlanWindow);\n\tif (preferred === \"5h\") return fiveH ?? weekly ?? usable[0] ?? usage.windows[0];\n\treturn weekly ?? fiveH ?? usable[0] ?? usage.windows[0];\n}\n\nexport function footerPlanLabel(window: PlanUsageWindow): string {\n\tif (isFiveHourPlanWindow(window)) return \"5h\";\n\tif (isWeeklyPlanWindow(window)) return \"wk\";\n\treturn window.label.length <= 4 ? window.label : window.label.slice(0, 4);\n}\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\nconst inflight = new Map<string, Promise<PlanUsageResult>>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\tconst pending = inflight.get(providerId);\n\tif (pending) return pending;\n\tconst task = (async (): Promise<PlanUsageResult> => {\n\t\ttry {\n\t\t\tconst value = await adapter(runtime);\n\t\t\tcache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\t\treturn { usage: value };\n\t\t} catch (error) {\n\t\t\tconst authError = planUsageAuthError(error);\n\t\t\tif (authError) return { error: authError };\n\t\t\tcache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\t\treturn {};\n\t\t} finally {\n\t\t\tinflight.delete(providerId);\n\t\t}\n\t})();\n\tinflight.set(providerId, task);\n\treturn task;\n}\n\n/** Last fetched usage, including expired cache (footer can show stale while refetching). */\nexport function peekPlanUsage(providerId: string): PlanUsage | undefined {\n\treturn cache.get(providerId)?.value;\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n\tinflight.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n\nexport const USAGE_SERVICE_BRIDGE_SYMBOL = Symbol.for(\"@lunr/usage-service\");\n\nexport interface FooterPlanSegment {\n\tlabel: string;\n\tusedPercent: number;\n}\n\nexport interface UsageServiceBridge {\n\tpeek(providerId: string): PlanUsage | undefined;\n\tprefetch(providerId: string): void;\n\tgetPreferredWindow(): PlanUsageWindowPreference;\n\tpickForFooter(providerId: string): FooterPlanSegment | undefined;\n\tsetOnUpdate(fn: (() => void) | undefined): void;\n}\n\nlet activeRuntime: ModelRuntime | undefined;\nlet activeUsageSettings: SettingsManager | undefined;\nlet onUsageUpdate: (() => void) | undefined;\n\nfunction preferredWindow(): PlanUsageWindowPreference {\n\treturn activeUsageSettings?.getPlanUsageWindow() ?? \"weekly\";\n}\n\nconst usageBridge: UsageServiceBridge = {\n\tpeek(providerId: string): PlanUsage | undefined {\n\t\treturn peekPlanUsage(providerId);\n\t},\n\tprefetch(providerId: string): void {\n\t\tif (!activeRuntime || !hasPlanUsageAdapter(providerId)) return;\n\t\tvoid getPlanUsage(providerId, activeRuntime).then(() => onUsageUpdate?.());\n\t},\n\tsetOnUpdate(fn: (() => void) | undefined): void {\n\t\tonUsageUpdate = fn;\n\t},\n\tgetPreferredWindow(): PlanUsageWindowPreference {\n\t\treturn preferredWindow();\n\t},\n\tpickForFooter(providerId: string): FooterPlanSegment | undefined {\n\t\tconst window = pickPlanWindow(peekPlanUsage(providerId), preferredWindow());\n\t\tif (!window) return undefined;\n\t\treturn { label: footerPlanLabel(window), usedPercent: window.usedPercent };\n\t},\n};\n\nexport function registerUsageServiceBridge(runtime: ModelRuntime, settingsManager: SettingsManager): void {\n\tactiveRuntime = runtime;\n\tactiveUsageSettings = settingsManager;\n\t(globalThis as Record<symbol, unknown>)[USAGE_SERVICE_BRIDGE_SYMBOL] = usageBridge;\n}\n\nexport function getUsageServiceBridge(): UsageServiceBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[USAGE_SERVICE_BRIDGE_SYMBOL] as UsageServiceBridge | undefined;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Plan/subscription usage service. Per-provider adapters query provider quota
3
- * endpoints (5-minute cache each) so /usage can show subscription windows
3
+ * endpoints (60-second cache each) so /usage and the footer can show subscription windows
4
4
  * alongside session/context totals.
5
5
  *
6
6
  * Deliberately absent adapters:
@@ -50,8 +50,33 @@ const ADAPTERS = {
50
50
  zai: fetchZaiPlanUsage,
51
51
  xai: fetchXaiPlanUsage,
52
52
  };
53
- export const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;
53
+ export const PLAN_USAGE_CACHE_TTL_MS = 60 * 1000;
54
+ export function isWeeklyPlanWindow(window) {
55
+ return /week/i.test(window.label);
56
+ }
57
+ export function isFiveHourPlanWindow(window) {
58
+ return /5h/i.test(window.label) || window.label === "5h";
59
+ }
60
+ /** Preferred window, falling back to the other, skipping Extra/on-demand. */
61
+ export function pickPlanWindow(usage, preferred) {
62
+ if (!usage?.windows.length)
63
+ return undefined;
64
+ const usable = usage.windows.filter((w) => !/extra/i.test(w.label));
65
+ const weekly = usable.find(isWeeklyPlanWindow);
66
+ const fiveH = usable.find(isFiveHourPlanWindow);
67
+ if (preferred === "5h")
68
+ return fiveH ?? weekly ?? usable[0] ?? usage.windows[0];
69
+ return weekly ?? fiveH ?? usable[0] ?? usage.windows[0];
70
+ }
71
+ export function footerPlanLabel(window) {
72
+ if (isFiveHourPlanWindow(window))
73
+ return "5h";
74
+ if (isWeeklyPlanWindow(window))
75
+ return "wk";
76
+ return window.label.length <= 4 ? window.label : window.label.slice(0, 4);
77
+ }
54
78
  const cache = new Map();
79
+ const inflight = new Map();
55
80
  /** Whether a plan-usage adapter exists for this provider. */
56
81
  export function hasPlanUsageAdapter(providerId) {
57
82
  return providerId in ADAPTERS;
@@ -69,24 +94,76 @@ export async function getPlanUsageResult(providerId, runtime) {
69
94
  const cached = cache.get(providerId);
70
95
  if (cached && cached.expiresAt > now)
71
96
  return { usage: cached.value };
72
- try {
73
- const value = await adapter(runtime);
74
- cache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });
75
- return { usage: value };
76
- }
77
- catch (error) {
78
- const authError = planUsageAuthError(error);
79
- if (authError)
80
- return { error: authError };
81
- cache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });
82
- return {};
83
- }
97
+ const pending = inflight.get(providerId);
98
+ if (pending)
99
+ return pending;
100
+ const task = (async () => {
101
+ try {
102
+ const value = await adapter(runtime);
103
+ cache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value });
104
+ return { usage: value };
105
+ }
106
+ catch (error) {
107
+ const authError = planUsageAuthError(error);
108
+ if (authError)
109
+ return { error: authError };
110
+ cache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: undefined });
111
+ return {};
112
+ }
113
+ finally {
114
+ inflight.delete(providerId);
115
+ }
116
+ })();
117
+ inflight.set(providerId, task);
118
+ return task;
119
+ }
120
+ /** Last fetched usage, including expired cache (footer can show stale while refetching). */
121
+ export function peekPlanUsage(providerId) {
122
+ return cache.get(providerId)?.value;
84
123
  }
85
124
  /** Clear the adapter cache (tests, /login changes). */
86
125
  export function clearPlanUsageCache() {
87
126
  cache.clear();
127
+ inflight.clear();
88
128
  }
89
129
  // ---------------------------------------------------------------------------
90
130
  // Extension bridge
91
131
  // ---------------------------------------------------------------------------
132
+ export const USAGE_SERVICE_BRIDGE_SYMBOL = Symbol.for("@lunr/usage-service");
133
+ let activeRuntime;
134
+ let activeUsageSettings;
135
+ let onUsageUpdate;
136
+ function preferredWindow() {
137
+ return activeUsageSettings?.getPlanUsageWindow() ?? "weekly";
138
+ }
139
+ const usageBridge = {
140
+ peek(providerId) {
141
+ return peekPlanUsage(providerId);
142
+ },
143
+ prefetch(providerId) {
144
+ if (!activeRuntime || !hasPlanUsageAdapter(providerId))
145
+ return;
146
+ void getPlanUsage(providerId, activeRuntime).then(() => onUsageUpdate?.());
147
+ },
148
+ setOnUpdate(fn) {
149
+ onUsageUpdate = fn;
150
+ },
151
+ getPreferredWindow() {
152
+ return preferredWindow();
153
+ },
154
+ pickForFooter(providerId) {
155
+ const window = pickPlanWindow(peekPlanUsage(providerId), preferredWindow());
156
+ if (!window)
157
+ return undefined;
158
+ return { label: footerPlanLabel(window), usedPercent: window.usedPercent };
159
+ },
160
+ };
161
+ export function registerUsageServiceBridge(runtime, settingsManager) {
162
+ activeRuntime = runtime;
163
+ activeUsageSettings = settingsManager;
164
+ globalThis[USAGE_SERVICE_BRIDGE_SYMBOL] = usageBridge;
165
+ }
166
+ export function getUsageServiceBridge() {
167
+ return globalThis[USAGE_SERVICE_BRIDGE_SYMBOL];
168
+ }
92
169
  //# sourceMappingURL=usage-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/timed out|aborted|cancelled|ECONN|ENOTFOUND|network/i.test(chain)) return undefined;\n\t// 403 on billing is often entitlement, not a dead refresh token.\n\tif (/xai billing rejected the session \\(HTTP 401\\)/i.test(chain)) return XAI_RELOGIN;\n\tif (/invalid_grant|refresh token revoked/i.test(chain)) return XAI_RELOGIN;\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\ttry {\n\t\tconst value = await adapter(runtime);\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\treturn { usage: value };\n\t} catch (error) {\n\t\tconst authError = planUsageAuthError(error);\n\t\tif (authError) return { error: authError };\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\treturn {};\n\t}\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n"]}
1
+ {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (60-second cache each) so /usage and the footer can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/timed out|aborted|cancelled|ECONN|ENOTFOUND|network/i.test(chain)) return undefined;\n\t// 403 on billing is often entitlement, not a dead refresh token.\n\tif (/xai billing rejected the session \\(HTTP 401\\)/i.test(chain)) return XAI_RELOGIN;\n\tif (/invalid_grant|refresh token revoked/i.test(chain)) return XAI_RELOGIN;\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 60 * 1000;\n\nexport type PlanUsageWindowPreference = \"5h\" | \"weekly\";\n\nexport function isWeeklyPlanWindow(window: PlanUsageWindow): boolean {\n\treturn /week/i.test(window.label);\n}\n\nexport function isFiveHourPlanWindow(window: PlanUsageWindow): boolean {\n\treturn /5h/i.test(window.label) || window.label === \"5h\";\n}\n\n/** Preferred window, falling back to the other, skipping Extra/on-demand. */\nexport function pickPlanWindow(\n\tusage: PlanUsage | undefined,\n\tpreferred: PlanUsageWindowPreference,\n): PlanUsageWindow | undefined {\n\tif (!usage?.windows.length) return undefined;\n\tconst usable = usage.windows.filter((w) => !/extra/i.test(w.label));\n\tconst weekly = usable.find(isWeeklyPlanWindow);\n\tconst fiveH = usable.find(isFiveHourPlanWindow);\n\tif (preferred === \"5h\") return fiveH ?? weekly ?? usable[0] ?? usage.windows[0];\n\treturn weekly ?? fiveH ?? usable[0] ?? usage.windows[0];\n}\n\nexport function footerPlanLabel(window: PlanUsageWindow): string {\n\tif (isFiveHourPlanWindow(window)) return \"5h\";\n\tif (isWeeklyPlanWindow(window)) return \"wk\";\n\treturn window.label.length <= 4 ? window.label : window.label.slice(0, 4);\n}\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\nconst inflight = new Map<string, Promise<PlanUsageResult>>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\tconst pending = inflight.get(providerId);\n\tif (pending) return pending;\n\tconst task = (async (): Promise<PlanUsageResult> => {\n\t\ttry {\n\t\t\tconst value = await adapter(runtime);\n\t\t\tcache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\t\treturn { usage: value };\n\t\t} catch (error) {\n\t\t\tconst authError = planUsageAuthError(error);\n\t\t\tif (authError) return { error: authError };\n\t\t\tcache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\t\treturn {};\n\t\t} finally {\n\t\t\tinflight.delete(providerId);\n\t\t}\n\t})();\n\tinflight.set(providerId, task);\n\treturn task;\n}\n\n/** Last fetched usage, including expired cache (footer can show stale while refetching). */\nexport function peekPlanUsage(providerId: string): PlanUsage | undefined {\n\treturn cache.get(providerId)?.value;\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n\tinflight.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n\nexport const USAGE_SERVICE_BRIDGE_SYMBOL = Symbol.for(\"@lunr/usage-service\");\n\nexport interface FooterPlanSegment {\n\tlabel: string;\n\tusedPercent: number;\n}\n\nexport interface UsageServiceBridge {\n\tpeek(providerId: string): PlanUsage | undefined;\n\tprefetch(providerId: string): void;\n\tgetPreferredWindow(): PlanUsageWindowPreference;\n\tpickForFooter(providerId: string): FooterPlanSegment | undefined;\n\tsetOnUpdate(fn: (() => void) | undefined): void;\n}\n\nlet activeRuntime: ModelRuntime | undefined;\nlet activeUsageSettings: SettingsManager | undefined;\nlet onUsageUpdate: (() => void) | undefined;\n\nfunction preferredWindow(): PlanUsageWindowPreference {\n\treturn activeUsageSettings?.getPlanUsageWindow() ?? \"weekly\";\n}\n\nconst usageBridge: UsageServiceBridge = {\n\tpeek(providerId: string): PlanUsage | undefined {\n\t\treturn peekPlanUsage(providerId);\n\t},\n\tprefetch(providerId: string): void {\n\t\tif (!activeRuntime || !hasPlanUsageAdapter(providerId)) return;\n\t\tvoid getPlanUsage(providerId, activeRuntime).then(() => onUsageUpdate?.());\n\t},\n\tsetOnUpdate(fn: (() => void) | undefined): void {\n\t\tonUsageUpdate = fn;\n\t},\n\tgetPreferredWindow(): PlanUsageWindowPreference {\n\t\treturn preferredWindow();\n\t},\n\tpickForFooter(providerId: string): FooterPlanSegment | undefined {\n\t\tconst window = pickPlanWindow(peekPlanUsage(providerId), preferredWindow());\n\t\tif (!window) return undefined;\n\t\treturn { label: footerPlanLabel(window), usedPercent: window.usedPercent };\n\t},\n};\n\nexport function registerUsageServiceBridge(runtime: ModelRuntime, settingsManager: SettingsManager): void {\n\tactiveRuntime = runtime;\n\tactiveUsageSettings = settingsManager;\n\t(globalThis as Record<symbol, unknown>)[USAGE_SERVICE_BRIDGE_SYMBOL] = usageBridge;\n}\n\nexport function getUsageServiceBridge(): UsageServiceBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[USAGE_SERVICE_BRIDGE_SYMBOL] as UsageServiceBridge | undefined;\n}\n"]}
@@ -91,6 +91,7 @@ export interface BridgeSession {
91
91
  label?: string;
92
92
  }): Promise<{
93
93
  editorText?: string;
94
+ editorImages?: ImageContent[];
94
95
  cancelled: boolean;
95
96
  aborted?: boolean;
96
97
  }>;