@ashx-j/lunr 0.2.26 → 0.2.28
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.
- package/CHANGELOG.md +25 -1
- package/LICENSE +22 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +6 -10
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
- package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-cron.js +94 -108
- package/dist/builtin-extensions/lunr-cron.js.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js +5 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +2 -2
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
- package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
- package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
- package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts +4 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +6 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +17 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +5 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +78 -42
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts +0 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +134 -28
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +26 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts +7 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js +70 -38
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +9 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +13 -62
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +18 -9
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-tps.js +28 -17
- package/dist/builtin-extensions/pi-tps.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
- package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
- package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +48 -73
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
- package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
- package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/google-vertex.json +1 -1
- package/dist/catalog/providers/google.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/cli/install-cli.d.ts.map +1 -1
- package/dist/cli/install-cli.js +38 -30
- package/dist/cli/install-cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +13 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +75 -11
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +46 -21
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +344 -126
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/browser/runtime.d.ts +2 -0
- package/dist/core/browser/runtime.d.ts.map +1 -1
- package/dist/core/browser/runtime.js +80 -45
- package/dist/core/browser/runtime.js.map +1 -1
- package/dist/core/browser/setup.d.ts.map +1 -1
- package/dist/core/browser/setup.js +12 -1
- package/dist/core/browser/setup.js.map +1 -1
- package/dist/core/cache-stats.d.ts +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +5 -11
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +38 -92
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts.map +1 -1
- package/dist/core/computer-use/adapter.js +7 -2
- package/dist/core/computer-use/adapter.js.map +1 -1
- package/dist/core/computer-use/image.d.ts.map +1 -1
- package/dist/core/computer-use/image.js +17 -5
- package/dist/core/computer-use/image.js.map +1 -1
- package/dist/core/computer-use/schemas.d.ts.map +1 -1
- package/dist/core/computer-use/schemas.js +23 -6
- package/dist/core/computer-use/schemas.js.map +1 -1
- package/dist/core/computer-use/workflow.d.ts.map +1 -1
- package/dist/core/computer-use/workflow.js +459 -119
- package/dist/core/computer-use/workflow.js.map +1 -1
- package/dist/core/context-breakdown.d.ts +2 -2
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +22 -20
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/cron/jobs.d.ts +26 -8
- package/dist/core/cron/jobs.d.ts.map +1 -1
- package/dist/core/cron/jobs.js +357 -232
- package/dist/core/cron/jobs.js.map +1 -1
- package/dist/core/cron/origin-context.d.ts +1 -0
- package/dist/core/cron/origin-context.d.ts.map +1 -1
- package/dist/core/cron/origin-context.js.map +1 -1
- package/dist/core/cron/scheduler.d.ts +22 -17
- package/dist/core/cron/scheduler.d.ts.map +1 -1
- package/dist/core/cron/scheduler.js +153 -73
- package/dist/core/cron/scheduler.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +11 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +4 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +22 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +1 -1
- package/dist/core/large-subagent-launch.d.ts.map +1 -1
- package/dist/core/large-subagent-launch.js +7 -7
- package/dist/core/large-subagent-launch.js.map +1 -1
- package/dist/core/model-registry.d.ts +2 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-runtime.d.ts +4 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +40 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +3 -3
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +11 -35
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts +5 -13
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +126 -116
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-resource-bridge.d.ts +12 -0
- package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
- package/dist/core/prompt-resource-bridge.js +9 -0
- package/dist/core/prompt-resource-bridge.js.map +1 -0
- package/dist/core/protected-targets.d.ts +6 -0
- package/dist/core/protected-targets.d.ts.map +1 -0
- package/dist/core/protected-targets.js +45 -0
- package/dist/core/protected-targets.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +7 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/rollback.d.ts +19 -3
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +172 -56
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +3 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +14 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +111 -33
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-ownership.d.ts.map +1 -1
- package/dist/core/session-ownership.js +8 -3
- package/dist/core/session-ownership.js.map +1 -1
- package/dist/core/session-retention.d.ts.map +1 -1
- package/dist/core/session-retention.js +18 -7
- package/dist/core/session-retention.js.map +1 -1
- package/dist/core/session-startup-settings.d.ts +10 -0
- package/dist/core/session-startup-settings.d.ts.map +1 -0
- package/dist/core/session-startup-settings.js +70 -0
- package/dist/core/session-startup-settings.js.map +1 -0
- package/dist/core/subscriptions.d.ts +16 -7
- package/dist/core/subscriptions.d.ts.map +1 -1
- package/dist/core/subscriptions.js +96 -55
- package/dist/core/subscriptions.js.map +1 -1
- package/dist/core/trust-manager.d.ts +2 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +6 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/update-check.d.ts.map +1 -1
- package/dist/core/update-check.js.map +1 -1
- package/dist/core/usage-accounting.d.ts +47 -0
- package/dist/core/usage-accounting.d.ts.map +1 -0
- package/dist/core/usage-accounting.js +205 -0
- package/dist/core/usage-accounting.js.map +1 -0
- package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
- package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
- package/dist/core/usage-adapters/kimi-coding.js +2 -2
- package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
- package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
- package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
- package/dist/core/usage-adapters/openai-codex.js +3 -3
- package/dist/core/usage-adapters/openai-codex.js.map +1 -1
- package/dist/core/usage-adapters/xai.d.ts +2 -2
- package/dist/core/usage-adapters/xai.d.ts.map +1 -1
- package/dist/core/usage-adapters/xai.js +3 -3
- package/dist/core/usage-adapters/xai.js.map +1 -1
- package/dist/core/usage-adapters/zai.d.ts +2 -2
- package/dist/core/usage-adapters/zai.d.ts.map +1 -1
- package/dist/core/usage-adapters/zai.js +2 -2
- package/dist/core/usage-adapters/zai.js.map +1 -1
- package/dist/core/usage-history.d.ts +1 -18
- package/dist/core/usage-history.d.ts.map +1 -1
- package/dist/core/usage-history.js +46 -114
- package/dist/core/usage-history.js.map +1 -1
- package/dist/core/usage-service.d.ts +9 -2
- package/dist/core/usage-service.d.ts.map +1 -1
- package/dist/core/usage-service.js +95 -50
- package/dist/core/usage-service.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts +9 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +91 -37
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/buttons.d.ts +8 -1
- package/dist/gateway/buttons.d.ts.map +1 -1
- package/dist/gateway/buttons.js +76 -18
- package/dist/gateway/buttons.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -0
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +10 -4
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts +12 -11
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +65 -56
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +2 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/mobile-commands.d.ts +1 -0
- package/dist/gateway/mobile-commands.d.ts.map +1 -1
- package/dist/gateway/mobile-commands.js +69 -15
- package/dist/gateway/mobile-commands.js.map +1 -1
- package/dist/gateway/presenter.d.ts +12 -2
- package/dist/gateway/presenter.d.ts.map +1 -1
- package/dist/gateway/presenter.js +73 -38
- package/dist/gateway/presenter.js.map +1 -1
- package/dist/gateway/router.d.ts +2 -2
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +17 -2
- package/dist/gateway/router.js.map +1 -1
- package/dist/gateway/service.d.ts +2 -0
- package/dist/gateway/service.d.ts.map +1 -1
- package/dist/gateway/service.js +153 -0
- package/dist/gateway/service.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +86 -33
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +6 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-view.js +2 -2
- package/dist/modes/interactive/components/context-view.js.map +1 -1
- package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
- package/dist/modes/interactive/components/copyable-text.js +3 -2
- package/dist/modes/interactive/components/copyable-text.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/usage-view.d.ts +5 -0
- package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/usage-view.js +21 -6
- package/dist/modes/interactive/components/usage-view.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -7
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +151 -131
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
- package/dist/node-runtime/agent-session-services-RTUGP3XR.js +54 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-N4EUD6SB.js} +8 -7
- package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
- package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
- package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
- package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
- package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
- package/dist/node-runtime/chunk-4QRUZ64T.js +1203 -0
- package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
- package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
- package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
- package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-BMRXLQWQ.js} +464 -160
- package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-DFMPRAQQ.js} +162 -82
- package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
- package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-E5DQGNJI.js} +455 -278
- package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
- package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
- package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
- package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
- package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
- package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
- package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
- package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
- package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
- package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
- package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
- package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
- package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
- package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-ICKGAKWX.js} +23 -59
- package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
- package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
- package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
- package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JEK35V2M.js} +45 -31
- package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
- package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
- package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
- package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-PWHTSQC4.js} +83 -21
- package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-QT3F6UEV.js} +233 -169
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-RY3FJ6WI.js} +29 -27
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-STMX7W5I.js} +3 -3
- package/dist/node-runtime/chunk-SWWHZ2RI.js +158 -0
- package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
- package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-U3YVZ6UC.js} +18 -14
- package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
- package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
- package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
- package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
- package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
- package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
- package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
- package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
- package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
- package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-FSBU2EVZ.js → command-YRT7I2BW.js} +5 -5
- package/dist/node-runtime/{commands-NAAL7KNT.js → commands-2ETOWWRA.js} +16 -16
- package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
- package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
- package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
- package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-DBQRRMAY.js} +4 -4
- package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
- package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
- package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
- package/dist/node-runtime/{gateway-DQM333YV.js → gateway-V6K2CDWN.js} +51 -42
- package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
- package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
- package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
- package/dist/node-runtime/{goal-NJEE5CTU.js → goal-J7TYMOBK.js} +14 -8
- package/dist/node-runtime/index.js +51 -48
- package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
- package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-ASZ7APYP.js +75 -0
- package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
- package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
- package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
- package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
- package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-WD3YH4CR.js} +93 -80
- package/dist/node-runtime/main.js +43 -41
- package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-ICUXFW5V.js} +12 -12
- package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
- package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
- package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
- package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
- package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
- package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
- package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
- package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
- package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
- package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-QPU3OXE4.js} +114 -167
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
- package/dist/node-runtime/rpc-entry.js +43 -41
- package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
- package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
- package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
- package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LYH53ESV.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-ZJ3N5Q74.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-UI6SHG3X.js} +412 -288
- package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
- package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
- package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
- package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
- package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
- package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
- package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
- package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +25 -10
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +2 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +33 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/sanitize-terminal-text.d.ts +7 -0
- package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
- package/dist/utils/sanitize-terminal-text.js +67 -0
- package/dist/utils/sanitize-terminal-text.js.map +1 -0
- package/docs/browser.md +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +25 -7
- package/docs/keybindings.md +1 -1
- package/docs/models.md +5 -5
- package/docs/packages.md +1 -1
- package/docs/providers.md +2 -2
- package/docs/rpc.md +7 -7
- package/docs/security.md +10 -5
- package/docs/sessions.md +2 -2
- package/docs/skills.md +1 -1
- package/docs/terminal-setup.md +7 -7
- package/docs/themes.md +1 -1
- package/docs/usage.md +1 -1
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +2 -2
- package/examples/extensions/gondolin/index.ts +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +2 -2
- package/examples/extensions/input-transform-streaming.ts +2 -2
- package/examples/extensions/input-transform.ts +2 -2
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/kimi-deferred-tools.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +3 -3
- package/examples/extensions/overlay-qa-tests.ts +1 -1
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/preset.ts +3 -3
- package/examples/extensions/project-trust.ts +4 -4
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +7 -7
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/shutdown-command.ts +5 -5
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/subagent/README.md +3 -3
- package/examples/extensions/subagent/index.ts +2 -2
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/06-extensions.ts +2 -2
- package/examples/sdk/08-prompt-templates.ts +1 -1
- package/install-lock/package-lock.json +24 -24
- package/install-lock/package.json +2 -2
- package/npm-shrinkwrap.json +60 -24
- package/package.json +9 -9
- package/dist/modes/interactive/components/exit-card.d.ts +0 -22
- package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
- package/dist/modes/interactive/components/exit-card.js +0 -69
- package/dist/modes/interactive/components/exit-card.js.map +0 -1
- package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
- package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
- package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
- package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
- package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
- package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,qBAAqB,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA+BzF,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,YAAY,EACrB,IAAI,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAyChC","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow, UsageAuthSnapshot } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(\n\truntime: ModelRuntime,\n\tauth?: UsageAuthSnapshot,\n): Promise<PlanUsage | undefined> {\n\tif (!(auth?.oauth ?? runtime.isUsingOAuth(XAI_PROVIDER_ID))) return undefined;\n\n\tconst resolution = auth ? auth.resolution : await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (response.status === 401 || response.status === 403) {\n\t\tthrow new ModelsError(\"oauth\", `xAI billing rejected the session (HTTP ${response.status})`);\n\t}\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
|
|
@@ -35,10 +35,10 @@ function weeklyWindow(config) {
|
|
|
35
35
|
resetsAt: toEpochMs(period?.end),
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
export async function fetchXaiPlanUsage(runtime) {
|
|
39
|
-
if (!runtime.isUsingOAuth(XAI_PROVIDER_ID))
|
|
38
|
+
export async function fetchXaiPlanUsage(runtime, auth) {
|
|
39
|
+
if (!(auth?.oauth ?? runtime.isUsingOAuth(XAI_PROVIDER_ID)))
|
|
40
40
|
return undefined;
|
|
41
|
-
const resolution = await runtime.getAuth(XAI_PROVIDER_ID);
|
|
41
|
+
const resolution = auth ? auth.resolution : await runtime.getAuth(XAI_PROVIDER_ID);
|
|
42
42
|
const accessToken = resolution?.auth.apiKey;
|
|
43
43
|
if (!accessToken)
|
|
44
44
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACN,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,mBAAmB,GACnB,MAAM,aAAa,CAAC;AAErB,MAAM,eAAe,GAAG,KAAK,CAAC;AAC9B,MAAM,eAAe,GAAG,2DAA2D,CAAC;AAEpF,SAAS,aAAa,CAAC,MAA+B,EAAuC;IAC5F,OAAO,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAAA,CACzE;AAED,SAAS,YAAY,CAAC,MAA+B,EAA+B;IACnF,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChF,0EAA0E;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,WAAW,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC9D,OAAO;QACN,KAAK,EAAE,QAAQ;QACf,WAAW;QACX,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC;KAChC,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,
|
|
1
|
+
{"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow, UsageAuthSnapshot } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(\n\truntime: ModelRuntime,\n\tauth?: UsageAuthSnapshot,\n): Promise<PlanUsage | undefined> {\n\tif (!(auth?.oauth ?? runtime.isUsingOAuth(XAI_PROVIDER_ID))) return undefined;\n\n\tconst resolution = auth ? auth.resolution : await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (response.status === 401 || response.status === 403) {\n\t\tthrow new ModelsError(\"oauth\", `xAI billing rejected the session (HTTP ${response.status})`);\n\t}\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
* `unit`: 1 = days, 3 = hours, 5 = minutes, 6 = weeks.
|
|
10
10
|
*/
|
|
11
11
|
import type { ModelRuntime } from "../model-runtime.ts";
|
|
12
|
-
import type { PlanUsage } from "../usage-service.ts";
|
|
13
|
-
export declare function fetchZaiPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined>;
|
|
12
|
+
import type { PlanUsage, UsageAuthSnapshot } from "../usage-service.ts";
|
|
13
|
+
export declare function fetchZaiPlanUsage(runtime: ModelRuntime, auth?: UsageAuthSnapshot): Promise<PlanUsage | undefined>;
|
|
14
14
|
//# sourceMappingURL=zai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/zai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/zai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAmBzF,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,YAAY,EACrB,IAAI,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CA8BhC","sourcesContent":["/**\n * Z.AI (GLM coding plan) usage: `GET https://api.z.ai/api/monitor/usage/quota/limit`\n * with the stored API key. Response shape (undocumented, used by the official\n * plugin and several open-source quota dashboards):\n * { success, code, msg,\n * data: { planName?/plan?/level?,\n * limits: [{ type: \"TOKENS_LIMIT\"|\"TIME_LIMIT\", unit, number,\n * percentage, nextResetTime? }] } }\n * `unit`: 1 = days, 3 = hours, 5 = minutes, 6 = weeks.\n */\n\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow, UsageAuthSnapshot } from \"../usage-service.ts\";\nimport { asNumber, asObject, asString, clampPercent, fetchWithTimeout, toEpochMs } from \"./shared.ts\";\n\nconst ZAI_PROVIDER_ID = \"zai\";\nconst ZAI_QUOTA_URL = \"https://api.z.ai/api/monitor/usage/quota/limit\";\n\nfunction limitLabel(limit: Record<string, unknown>): string | undefined {\n\tconst type = asString(limit.type);\n\tif (type === \"TIME_LIMIT\") return \"Monthly\";\n\tif (type !== \"TOKENS_LIMIT\") return undefined;\n\tconst unit = asNumber(limit.unit);\n\tconst number = asNumber(limit.number);\n\tif (unit === 1 && number !== undefined) return `${number}d`;\n\tif (unit === 3 && number !== undefined) return `${number}h`;\n\tif (unit === 5 && number !== undefined) return `${number}m`;\n\tif (unit === 6) return \"Weekly\";\n\treturn \"Limit\";\n}\n\nexport async function fetchZaiPlanUsage(\n\truntime: ModelRuntime,\n\tauth?: UsageAuthSnapshot,\n): Promise<PlanUsage | undefined> {\n\tconst resolution = auth ? auth.resolution : await runtime.getAuth(ZAI_PROVIDER_ID);\n\tconst apiKey = resolution?.auth.apiKey;\n\tif (!apiKey) return undefined;\n\n\tconst response = await fetchWithTimeout(ZAI_QUOTA_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Accept-Language\": \"en-US,en\",\n\t\t},\n\t});\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload || payload.success !== true || asNumber(payload.code) !== 200) return undefined;\n\n\tconst data = asObject(payload.data);\n\tconst limits = Array.isArray(data?.limits) ? data.limits : [];\n\tconst windows: PlanUsageWindow[] = [];\n\tfor (const item of limits) {\n\t\tconst limit = asObject(item);\n\t\tif (!limit) continue;\n\t\tconst label = limitLabel(limit);\n\t\tconst percentage = asNumber(limit.percentage);\n\t\tif (!label || percentage === undefined) continue;\n\t\twindows.push({ label, usedPercent: clampPercent(percentage), resetsAt: toEpochMs(limit.nextResetTime) });\n\t}\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(data?.planName) ?? asString(data?.plan) ?? asString(data?.level);\n\treturn { provider: ZAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
|
|
@@ -29,8 +29,8 @@ function limitLabel(limit) {
|
|
|
29
29
|
return "Weekly";
|
|
30
30
|
return "Limit";
|
|
31
31
|
}
|
|
32
|
-
export async function fetchZaiPlanUsage(runtime) {
|
|
33
|
-
const resolution = await runtime.getAuth(ZAI_PROVIDER_ID);
|
|
32
|
+
export async function fetchZaiPlanUsage(runtime, auth) {
|
|
33
|
+
const resolution = auth ? auth.resolution : await runtime.getAuth(ZAI_PROVIDER_ID);
|
|
34
34
|
const apiKey = resolution?.auth.apiKey;
|
|
35
35
|
if (!apiKey)
|
|
36
36
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/zai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEtG,MAAM,eAAe,GAAG,KAAK,CAAC;AAC9B,MAAM,aAAa,GAAG,gDAAgD,CAAC;AAEvE,SAAS,UAAU,CAAC,KAA8B,EAAsB;IACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,IAAI,KAAK,cAAc;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,MAAM,GAAG,CAAC;IAC5D,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,MAAM,GAAG,CAAC;IAC5D,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,MAAM,GAAG,CAAC;IAC5D,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChC,OAAO,OAAO,CAAC;AAAA,CACf;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,
|
|
1
|
+
{"version":3,"file":"zai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/zai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Z.AI (GLM coding plan) usage: `GET https://api.z.ai/api/monitor/usage/quota/limit`\n * with the stored API key. Response shape (undocumented, used by the official\n * plugin and several open-source quota dashboards):\n * { success, code, msg,\n * data: { planName?/plan?/level?,\n * limits: [{ type: \"TOKENS_LIMIT\"|\"TIME_LIMIT\", unit, number,\n * percentage, nextResetTime? }] } }\n * `unit`: 1 = days, 3 = hours, 5 = minutes, 6 = weeks.\n */\n\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow, UsageAuthSnapshot } from \"../usage-service.ts\";\nimport { asNumber, asObject, asString, clampPercent, fetchWithTimeout, toEpochMs } from \"./shared.ts\";\n\nconst ZAI_PROVIDER_ID = \"zai\";\nconst ZAI_QUOTA_URL = \"https://api.z.ai/api/monitor/usage/quota/limit\";\n\nfunction limitLabel(limit: Record<string, unknown>): string | undefined {\n\tconst type = asString(limit.type);\n\tif (type === \"TIME_LIMIT\") return \"Monthly\";\n\tif (type !== \"TOKENS_LIMIT\") return undefined;\n\tconst unit = asNumber(limit.unit);\n\tconst number = asNumber(limit.number);\n\tif (unit === 1 && number !== undefined) return `${number}d`;\n\tif (unit === 3 && number !== undefined) return `${number}h`;\n\tif (unit === 5 && number !== undefined) return `${number}m`;\n\tif (unit === 6) return \"Weekly\";\n\treturn \"Limit\";\n}\n\nexport async function fetchZaiPlanUsage(\n\truntime: ModelRuntime,\n\tauth?: UsageAuthSnapshot,\n): Promise<PlanUsage | undefined> {\n\tconst resolution = auth ? auth.resolution : await runtime.getAuth(ZAI_PROVIDER_ID);\n\tconst apiKey = resolution?.auth.apiKey;\n\tif (!apiKey) return undefined;\n\n\tconst response = await fetchWithTimeout(ZAI_QUOTA_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Accept-Language\": \"en-US,en\",\n\t\t},\n\t});\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload || payload.success !== true || asNumber(payload.code) !== 200) return undefined;\n\n\tconst data = asObject(payload.data);\n\tconst limits = Array.isArray(data?.limits) ? data.limits : [];\n\tconst windows: PlanUsageWindow[] = [];\n\tfor (const item of limits) {\n\t\tconst limit = asObject(item);\n\t\tif (!limit) continue;\n\t\tconst label = limitLabel(limit);\n\t\tconst percentage = asNumber(limit.percentage);\n\t\tif (!label || percentage === undefined) continue;\n\t\twindows.push({ label, usedPercent: clampPercent(percentage), resetsAt: toEpochMs(limit.nextResetTime) });\n\t}\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(data?.planName) ?? asString(data?.plan) ?? asString(data?.level);\n\treturn { provider: ZAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lunr: 30-day token usage history for the /usage command.
|
|
3
|
-
*
|
|
4
|
-
* Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the
|
|
5
|
-
* flat and the per-project-subdirectory layouts) and aggregates:
|
|
6
|
-
* - REAL token totals per provider/model from assistant-message `usage`
|
|
7
|
-
* metadata (same fields the live session rows use),
|
|
8
|
-
* - per-day totals (YYYY-MM-DD buckets from the entry timestamp),
|
|
9
|
-
* - an ESTIMATED category breakdown (chars/4) via the same accounting as
|
|
10
|
-
* `computeContextBreakdown` — session files don't store the system prompt or
|
|
11
|
-
* tool definitions, so `includesSystemPrompt` is always false and the
|
|
12
|
-
* message categories carry the whole breakdown.
|
|
13
|
-
*
|
|
14
|
-
* Results are cached per file keyed on mtime+size; unchanged files are not
|
|
15
|
-
* re-parsed. Never throws — corrupt files are skipped, total failure returns
|
|
16
|
-
* an empty aggregate.
|
|
17
|
-
*/
|
|
18
1
|
export interface UsageHistoryModelRow {
|
|
19
2
|
model: string;
|
|
20
3
|
input: number;
|
|
@@ -53,7 +36,7 @@ export interface UsageHistory {
|
|
|
53
36
|
/** Test hook: drop all cached per-file aggregates. */
|
|
54
37
|
export declare function resetUsageHistoryCache(): void;
|
|
55
38
|
/**
|
|
56
|
-
* Aggregate
|
|
39
|
+
* Aggregate requests since `sinceMs`; file modification time only narrows the scan.
|
|
57
40
|
* Never throws; on total failure returns an empty aggregate.
|
|
58
41
|
*/
|
|
59
42
|
export declare function collectUsageHistory(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-history.d.ts","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,EAAE,sBAAsB,CAAC;IACnC,6EAA6E;IAC7E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gEAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAaD,sDAAsD;AACtD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAoID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CA2EpG","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"usage-history.d.ts","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,EAAE,sBAAsB,CAAC;IACnC,6EAA6E;IAC7E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gEAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAWD,sDAAsD;AACtD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAsDD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CA+EpG","sourcesContent":["import { type AccountedRequest, collectUsageRequests, usageTokens } from \"./usage-accounting.ts\";\n/**\n * Request usage history for integrations.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - Token totals per provider/model from conversation and auxiliary-request `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport { loadEntriesFromFile, type SessionEntry, sessionEntryToContextMessages } from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\trequests: AccountedRequest[];\n\tentries: SessionEntry[];\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Cache parsed records, not date-window aggregates, so changing the window stays correct. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\ttry {\n\t\tconst entries = loadEntriesFromFile(filePath).filter((entry): entry is SessionEntry => entry.type !== \"session\");\n\t\treturn { mtimeMs, size, entries, requests: collectUsageRequests(entries) };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Aggregate requests since `sinceMs`; file modification time only narrows the scan.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\t\tconst seenRequests = new Set<string>();\n\t\tconst seenEntries = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tlet hasUsage = false;\n\t\t\tfor (const request of aggregate.requests) {\n\t\t\t\tif (request.timestamp < options.sinceMs || seenRequests.has(request.id)) continue;\n\t\t\t\tseenRequests.add(request.id);\n\t\t\t\thasUsage = true;\n\t\t\t\tconst model = `${request.provider}/${request.model}`;\n\t\t\t\tlet row = result.perModel.find((candidate) => candidate.model === model);\n\t\t\t\tif (!row) {\n\t\t\t\t\trow = { model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(row);\n\t\t\t\t}\n\t\t\t\trow.input += request.usage.input;\n\t\t\t\trow.output += request.usage.output;\n\t\t\t\trow.cacheRead += request.usage.cacheRead;\n\t\t\t\trow.cacheWrite += request.usage.cacheWrite;\n\t\t\t\trow.total += usageTokens(request.usage);\n\t\t\t\tconst day = new Date(request.timestamp).toISOString().slice(0, 10);\n\t\t\t\tconst dayRow = result.perDay.find((candidate) => candidate.day === day);\n\t\t\t\tif (dayRow) dayRow.total += usageTokens(request.usage);\n\t\t\t\telse result.perDay.push({ day, total: usageTokens(request.usage) });\n\t\t\t}\n\t\t\tif (hasUsage) result.sessionsWithUsage++;\n\t\t\tconst messages = aggregate.entries\n\t\t\t\t.filter((entry) => {\n\t\t\t\t\tconst identity = `${entry.id}:${entry.timestamp}`;\n\t\t\t\t\tif (entry.inherited || Date.parse(entry.timestamp) < options.sinceMs || seenEntries.has(identity))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tseenEntries.add(identity);\n\t\t\t\t\treturn true;\n\t\t\t\t})\n\t\t\t\t.flatMap(sessionEntryToContextMessages);\n\t\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\t\tfor (const key of Object.keys(result.categories) as Array<keyof UsageHistoryCategories>)\n\t\t\t\tresult.categories[key] += breakdown[key];\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { collectUsageRequests, usageTokens } from "./usage-accounting.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Request usage history for integrations.
|
|
3
4
|
*
|
|
4
5
|
* Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the
|
|
5
6
|
* flat and the per-project-subdirectory layouts) and aggregates:
|
|
6
|
-
* -
|
|
7
|
+
* - Token totals per provider/model from conversation and auxiliary-request `usage`
|
|
7
8
|
* metadata (same fields the live session rows use),
|
|
8
9
|
* - per-day totals (YYYY-MM-DD buckets from the entry timestamp),
|
|
9
10
|
* - an ESTIMATED category breakdown (chars/4) via the same accounting as
|
|
@@ -19,7 +20,7 @@ import { readdirSync, statSync } from "node:fs";
|
|
|
19
20
|
import { join } from "node:path";
|
|
20
21
|
import { getSessionsDir } from "../config.js";
|
|
21
22
|
import { computeContextBreakdown } from "./context-breakdown.js";
|
|
22
|
-
import { loadEntriesFromFile, sessionEntryToContextMessages
|
|
23
|
+
import { loadEntriesFromFile, sessionEntryToContextMessages } from "./session-manager.js";
|
|
23
24
|
const fileCache = new Map();
|
|
24
25
|
/** Test hook: drop all cached per-file aggregates. */
|
|
25
26
|
export function resetUsageHistoryCache() {
|
|
@@ -68,93 +69,18 @@ function enumerateSessionFiles(sessionsDir) {
|
|
|
68
69
|
}
|
|
69
70
|
return files;
|
|
70
71
|
}
|
|
71
|
-
/**
|
|
72
|
-
function entryTimestampMs(entry, message, fallbackMs) {
|
|
73
|
-
const entryMs = Date.parse(entry.timestamp ?? "");
|
|
74
|
-
if (!Number.isNaN(entryMs))
|
|
75
|
-
return entryMs;
|
|
76
|
-
if (typeof message.timestamp === "number" && message.timestamp > 0)
|
|
77
|
-
return message.timestamp;
|
|
78
|
-
return fallbackMs;
|
|
79
|
-
}
|
|
80
|
-
/** Parse one session file into its aggregate. Returns null when the file is unusable. */
|
|
72
|
+
/** Cache parsed records, not date-window aggregates, so changing the window stays correct. */
|
|
81
73
|
function parseSessionFile(filePath, mtimeMs, size) {
|
|
82
|
-
let entries;
|
|
83
74
|
try {
|
|
84
|
-
entries = loadEntriesFromFile(filePath);
|
|
75
|
+
const entries = loadEntriesFromFile(filePath).filter((entry) => entry.type !== "session");
|
|
76
|
+
return { mtimeMs, size, entries, requests: collectUsageRequests(entries) };
|
|
85
77
|
}
|
|
86
78
|
catch {
|
|
87
79
|
return null;
|
|
88
80
|
}
|
|
89
|
-
if (entries.length === 0)
|
|
90
|
-
return null;
|
|
91
|
-
const aggregate = {
|
|
92
|
-
mtimeMs,
|
|
93
|
-
size,
|
|
94
|
-
perModel: new Map(),
|
|
95
|
-
perDay: new Map(),
|
|
96
|
-
categories: emptyCategories(),
|
|
97
|
-
hasUsage: false,
|
|
98
|
-
};
|
|
99
|
-
const sessionEntries = [];
|
|
100
|
-
for (const entry of entries) {
|
|
101
|
-
if (entry.type === "session")
|
|
102
|
-
continue; // header
|
|
103
|
-
sessionEntries.push(entry);
|
|
104
|
-
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
105
|
-
continue;
|
|
106
|
-
const message = entry.message;
|
|
107
|
-
const usage = message.usage;
|
|
108
|
-
if (!usage || typeof usage.input !== "number" || typeof usage.output !== "number")
|
|
109
|
-
continue;
|
|
110
|
-
const provider = typeof message.provider === "string" && message.provider ? message.provider : "unknown";
|
|
111
|
-
const modelId = message.responseModel ?? message.model;
|
|
112
|
-
const key = `${provider}/${typeof modelId === "string" && modelId ? modelId : "unknown"}`;
|
|
113
|
-
const input = usage.input;
|
|
114
|
-
const output = usage.output;
|
|
115
|
-
const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
|
|
116
|
-
const cacheWrite = typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0;
|
|
117
|
-
let row = aggregate.perModel.get(key);
|
|
118
|
-
if (!row) {
|
|
119
|
-
row = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
120
|
-
aggregate.perModel.set(key, row);
|
|
121
|
-
}
|
|
122
|
-
row.input += input;
|
|
123
|
-
row.output += output;
|
|
124
|
-
row.cacheRead += cacheRead;
|
|
125
|
-
row.cacheWrite += cacheWrite;
|
|
126
|
-
row.total += input + output + cacheRead + cacheWrite;
|
|
127
|
-
aggregate.hasUsage = true;
|
|
128
|
-
const day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);
|
|
129
|
-
aggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);
|
|
130
|
-
}
|
|
131
|
-
// Category estimate over the file's messages (system prompt/tools are not
|
|
132
|
-
// stored in session files, so message categories carry the breakdown).
|
|
133
|
-
try {
|
|
134
|
-
const messages = sessionEntries.flatMap(sessionEntryToContextMessages);
|
|
135
|
-
const breakdown = computeContextBreakdown({ systemPrompt: "", tools: [], messages, contextWindow: 0 });
|
|
136
|
-
aggregate.categories = {
|
|
137
|
-
user: breakdown.user,
|
|
138
|
-
assistantText: breakdown.assistantText,
|
|
139
|
-
thinking: breakdown.thinking,
|
|
140
|
-
toolCalls: breakdown.toolCalls,
|
|
141
|
-
toolResults: breakdown.toolResults,
|
|
142
|
-
summaries: breakdown.summaries,
|
|
143
|
-
total: breakdown.user +
|
|
144
|
-
breakdown.assistantText +
|
|
145
|
-
breakdown.thinking +
|
|
146
|
-
breakdown.toolCalls +
|
|
147
|
-
breakdown.toolResults +
|
|
148
|
-
breakdown.summaries,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// Category estimation is best-effort; per-model/day totals above still stand.
|
|
153
|
-
}
|
|
154
|
-
return aggregate;
|
|
155
81
|
}
|
|
156
82
|
/**
|
|
157
|
-
* Aggregate
|
|
83
|
+
* Aggregate requests since `sinceMs`; file modification time only narrows the scan.
|
|
158
84
|
* Never throws; on total failure returns an empty aggregate.
|
|
159
85
|
*/
|
|
160
86
|
export function collectUsageHistory(options) {
|
|
@@ -163,6 +89,8 @@ export function collectUsageHistory(options) {
|
|
|
163
89
|
const sessionsDir = options.sessionsDir ?? getSessionsDir();
|
|
164
90
|
const files = enumerateSessionFiles(sessionsDir);
|
|
165
91
|
const seen = new Set();
|
|
92
|
+
const seenRequests = new Set();
|
|
93
|
+
const seenEntries = new Set();
|
|
166
94
|
for (const filePath of files) {
|
|
167
95
|
seen.add(filePath);
|
|
168
96
|
let stats;
|
|
@@ -188,40 +116,44 @@ export function collectUsageHistory(options) {
|
|
|
188
116
|
}
|
|
189
117
|
if (!aggregate)
|
|
190
118
|
continue;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
target.cacheWrite += row.cacheWrite;
|
|
203
|
-
target.total += row.total;
|
|
204
|
-
}
|
|
205
|
-
for (const [day, total] of aggregate.perDay) {
|
|
206
|
-
const existing = result.perDay.find((r) => r.day === day);
|
|
207
|
-
if (existing) {
|
|
208
|
-
existing.total += total;
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
result.perDay.push({ day, total });
|
|
119
|
+
let hasUsage = false;
|
|
120
|
+
for (const request of aggregate.requests) {
|
|
121
|
+
if (request.timestamp < options.sinceMs || seenRequests.has(request.id))
|
|
122
|
+
continue;
|
|
123
|
+
seenRequests.add(request.id);
|
|
124
|
+
hasUsage = true;
|
|
125
|
+
const model = `${request.provider}/${request.model}`;
|
|
126
|
+
let row = result.perModel.find((candidate) => candidate.model === model);
|
|
127
|
+
if (!row) {
|
|
128
|
+
row = { model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
129
|
+
result.perModel.push(row);
|
|
212
130
|
}
|
|
131
|
+
row.input += request.usage.input;
|
|
132
|
+
row.output += request.usage.output;
|
|
133
|
+
row.cacheRead += request.usage.cacheRead;
|
|
134
|
+
row.cacheWrite += request.usage.cacheWrite;
|
|
135
|
+
row.total += usageTokens(request.usage);
|
|
136
|
+
const day = new Date(request.timestamp).toISOString().slice(0, 10);
|
|
137
|
+
const dayRow = result.perDay.find((candidate) => candidate.day === day);
|
|
138
|
+
if (dayRow)
|
|
139
|
+
dayRow.total += usageTokens(request.usage);
|
|
140
|
+
else
|
|
141
|
+
result.perDay.push({ day, total: usageTokens(request.usage) });
|
|
213
142
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
143
|
+
if (hasUsage)
|
|
144
|
+
result.sessionsWithUsage++;
|
|
145
|
+
const messages = aggregate.entries
|
|
146
|
+
.filter((entry) => {
|
|
147
|
+
const identity = `${entry.id}:${entry.timestamp}`;
|
|
148
|
+
if (entry.inherited || Date.parse(entry.timestamp) < options.sinceMs || seenEntries.has(identity))
|
|
149
|
+
return false;
|
|
150
|
+
seenEntries.add(identity);
|
|
151
|
+
return true;
|
|
152
|
+
})
|
|
153
|
+
.flatMap(sessionEntryToContextMessages);
|
|
154
|
+
const breakdown = computeContextBreakdown({ systemPrompt: "", tools: [], messages, contextWindow: 0 });
|
|
155
|
+
for (const key of Object.keys(result.categories))
|
|
156
|
+
result.categories[key] += breakdown[key];
|
|
225
157
|
}
|
|
226
158
|
// Drop cache entries for deleted files.
|
|
227
159
|
for (const cachedPath of fileCache.keys()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-history.js","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAe,WAAW,EAAc,QAAQ,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAEN,mBAAmB,EAEnB,6BAA6B,GAC7B,MAAM,sBAAsB,CAAC;AAkD9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;AAEnD,sDAAsD;AACtD,MAAM,UAAU,sBAAsB,GAAS;IAC9C,SAAS,CAAC,KAAK,EAAE,CAAC;AAAA,CAClB;AAED,SAAS,eAAe,GAA2B;IAClD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAAA,CACxG;AAED,SAAS,YAAY,GAAiB;IACrC,OAAO;QACN,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE;QACV,UAAU,EAAE,eAAe,EAAE;QAC7B,oBAAoB,EAAE,KAAK;QAC3B,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,iBAAiB,EAAE,CAAC;KACpB,CAAC;AAAA,CACF;AAED,6FAA6F;AAC7F,SAAS,qBAAqB,CAAC,WAAmB,EAAY;IAC7D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,UAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,UAAU,GAAG,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC7C,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;wBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC7D,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,oCAAkC;YACnC,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,gGAAgG;AAChG,SAAS,gBAAgB,CAAC,KAAmB,EAAE,OAAyB,EAAE,UAAkB,EAAU;IACrG,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC,SAAS,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,SAAS,CAAC;IAC7F,OAAO,UAAU,CAAC;AAAA,CAClB;AAED,yFAAyF;AACzF,SAAS,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAE,IAAY,EAAwB;IAChG,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,SAAS,GAAkB;QAChC,OAAO;QACP,IAAI;QACJ,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,UAAU,EAAE,eAAe,EAAE;QAC7B,QAAQ,EAAE,KAAK;KACf,CAAC;IAEF,MAAM,cAAc,GAAmB,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,SAAS;QACjD,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,OAA2B,CAAC;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QAE5F,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACzG,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,KAAK,CAAC;QACvD,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAC1F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/E,IAAI,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,EAAE,CAAC;YACV,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YACjF,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;QACD,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC;QACnB,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC;QACrB,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC;QAC3B,GAAG,CAAC,UAAU,IAAI,UAAU,CAAC;QAC7B,GAAG,CAAC,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;QACrD,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;QAE1B,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3F,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;IACvG,CAAC;IAED,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,uBAAuB,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC;QACvG,SAAS,CAAC,UAAU,GAAG;YACtB,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,aAAa,EAAE,SAAS,CAAC,aAAa;YACtC,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,KAAK,EACJ,SAAS,CAAC,IAAI;gBACd,SAAS,CAAC,aAAa;gBACvB,SAAS,CAAC,QAAQ;gBAClB,SAAS,CAAC,SAAS;gBACnB,SAAS,CAAC,WAAW;gBACrB,SAAS,CAAC,SAAS;SACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,8EAA8E;IAC/E,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAkD,EAAgB;IACrG,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,cAAc,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACnB,IAAI,KAAY,CAAC;YACjB,IAAI,CAAC;gBACJ,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACR,SAAS,CAAC,oCAAoC;YAC/C,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO;gBAAE,SAAS;YAC9C,MAAM,CAAC,YAAY,EAAE,CAAC;YAEtB,IAAI,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;gBACxF,SAAS,GAAG,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;gBAC/E,MAAM,CAAC,WAAW,EAAE,CAAC;gBACrB,IAAI,SAAS,EAAE,CAAC;oBACf,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACP,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YACD,IAAI,CAAC,SAAS;gBAAE,SAAS;YAEzB,IAAI,SAAS,CAAC,QAAQ;gBAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACnD,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC/C,IAAI,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;gBAChE,IAAI,CAAC,MAAM,EAAE,CAAC;oBACb,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;oBAC1F,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9B,CAAC;gBACD,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;gBAC1B,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC;gBAC5B,MAAM,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,CAAC;gBAClC,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC;gBACpC,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;YAC3B,CAAC;YACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;gBAC1D,IAAI,QAAQ,EAAE,CAAC;oBACd,QAAQ,CAAC,KAAK,IAAI,KAAK,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpC,CAAC;YACF,CAAC;YACD,KAAK,MAAM,GAAG,IAAI;gBACjB,MAAM;gBACN,eAAe;gBACf,UAAU;gBACV,WAAW;gBACX,aAAa;gBACb,WAAW;gBACX,OAAO;aACE,EAAE,CAAC;gBACZ,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrD,CAAC;QACF,CAAC;QAED,wCAAwC;QACxC,KAAK,MAAM,UAAU,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,YAAY,EAAE,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"usage-history.js","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AccountedRequest, collectUsageRequests, usageTokens } from \"./usage-accounting.ts\";\n/**\n * Request usage history for integrations.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - Token totals per provider/model from conversation and auxiliary-request `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport { loadEntriesFromFile, type SessionEntry, sessionEntryToContextMessages } from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\trequests: AccountedRequest[];\n\tentries: SessionEntry[];\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Cache parsed records, not date-window aggregates, so changing the window stays correct. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\ttry {\n\t\tconst entries = loadEntriesFromFile(filePath).filter((entry): entry is SessionEntry => entry.type !== \"session\");\n\t\treturn { mtimeMs, size, entries, requests: collectUsageRequests(entries) };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Aggregate requests since `sinceMs`; file modification time only narrows the scan.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\t\tconst seenRequests = new Set<string>();\n\t\tconst seenEntries = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tlet hasUsage = false;\n\t\t\tfor (const request of aggregate.requests) {\n\t\t\t\tif (request.timestamp < options.sinceMs || seenRequests.has(request.id)) continue;\n\t\t\t\tseenRequests.add(request.id);\n\t\t\t\thasUsage = true;\n\t\t\t\tconst model = `${request.provider}/${request.model}`;\n\t\t\t\tlet row = result.perModel.find((candidate) => candidate.model === model);\n\t\t\t\tif (!row) {\n\t\t\t\t\trow = { model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(row);\n\t\t\t\t}\n\t\t\t\trow.input += request.usage.input;\n\t\t\t\trow.output += request.usage.output;\n\t\t\t\trow.cacheRead += request.usage.cacheRead;\n\t\t\t\trow.cacheWrite += request.usage.cacheWrite;\n\t\t\t\trow.total += usageTokens(request.usage);\n\t\t\t\tconst day = new Date(request.timestamp).toISOString().slice(0, 10);\n\t\t\t\tconst dayRow = result.perDay.find((candidate) => candidate.day === day);\n\t\t\t\tif (dayRow) dayRow.total += usageTokens(request.usage);\n\t\t\t\telse result.perDay.push({ day, total: usageTokens(request.usage) });\n\t\t\t}\n\t\t\tif (hasUsage) result.sessionsWithUsage++;\n\t\t\tconst messages = aggregate.entries\n\t\t\t\t.filter((entry) => {\n\t\t\t\t\tconst identity = `${entry.id}:${entry.timestamp}`;\n\t\t\t\t\tif (entry.inherited || Date.parse(entry.timestamp) < options.sinceMs || seenEntries.has(identity))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tseenEntries.add(identity);\n\t\t\t\t\treturn true;\n\t\t\t\t})\n\t\t\t\t.flatMap(sessionEntryToContextMessages);\n\t\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\t\tfor (const key of Object.keys(result.categories) as Array<keyof UsageHistoryCategories>)\n\t\t\t\tresult.categories[key] += breakdown[key];\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* Adapter/network errors resolve to `undefined` (callers omit the plan UI).
|
|
13
13
|
* Auth failures are different: they return `error` and are not cached as empty.
|
|
14
14
|
*/
|
|
15
|
+
import type { AuthResult } from "@ashx-j/lunr-ai";
|
|
15
16
|
import type { ModelRuntime } from "./model-runtime.ts";
|
|
16
17
|
import type { SettingsManager } from "./settings-manager.ts";
|
|
17
18
|
export interface PlanUsageWindow {
|
|
@@ -39,6 +40,11 @@ export interface AllPlanUsageResult {
|
|
|
39
40
|
}
|
|
40
41
|
/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */
|
|
41
42
|
export declare function planUsageAuthError(error: unknown): string | undefined;
|
|
43
|
+
export interface UsageAuthSnapshot {
|
|
44
|
+
resolution?: AuthResult;
|
|
45
|
+
codexHeaders?: Record<string, string>;
|
|
46
|
+
oauth: boolean;
|
|
47
|
+
}
|
|
42
48
|
export declare const PLAN_USAGE_CACHE_TTL_MS: number;
|
|
43
49
|
export type PlanUsageWindowPreference = "5h" | "weekly";
|
|
44
50
|
export declare function isWeeklyPlanWindow(window: PlanUsageWindow): boolean;
|
|
@@ -55,7 +61,7 @@ export declare function getPlanUsageResult(providerId: string, runtime: ModelRun
|
|
|
55
61
|
/** Fetch every stored-credential plan plus the current provider, in parallel. */
|
|
56
62
|
export declare function getAllPlanUsageResults(currentProvider: string | undefined, runtime: ModelRuntime): Promise<AllPlanUsageResult>;
|
|
57
63
|
/** Last fetched usage, including expired cache (footer can show stale while refetching). */
|
|
58
|
-
export declare function peekPlanUsage(providerId: string): PlanUsage | undefined;
|
|
64
|
+
export declare function peekPlanUsage(providerId: string, runtime: ModelRuntime): PlanUsage | undefined;
|
|
59
65
|
/** Clear the adapter cache (tests, /login changes). */
|
|
60
66
|
export declare function clearPlanUsageCache(): void;
|
|
61
67
|
export declare const USAGE_SERVICE_BRIDGE_SYMBOL: unique symbol;
|
|
@@ -70,6 +76,7 @@ export interface UsageServiceBridge {
|
|
|
70
76
|
pickForFooter(providerId: string): FooterPlanSegment | undefined;
|
|
71
77
|
setOnUpdate(fn: (() => void) | undefined): void;
|
|
72
78
|
}
|
|
79
|
+
/** Register a bridge bound to its runtime; footer callbacks can retain it outside async scope. */
|
|
73
80
|
export declare function registerUsageServiceBridge(runtime: ModelRuntime, settingsManager: SettingsManager): void;
|
|
74
|
-
export declare function getUsageServiceBridge(): UsageServiceBridge | undefined;
|
|
81
|
+
export declare function getUsageServiceBridge(runtime?: ModelRuntime | undefined): UsageServiceBridge | undefined;
|
|
75
82
|
//# 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":"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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 type { ModelRuntime } from \"./model-runtime.ts\";\nimport { runtimeScope } from \"./runtime-scope.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\t/** First plan, kept for footer and compatibility callers. */\n\tusage?: PlanUsage;\n\t/** Every plan returned by the provider. */\n\tusages?: PlanUsage[];\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nexport interface AllPlanUsageResult {\n\tusages: PlanUsage[];\n\terrors: 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 | 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[] }>();\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 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[0], usages: 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\tconst usages = value === undefined ? [] : Array.isArray(value) ? value : [value];\n\t\t\tcache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: usages });\n\t\t\treturn { usage: usages[0], usages };\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: [] });\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/** Fetch every stored-credential plan plus the current provider, in parallel. */\nexport async function getAllPlanUsageResults(\n\tcurrentProvider: string | undefined,\n\truntime: ModelRuntime,\n): Promise<AllPlanUsageResult> {\n\tconst stored = new Set((await runtime.listCredentials()).map((credential) => credential.providerId));\n\tconst providers = Object.keys(ADAPTERS).filter(\n\t\t(providerId) => stored.has(providerId) || providerId === currentProvider,\n\t);\n\tconst results = await Promise.all(providers.map((providerId) => getPlanUsageResult(providerId, runtime)));\n\treturn {\n\t\tusages: results.flatMap((result) => result.usages ?? []),\n\t\terrors: results.flatMap((result) => (result.error ? [`${result.error}`] : [])),\n\t};\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[0];\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 (runtimeScope.getStore()?.settingsManager ?? 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\tconst runtime = runtimeScope.getStore()?.modelRuntime ?? activeRuntime;\n\t\tif (!runtime || !hasPlanUsageAdapter(providerId)) return;\n\t\tvoid getPlanUsage(providerId, runtime).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
|
+
{"version":3,"file":"usage-service.d.ts","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 { createHash } from \"node:crypto\";\nimport type { AuthResult } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { runtimeScope } from \"./runtime-scope.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage, resolveCodexHeaders } 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\t/** First plan, kept for footer and compatibility callers. */\n\tusage?: PlanUsage;\n\t/** Every plan returned by the provider. */\n\tusages?: PlanUsage[];\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nexport interface AllPlanUsageResult {\n\tusages: PlanUsage[];\n\terrors: 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\nexport interface UsageAuthSnapshot {\n\tresolution?: AuthResult;\n\tcodexHeaders?: Record<string, string>;\n\toauth: boolean;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime, auth: UsageAuthSnapshot) => Promise<PlanUsage | 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\ninterface UsageState {\n\taccounts: Map<string, string>;\n\tauthReads: Map<string, { started: number; settled: number }>;\n\tcache: Map<string, { expiresAt: number; value: PlanUsage[] }>;\n\tinflight: Map<string, Promise<PlanUsageResult>>;\n}\nlet generation = 0;\nlet states = new WeakMap<ModelRuntime, UsageState>();\n\nfunction stateFor(runtime: ModelRuntime): UsageState {\n\tlet state = states.get(runtime);\n\tif (!state) {\n\t\tstate = { accounts: new Map(), authReads: new Map(), cache: new Map(), inflight: new Map() };\n\t\tstates.set(runtime, state);\n\t}\n\treturn state;\n}\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 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 state = stateFor(runtime);\n\tconst requestGeneration = generation;\n\tconst reads = state.authReads.get(providerId) ?? { started: 0, settled: 0 };\n\tstate.authReads.set(providerId, reads);\n\tconst authRead = ++reads.started;\n\tlet auth: UsageAuthSnapshot;\n\ttry {\n\t\tauth =\n\t\t\tproviderId === \"openai-codex\"\n\t\t\t\t? { codexHeaders: await resolveCodexHeaders(runtime), oauth: runtime.isUsingOAuth(providerId) }\n\t\t\t\t: { resolution: await runtime.getAuth(providerId), oauth: runtime.isUsingOAuth(providerId) };\n\t} catch (error) {\n\t\tif (reads.settled <= authRead) {\n\t\t\treads.settled = authRead;\n\t\t\tstate.accounts.delete(providerId);\n\t\t\tstate.cache.delete(providerId);\n\t\t\tstate.inflight.delete(providerId);\n\t\t}\n\t\treturn { error: planUsageAuthError(error) };\n\t}\n\tif (generation !== requestGeneration || states.get(runtime) !== state) return {};\n\t// Only a digest is retained. Neither credentials nor account identifiers are persisted.\n\tconst account = createHash(\"sha256\").update(JSON.stringify(auth)).digest(\"hex\");\n\tconst previous = state.accounts.get(providerId);\n\tif (reads.settled > authRead && previous !== account) return {};\n\treads.settled = Math.max(reads.settled, authRead);\n\tif (previous !== account) {\n\t\tstate.cache.delete(providerId);\n\t\tstate.inflight.delete(providerId);\n\t\tstate.accounts.set(providerId, account);\n\t}\n\tconst cached = state.cache.get(providerId);\n\tif (cached && cached.expiresAt > Date.now()) return { usage: cached.value[0], usages: cached.value };\n\tconst pending = state.inflight.get(providerId);\n\tif (pending) return pending;\n\tconst isCurrent = () =>\n\t\tgeneration === requestGeneration &&\n\t\tstates.get(runtime) === state &&\n\t\tstate.accounts.get(providerId) === account &&\n\t\tstate.inflight.get(providerId) === task;\n\tconst task = (async (): Promise<PlanUsageResult> => {\n\t\ttry {\n\t\t\tconst value = await adapter(runtime, auth);\n\t\t\tconst usages = value === undefined ? [] : Array.isArray(value) ? value : [value];\n\t\t\tif (isCurrent())\n\t\t\t\tstate.cache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: usages });\n\t\t\treturn { usage: usages[0], usages };\n\t\t} catch (error) {\n\t\t\tconst authError = planUsageAuthError(error);\n\t\t\tif (authError) return { error: authError };\n\t\t\tif (isCurrent()) state.cache.set(providerId, { expiresAt: Date.now() + PLAN_USAGE_CACHE_TTL_MS, value: [] });\n\t\t\treturn {};\n\t\t} finally {\n\t\t\tif (isCurrent()) state.inflight.delete(providerId);\n\t\t}\n\t})();\n\tstate.inflight.set(providerId, task);\n\treturn task;\n}\n\n/** Fetch every stored-credential plan plus the current provider, in parallel. */\nexport async function getAllPlanUsageResults(\n\tcurrentProvider: string | undefined,\n\truntime: ModelRuntime,\n): Promise<AllPlanUsageResult> {\n\tconst stored = new Set((await runtime.listCredentials()).map((credential) => credential.providerId));\n\tconst providers = Object.keys(ADAPTERS).filter(\n\t\t(providerId) => stored.has(providerId) || providerId === currentProvider,\n\t);\n\tconst results = await Promise.all(providers.map((providerId) => getPlanUsageResult(providerId, runtime)));\n\treturn {\n\t\tusages: results.flatMap((result) => result.usages ?? []),\n\t\terrors: results.flatMap((result) => (result.error ? [`${result.error}`] : [])),\n\t};\n}\n\n/** Last fetched usage, including expired cache (footer can show stale while refetching). */\nexport function peekPlanUsage(providerId: string, runtime: ModelRuntime): PlanUsage | undefined {\n\treturn states.get(runtime)?.cache.get(providerId)?.value[0];\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tgeneration++;\n\tstates = new WeakMap();\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\nconst bridges = new WeakMap<ModelRuntime, UsageServiceBridge>();\n\n/** Register a bridge bound to its runtime; footer callbacks can retain it outside async scope. */\nexport function registerUsageServiceBridge(runtime: ModelRuntime, settingsManager: SettingsManager): void {\n\tlet onUpdate: (() => void) | undefined;\n\tconst bridge: UsageServiceBridge = {\n\t\tpeek: (providerId) => peekPlanUsage(providerId, runtime),\n\t\tprefetch(providerId) {\n\t\t\tif (!hasPlanUsageAdapter(providerId)) return;\n\t\t\tvoid getPlanUsage(providerId, runtime).then(() => onUpdate?.());\n\t\t},\n\t\tsetOnUpdate(fn) {\n\t\t\tonUpdate = fn;\n\t\t},\n\t\tgetPreferredWindow: () => settingsManager.getPlanUsageWindow(),\n\t\tpickForFooter(providerId) {\n\t\t\tconst window = pickPlanWindow(peekPlanUsage(providerId, runtime), settingsManager.getPlanUsageWindow());\n\t\t\treturn window ? { label: footerPlanLabel(window), usedPercent: window.usedPercent } : undefined;\n\t\t},\n\t};\n\tbridges.set(runtime, bridge);\n\t// Compatibility bridge requires an explicit async owner; it never falls back to another session.\n\t(globalThis as Record<symbol, unknown>)[USAGE_SERVICE_BRIDGE_SYMBOL] = {\n\t\tpeek: (providerId: string) => getUsageServiceBridge()?.peek(providerId),\n\t\tprefetch: (providerId: string) => getUsageServiceBridge()?.prefetch(providerId),\n\t\tpickForFooter: (providerId: string) => getUsageServiceBridge()?.pickForFooter(providerId),\n\t\tgetPreferredWindow: () => getUsageServiceBridge()?.getPreferredWindow() ?? \"weekly\",\n\t\tsetOnUpdate: (fn: (() => void) | undefined) => getUsageServiceBridge()?.setOnUpdate(fn),\n\t} satisfies UsageServiceBridge;\n}\n\nexport function getUsageServiceBridge(runtime = runtimeScope.getStore()?.modelRuntime): UsageServiceBridge | undefined {\n\treturn runtime ? bridges.get(runtime) : undefined;\n}\n"]}
|