@ashx-j/lunr 0.2.25 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -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/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +1 -0
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.d.ts +3 -0
- package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-computer-use.js +179 -0
- package/dist/builtin-extensions/lunr-computer-use.js.map +1 -0
- 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/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/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -4
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +8 -5
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -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 +15 -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 +1 -0
- 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 +21 -4
- 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 +11 -4
- 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.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +121 -25
- 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 +25 -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/chain-outputs.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js +5 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.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/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/tui/communication.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/communication.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/communication.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -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 +22 -11
- 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 +20 -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/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 +40 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +309 -76
- 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/computer-use/adapter.d.ts +28 -0
- package/dist/core/computer-use/adapter.d.ts.map +1 -0
- package/dist/core/computer-use/adapter.js +138 -0
- package/dist/core/computer-use/adapter.js.map +1 -0
- package/dist/core/computer-use/image.d.ts +27 -0
- package/dist/core/computer-use/image.d.ts.map +1 -0
- package/dist/core/computer-use/image.js +84 -0
- package/dist/core/computer-use/image.js.map +1 -0
- package/dist/core/computer-use/lease.d.ts +18 -0
- package/dist/core/computer-use/lease.d.ts.map +1 -0
- package/dist/core/computer-use/lease.js +129 -0
- package/dist/core/computer-use/lease.js.map +1 -0
- package/dist/core/computer-use/macos-runtime.d.ts +14 -0
- package/dist/core/computer-use/macos-runtime.d.ts.map +1 -0
- package/dist/core/computer-use/macos-runtime.js +148 -0
- package/dist/core/computer-use/macos-runtime.js.map +1 -0
- package/dist/core/computer-use/policy.d.ts +19 -0
- package/dist/core/computer-use/policy.d.ts.map +1 -0
- package/dist/core/computer-use/policy.js +67 -0
- package/dist/core/computer-use/policy.js.map +1 -0
- package/dist/core/computer-use/release.generated.d.ts +31 -0
- package/dist/core/computer-use/release.generated.d.ts.map +1 -0
- package/dist/core/computer-use/release.generated.js +36 -0
- package/dist/core/computer-use/release.generated.js.map +1 -0
- package/dist/core/computer-use/runtime.d.ts +13 -0
- package/dist/core/computer-use/runtime.d.ts.map +1 -0
- package/dist/core/computer-use/runtime.js +158 -0
- package/dist/core/computer-use/runtime.js.map +1 -0
- package/dist/core/computer-use/schemas.d.ts +100 -0
- package/dist/core/computer-use/schemas.d.ts.map +1 -0
- package/dist/core/computer-use/schemas.js +96 -0
- package/dist/core/computer-use/schemas.js.map +1 -0
- package/dist/core/computer-use/windows-desktop.d.ts +3 -0
- package/dist/core/computer-use/windows-desktop.d.ts.map +1 -0
- package/dist/core/computer-use/windows-desktop.js +48 -0
- package/dist/core/computer-use/windows-desktop.js.map +1 -0
- package/dist/core/computer-use/workflow.d.ts +38 -0
- package/dist/core/computer-use/workflow.d.ts.map +1 -0
- package/dist/core/computer-use/workflow.js +900 -0
- package/dist/core/computer-use/workflow.js.map +1 -0
- 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/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -0
- package/dist/core/extensions/loader.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 +8 -0
- 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 +4 -4
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +25 -38
- 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 +135 -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 +6 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +95 -32
- 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/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-event-transport.d.ts +3 -0
- package/dist/core/subagent-event-transport.d.ts.map +1 -0
- package/dist/core/subagent-event-transport.js +61 -0
- package/dist/core/subagent-event-transport.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-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-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/adapters/discord.d.ts.map +1 -1
- package/dist/gateway/adapters/discord.js +2 -2
- package/dist/gateway/adapters/discord.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/approval.d.ts.map +1 -1
- package/dist/gateway/approval.js +3 -3
- package/dist/gateway/approval.js.map +1 -1
- package/dist/gateway/authz.d.ts +4 -4
- package/dist/gateway/authz.d.ts.map +1 -1
- package/dist/gateway/authz.js +8 -12
- package/dist/gateway/authz.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 +12 -10
- 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 +85 -62
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +5 -4
- 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 +64 -22
- 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 +74 -39
- 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 +22 -9
- 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/gateway/setup.d.ts.map +1 -1
- package/dist/gateway/setup.js +6 -4
- package/dist/gateway/setup.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/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/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +104 -109
- 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/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +9 -1
- package/dist/modes/print-mode.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-247RTYLS.js +16 -0
- package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-ZLAX7TUH.js → builtin-extensions-OX6KELNX.js} +9 -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-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-NRKGMJYU.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
- package/dist/node-runtime/{chunk-ZXSGTGJ6.js → chunk-APVM5NEU.js} +169 -148
- package/dist/node-runtime/{chunk-RC7DMV45.js → chunk-AYHH7YSD.js} +205 -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-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-KWIYUUZK.js → chunk-C3P6Q3B2.js} +469 -283
- package/dist/node-runtime/{chunk-YCUZEGJG.js → chunk-DQSCFU7R.js} +17 -8
- package/dist/node-runtime/chunk-EBLAZVSA.js +4170 -0
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
- 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-FE3WZI7F.js +290 -0
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
- 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-FY4A63RK.js +73 -0
- package/dist/node-runtime/chunk-FYIFPFXE.js +98 -0
- 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-6XL67VVY.js → chunk-HUD2RFJW.js} +17 -2
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
- package/dist/node-runtime/chunk-IAZSK7KK.js +108 -0
- package/dist/node-runtime/{chunk-2HHLJWGD.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-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
- package/dist/node-runtime/{chunk-WF7PGWRZ.js → chunk-JWEVT363.js} +37 -28
- 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-IJIFHRTB.js → chunk-MQPKUHRN.js} +165 -85
- 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-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-E4D3IGP2.js → chunk-QNVMKNNN.js} +20 -17
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-7S3T6YEA.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-3XPLBUEM.js → chunk-RLGY2QIL.js} +49 -59
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-AD6VXAO7.js → chunk-TCT6PCUR.js} +47 -115
- package/dist/node-runtime/chunk-U5F4IC7R.js +53 -0
- package/dist/node-runtime/{chunk-OCVIXVTZ.js → chunk-UBWW2PID.js} +13 -2
- package/dist/node-runtime/{chunk-HFVZUPFJ.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-LQP3RB67.js → chunk-UYRKUKFE.js} +1 -1
- package/dist/node-runtime/{chunk-DSIKTUPW.js → chunk-VSUEAI7O.js} +2993 -3820
- 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-VQFSUJJG.js → chunk-ZDJUR45D.js} +430 -129
- package/dist/node-runtime/{chunk-YDO2F2IA.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/chunk-ZIQUS5QO.js +180 -0
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-6WDOYKS6.js → command-3HMSY6W2.js} +5 -5
- package/dist/node-runtime/{commands-NZLSOHPE.js → commands-D6A6H4TS.js} +11 -16
- package/dist/node-runtime/commands-OCSSEEWJ.js +37 -0
- 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-77GW4F5P.js → direct-tool-executor-2QJXVQD2.js} +8 -10
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.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-INAYBF4Q.js → gateway-6623C2U3.js} +61 -53
- 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-AIHEDUYA.js} +5 -5
- package/dist/node-runtime/index.js +54 -48
- package/dist/node-runtime/{init-HS6NQXSK.js → init-JBQJIPYI.js} +3 -4
- package/dist/node-runtime/{install-cli-KL4UDDAD.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -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-VDP2QNRQ.js +158 -0
- package/dist/node-runtime/{lunr-cron-LWIZJM2N.js → lunr-cron-LKVA5CZZ.js} +94 -79
- package/dist/node-runtime/main.js +42 -39
- package/dist/node-runtime/{mobile-commands-J7OJEKFO.js → mobile-commands-XT3NQXLD.js} +13 -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-VYZM5WHN.js → package-manager-cli-CYM262SI.js} +20 -19
- 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-5UKBZP44.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-DNY7EAIG.js → pi-subagents-LT3CTO4E.js} +114 -116
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XSFM26EL.js → print-mode-2XHN5SUA.js} +4 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-LGEUYD7R.js → proxy-modes-VEGC6QG5.js} +9 -13
- package/dist/node-runtime/rpc-entry.js +42 -39
- 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-UGXM4NPQ.js +21 -0
- package/dist/node-runtime/{runtime-bridges-VRDDR73P.js → runtime-bridges-DNKMNJCF.js} +8 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-J4SPQPDJ.js → settings-manager-P2TKGYTR.js} +6 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-CQ2NLVK5.js → setup-OLQ4GXWS.js} +22 -21
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-KZZQGAKV.js → subagent-executor-Q5F7DBVM.js} +141 -62
- 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-GTFJC7XK.js → update-cli-F3XXJANQ.js} +7 -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-ICDI4N3J.js +767 -0
- 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 +2 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +12 -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/computer-use.md +362 -0
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +22 -12
- 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 +3841 -0
- package/install-lock/package.json +18 -0
- package/native/computer-use/LICENSE.md +21 -0
- package/npm-shrinkwrap.json +3858 -0
- package/package.json +15 -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-YXXFCP5J.js +0 -49
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-4TLO4PO2.js +0 -3427
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-OVX6X377.js +0 -50
- package/dist/node-runtime/chunk-QTU6AKNJ.js +0 -773
- package/dist/node-runtime/chunk-TORR5XBV.js +0 -356
- package/dist/node-runtime/commands-QC6VUZJQ.js +0 -36
- package/dist/node-runtime/interactive-mode-Z5M7WYYZ.js +0 -72
- package/dist/node-runtime/mcp-auth-flow-QIXHW6QN.js +0 -32
|
@@ -25,9 +25,14 @@ interface ProviderPool {
|
|
|
25
25
|
keys: SubEntry[];
|
|
26
26
|
}
|
|
27
27
|
type SubscriptionData = Record<string, ProviderPool>;
|
|
28
|
+
export interface RequestSubscriptionKey {
|
|
29
|
+
providerId: string;
|
|
30
|
+
fingerprint: string;
|
|
31
|
+
}
|
|
32
|
+
/** Request identities are non-secret digests kept outside messages and storage. */
|
|
33
|
+
export declare function subscriptionKeyFingerprint(providerId: string, key: string): string;
|
|
28
34
|
export declare class SubscriptionManager {
|
|
29
35
|
private data;
|
|
30
|
-
private loaded;
|
|
31
36
|
private storage;
|
|
32
37
|
private authStorage;
|
|
33
38
|
private queue;
|
|
@@ -37,9 +42,13 @@ export declare class SubscriptionManager {
|
|
|
37
42
|
static fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager;
|
|
38
43
|
static inMemory(authStorage: CredentialStore, data?: SubscriptionData): SubscriptionManager;
|
|
39
44
|
private parseStorageData;
|
|
40
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Lock order is subscriptions then auth. Keep selection and its auth mirror
|
|
47
|
+
* under the subscription lock so later selectors cannot overtake a mirror.
|
|
48
|
+
* These files are not an atomic commit: a failed mirror aborts the pool write;
|
|
49
|
+
* a later pool write failure can leave auth ahead and a repeated selection repairs it.
|
|
50
|
+
*/
|
|
41
51
|
private enqueue;
|
|
42
|
-
private persist;
|
|
43
52
|
private readRawCredential;
|
|
44
53
|
private ensurePool;
|
|
45
54
|
private nextId;
|
|
@@ -51,12 +60,12 @@ export declare class SubscriptionManager {
|
|
|
51
60
|
renameKey(providerId: string, id: string, name: string): Promise<void>;
|
|
52
61
|
setActive(providerId: string, id: string): Promise<void>;
|
|
53
62
|
/**
|
|
54
|
-
* Mark the
|
|
63
|
+
* Mark the request's key exhausted and rotate to the next non-exhausted key in
|
|
55
64
|
* round-robin order. A parseable reset time is persisted as exhaustedUntil;
|
|
56
|
-
* otherwise the exhaustion is process-local only.
|
|
57
|
-
* or null when no
|
|
65
|
+
* otherwise the exhaustion is process-local only. A later selection is preserved.
|
|
66
|
+
* Returns the usable active key for retry, or null when no alternative exists.
|
|
58
67
|
*/
|
|
59
|
-
rotateOnFailure(providerId: string, errorMessage: string, now?: number): Promise<SubEntry | null>;
|
|
68
|
+
rotateOnFailure(providerId: string, errorMessage: string, requestFingerprint: string | undefined, now?: number): Promise<SubEntry | null>;
|
|
60
69
|
/** Manual reactivate: clears persisted and process-local exhaustion for a key. */
|
|
61
70
|
clearExhaustion(providerId: string, id: string): Promise<void>;
|
|
62
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mBAAmB,CAAC;AAGhH,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAkB;IAErC,OAAO,CAAC,KAAK,CAAuC;IAGpD,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,eAGN;IAED,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAK9E;IAED,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,mBAAmB,CAEjG;IAED,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,GAAE,gBAAqB,GAAG,mBAAmB,CAI9F;IAED,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,OAAO;YAQD,iBAAiB;YAajB,UAAU;IAiBxB,OAAO,CAAC,MAAM;YASA,YAAY;IAIpB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKlD;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAKjE;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAY9E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B7D;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7D;IAED;;;;;OAKG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA0ClH;IAED,kFAAkF;IAC5E,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAenE;CACD","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate loaded = false;\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider ids of keys exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as SubscriptionData;\n\t}\n\n\tprivate ensureLoaded(): void {\n\t\tif (this.loaded) return;\n\t\tthis.loaded = true;\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t} catch {\n\t\t\t// Preserve the empty snapshot; a malformed file is never overwritten by a load.\n\t\t}\n\t}\n\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(fn);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\tprivate persist(): Promise<void> {\n\t\treturn this.storage.withLockAsync(async () => ({\n\t\t\tresult: undefined,\n\t\t\tnext: JSON.stringify(this.data, null, 2),\n\t\t}));\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tthis.ensureLoaded();\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\tawait this.persist();\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tawait this.authStorage.modify(providerId, async () => ({ type: \"api_key\", key }));\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.ensureLoaded();\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the active key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. Returns the new active key,\n\t * or null when no non-exhausted alternative exists (current key stays active).\n\t */\n\tasync rotateOnFailure(providerId: string, errorMessage: string, now: number = Date.now()): Promise<SubEntry | null> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tif (!current) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tentry.id === current.id ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(current.id);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(entry.id) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the (now-exhausted) current key active, but still\n\t\t\t// persist its exhaustion state when a reset time was recorded.\n\t\t\tif (keys !== pool.keys) {\n\t\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mBAAmB,CAAC;AAIhH,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD,MAAM,WAAW,sBAAsB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAkB;IAErC,OAAO,CAAC,KAAK,CAAuC;IAGpD,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,eAGN;IAED,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAK9E;IAED,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,mBAAmB,CAEjG;IAED,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,GAAE,gBAAqB,GAAG,mBAAmB,CAI9F;IAED,OAAO,CAAC,gBAAgB;IAmCxB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;YAeD,iBAAiB;YAajB,UAAU;IAexB,OAAO,CAAC,MAAM;YASA,YAAY;IAapB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKlD;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAKjE;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU9E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC7D;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7D;IAED;;;;;OAKG;IACG,eAAe,CACpB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,GAAG,GAAE,MAAmB,GACtB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4C1B;IAED,kFAAkF;IAC5E,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcnE;CACD","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\nimport { clearPlanUsageCache } from \"./usage-service.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport interface RequestSubscriptionKey {\n\tproviderId: string;\n\tfingerprint: string;\n}\n\n/** Request identities are non-secret digests kept outside messages and storage. */\nexport function subscriptionKeyFingerprint(providerId: string, key: string): string {\n\treturn createHash(\"sha256\").update(providerId).update(\"\\0\").update(key).digest(\"hex\");\n}\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider fingerprints exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (content === undefined) return {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(content);\n\t\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error();\n\t\t\tfor (const value of Object.values(parsed as Record<string, unknown>)) {\n\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\tconst pool = value as Record<string, unknown>;\n\t\t\t\tif (typeof pool.active !== \"string\" || !Array.isArray(pool.keys)) throw new Error();\n\t\t\t\tconst ids = new Set<string>();\n\t\t\t\tfor (const value of pool.keys as unknown[]) {\n\t\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\t\tconst entry = value as Record<string, unknown>;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof entry.id !== \"string\" ||\n\t\t\t\t\t\tids.has(entry.id) ||\n\t\t\t\t\t\ttypeof entry.name !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.key !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.addedAt !== \"number\" ||\n\t\t\t\t\t\t!Number.isFinite(entry.addedAt) ||\n\t\t\t\t\t\t(entry.exhaustedUntil !== undefined &&\n\t\t\t\t\t\t\t(typeof entry.exhaustedUntil !== \"number\" || !Number.isFinite(entry.exhaustedUntil))) ||\n\t\t\t\t\t\t(entry.lastError !== undefined && typeof entry.lastError !== \"string\")\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error();\n\t\t\t\t\tids.add(entry.id);\n\t\t\t\t}\n\t\t\t\tif (!ids.has(pool.active)) throw new Error();\n\t\t\t}\n\t\t\treturn parsed as SubscriptionData;\n\t\t} catch {\n\t\t\tthrow new Error(\"Subscription storage is malformed. Repair it before changing subscriptions.\");\n\t\t}\n\t}\n\n\t/**\n\t * Lock order is subscriptions then auth. Keep selection and its auth mirror\n\t * under the subscription lock so later selectors cannot overtake a mirror.\n\t * These files are not an atomic commit: a failed mirror aborts the pool write;\n\t * a later pool write failure can leave auth ahead and a repeated selection repairs it.\n\t */\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(() =>\n\t\t\tthis.storage.withLockAsync(async (current) => {\n\t\t\t\tthis.data = this.parseStorageData(current);\n\t\t\t\tconst before = JSON.stringify(this.data, null, 2);\n\t\t\t\tconst result = await fn();\n\t\t\t\tconst after = JSON.stringify(this.data, null, 2);\n\t\t\t\treturn { result, next: before === after ? undefined : after };\n\t\t\t}),\n\t\t);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tclearPlanUsageCache();\n\t\ttry {\n\t\t\tawait this.authStorage.modify(providerId, async (current) => ({\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey,\n\t\t\t\t...(current?.type === \"api_key\" && current.env ? { env: current.env } : {}),\n\t\t\t}));\n\t\t} finally {\n\t\t\tclearPlanUsageCache();\n\t\t}\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tconst removed = pool.keys.find((entry) => entry.id === id)!;\n\t\t\tif (!keys.some((entry) => entry.key === removed.key)) {\n\t\t\t\tthis.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));\n\t\t\t}\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tclearPlanUsageCache();\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\t} finally {\n\t\t\t\t\tclearPlanUsageCache();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the request's key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. A later selection is preserved.\n\t * Returns the usable active key for retry, or null when no alternative exists.\n\t */\n\tasync rotateOnFailure(\n\t\tproviderId: string,\n\t\terrorMessage: string,\n\t\trequestFingerprint: string | undefined,\n\t\tnow: number = Date.now(),\n\t): Promise<SubEntry | null> {\n\t\tif (!requestFingerprint) return null;\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = this.data[providerId];\n\t\t\tif (!pool || pool.keys.length < 2) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tconst matchesRequest = (entry: SubEntry): boolean =>\n\t\t\t\tsubscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;\n\t\t\tif (!current || !pool.keys.some(matchesRequest)) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tmatchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(requestFingerprint);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tif (keys !== pool.keys) this.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t// Another owner already selected a different credential. Retry it without\n\t\t\t// replacing its selection or mirroring a stale failure over its auth.\n\t\t\tif (!matchesRequest(current)) return isExhausted(current) ? null : current;\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the now-exhausted current key active.\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (target) this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n}\n"]}
|
|
@@ -10,18 +10,23 @@
|
|
|
10
10
|
* Raw keys are never logged or embedded in error messages — errors refer to
|
|
11
11
|
* provider and key ids only.
|
|
12
12
|
*/
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
13
14
|
import { join } from "path";
|
|
14
15
|
import { getAgentDir } from "../config.js";
|
|
15
16
|
import { FileAuthStorageBackend, InMemoryAuthStorageBackend } from "./auth-storage.js";
|
|
16
17
|
import { parseResetTimeMs } from "./usage-limit.js";
|
|
18
|
+
import { clearPlanUsageCache } from "./usage-service.js";
|
|
19
|
+
/** Request identities are non-secret digests kept outside messages and storage. */
|
|
20
|
+
export function subscriptionKeyFingerprint(providerId, key) {
|
|
21
|
+
return createHash("sha256").update(providerId).update("\0").update(key).digest("hex");
|
|
22
|
+
}
|
|
17
23
|
export class SubscriptionManager {
|
|
18
24
|
data = {};
|
|
19
|
-
loaded = false;
|
|
20
25
|
storage;
|
|
21
26
|
authStorage;
|
|
22
27
|
// In-process write queue so mutations serialize even before the file lock.
|
|
23
28
|
queue = Promise.resolve();
|
|
24
|
-
// Per-provider
|
|
29
|
+
// Per-provider fingerprints exhausted without a parseable reset time.
|
|
25
30
|
// Process-local on purpose: infinite exhaustion is never persisted.
|
|
26
31
|
memoryExhausted = new Map();
|
|
27
32
|
constructor(authStorage, storage) {
|
|
@@ -40,38 +45,61 @@ export class SubscriptionManager {
|
|
|
40
45
|
return SubscriptionManager.fromStorage(authStorage, storage);
|
|
41
46
|
}
|
|
42
47
|
parseStorageData(content) {
|
|
43
|
-
if (
|
|
48
|
+
if (content === undefined)
|
|
44
49
|
return {};
|
|
45
|
-
}
|
|
46
|
-
return JSON.parse(content);
|
|
47
|
-
}
|
|
48
|
-
ensureLoaded() {
|
|
49
|
-
if (this.loaded)
|
|
50
|
-
return;
|
|
51
|
-
this.loaded = true;
|
|
52
|
-
let content;
|
|
53
50
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
const parsed = JSON.parse(content);
|
|
52
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
53
|
+
throw new Error();
|
|
54
|
+
for (const value of Object.values(parsed)) {
|
|
55
|
+
if (!value || typeof value !== "object")
|
|
56
|
+
throw new Error();
|
|
57
|
+
const pool = value;
|
|
58
|
+
if (typeof pool.active !== "string" || !Array.isArray(pool.keys))
|
|
59
|
+
throw new Error();
|
|
60
|
+
const ids = new Set();
|
|
61
|
+
for (const value of pool.keys) {
|
|
62
|
+
if (!value || typeof value !== "object")
|
|
63
|
+
throw new Error();
|
|
64
|
+
const entry = value;
|
|
65
|
+
if (typeof entry.id !== "string" ||
|
|
66
|
+
ids.has(entry.id) ||
|
|
67
|
+
typeof entry.name !== "string" ||
|
|
68
|
+
typeof entry.key !== "string" ||
|
|
69
|
+
typeof entry.addedAt !== "number" ||
|
|
70
|
+
!Number.isFinite(entry.addedAt) ||
|
|
71
|
+
(entry.exhaustedUntil !== undefined &&
|
|
72
|
+
(typeof entry.exhaustedUntil !== "number" || !Number.isFinite(entry.exhaustedUntil))) ||
|
|
73
|
+
(entry.lastError !== undefined && typeof entry.lastError !== "string"))
|
|
74
|
+
throw new Error();
|
|
75
|
+
ids.add(entry.id);
|
|
76
|
+
}
|
|
77
|
+
if (!ids.has(pool.active))
|
|
78
|
+
throw new Error();
|
|
79
|
+
}
|
|
80
|
+
return parsed;
|
|
59
81
|
}
|
|
60
82
|
catch {
|
|
61
|
-
|
|
83
|
+
throw new Error("Subscription storage is malformed. Repair it before changing subscriptions.");
|
|
62
84
|
}
|
|
63
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Lock order is subscriptions then auth. Keep selection and its auth mirror
|
|
88
|
+
* under the subscription lock so later selectors cannot overtake a mirror.
|
|
89
|
+
* These files are not an atomic commit: a failed mirror aborts the pool write;
|
|
90
|
+
* a later pool write failure can leave auth ahead and a repeated selection repairs it.
|
|
91
|
+
*/
|
|
64
92
|
enqueue(fn) {
|
|
65
|
-
const next = this.queue.then(
|
|
93
|
+
const next = this.queue.then(() => this.storage.withLockAsync(async (current) => {
|
|
94
|
+
this.data = this.parseStorageData(current);
|
|
95
|
+
const before = JSON.stringify(this.data, null, 2);
|
|
96
|
+
const result = await fn();
|
|
97
|
+
const after = JSON.stringify(this.data, null, 2);
|
|
98
|
+
return { result, next: before === after ? undefined : after };
|
|
99
|
+
}));
|
|
66
100
|
this.queue = next.catch(() => { });
|
|
67
101
|
return next;
|
|
68
102
|
}
|
|
69
|
-
persist() {
|
|
70
|
-
return this.storage.withLockAsync(async () => ({
|
|
71
|
-
result: undefined,
|
|
72
|
-
next: JSON.stringify(this.data, null, 2),
|
|
73
|
-
}));
|
|
74
|
-
}
|
|
75
103
|
/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */
|
|
76
104
|
async readRawCredential(providerId) {
|
|
77
105
|
let current;
|
|
@@ -86,7 +114,6 @@ export class SubscriptionManager {
|
|
|
86
114
|
* as "Sub 1" on first access. OAuth credentials are not imported.
|
|
87
115
|
*/
|
|
88
116
|
async ensurePool(providerId) {
|
|
89
|
-
this.ensureLoaded();
|
|
90
117
|
const existing = this.data[providerId];
|
|
91
118
|
if (existing)
|
|
92
119
|
return existing;
|
|
@@ -98,7 +125,6 @@ export class SubscriptionManager {
|
|
|
98
125
|
keys: [{ id: "1", name: "Sub 1", key: credential.key, addedAt: Date.now() }],
|
|
99
126
|
};
|
|
100
127
|
this.data = { ...this.data, [providerId]: pool };
|
|
101
|
-
await this.persist();
|
|
102
128
|
return pool;
|
|
103
129
|
}
|
|
104
130
|
nextId(pool) {
|
|
@@ -111,7 +137,17 @@ export class SubscriptionManager {
|
|
|
111
137
|
return String(max + 1);
|
|
112
138
|
}
|
|
113
139
|
async mirrorActive(providerId, key) {
|
|
114
|
-
|
|
140
|
+
clearPlanUsageCache();
|
|
141
|
+
try {
|
|
142
|
+
await this.authStorage.modify(providerId, async (current) => ({
|
|
143
|
+
type: "api_key",
|
|
144
|
+
key,
|
|
145
|
+
...(current?.type === "api_key" && current.env ? { env: current.env } : {}),
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
clearPlanUsageCache();
|
|
150
|
+
}
|
|
115
151
|
}
|
|
116
152
|
async list(providerId) {
|
|
117
153
|
return this.enqueue(async () => {
|
|
@@ -127,13 +163,11 @@ export class SubscriptionManager {
|
|
|
127
163
|
}
|
|
128
164
|
async addKey(providerId, key, name) {
|
|
129
165
|
return this.enqueue(async () => {
|
|
130
|
-
this.ensureLoaded();
|
|
131
166
|
const pool = await this.ensurePool(providerId);
|
|
132
167
|
const id = pool ? this.nextId(pool) : "1";
|
|
133
168
|
const entry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };
|
|
134
169
|
const keys = [...(pool?.keys ?? []), entry];
|
|
135
170
|
this.data = { ...this.data, [providerId]: { active: id, keys } };
|
|
136
|
-
await this.persist();
|
|
137
171
|
await this.mirrorActive(providerId, key);
|
|
138
172
|
return entry;
|
|
139
173
|
});
|
|
@@ -144,27 +178,33 @@ export class SubscriptionManager {
|
|
|
144
178
|
if (!pool || !pool.keys.some((entry) => entry.id === id))
|
|
145
179
|
return;
|
|
146
180
|
const keys = pool.keys.filter((entry) => entry.id !== id);
|
|
147
|
-
|
|
181
|
+
const removed = pool.keys.find((entry) => entry.id === id);
|
|
182
|
+
if (!keys.some((entry) => entry.key === removed.key)) {
|
|
183
|
+
this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));
|
|
184
|
+
}
|
|
148
185
|
if (keys.length === 0) {
|
|
149
186
|
const data = { ...this.data };
|
|
150
187
|
delete data[providerId];
|
|
151
188
|
this.data = data;
|
|
152
|
-
|
|
153
|
-
|
|
189
|
+
clearPlanUsageCache();
|
|
190
|
+
try {
|
|
191
|
+
await this.authStorage.delete(providerId);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
clearPlanUsageCache();
|
|
195
|
+
}
|
|
154
196
|
return;
|
|
155
197
|
}
|
|
156
198
|
let active = pool.active;
|
|
157
199
|
if (active === id) {
|
|
158
200
|
active = keys[0]?.id ?? active;
|
|
159
201
|
this.data = { ...this.data, [providerId]: { active, keys } };
|
|
160
|
-
await this.persist();
|
|
161
202
|
const promoted = keys.find((entry) => entry.id === active);
|
|
162
203
|
if (promoted)
|
|
163
204
|
await this.mirrorActive(providerId, promoted.key);
|
|
164
205
|
return;
|
|
165
206
|
}
|
|
166
207
|
this.data = { ...this.data, [providerId]: { active, keys } };
|
|
167
|
-
await this.persist();
|
|
168
208
|
});
|
|
169
209
|
}
|
|
170
210
|
async renameKey(providerId, id, name) {
|
|
@@ -174,7 +214,6 @@ export class SubscriptionManager {
|
|
|
174
214
|
return;
|
|
175
215
|
const keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));
|
|
176
216
|
this.data = { ...this.data, [providerId]: { ...pool, keys } };
|
|
177
|
-
await this.persist();
|
|
178
217
|
});
|
|
179
218
|
}
|
|
180
219
|
async setActive(providerId, id) {
|
|
@@ -184,65 +223,68 @@ export class SubscriptionManager {
|
|
|
184
223
|
if (!pool || !entry)
|
|
185
224
|
return;
|
|
186
225
|
this.data = { ...this.data, [providerId]: { ...pool, active: id } };
|
|
187
|
-
await this.persist();
|
|
188
226
|
await this.mirrorActive(providerId, entry.key);
|
|
189
227
|
});
|
|
190
228
|
}
|
|
191
229
|
/**
|
|
192
|
-
* Mark the
|
|
230
|
+
* Mark the request's key exhausted and rotate to the next non-exhausted key in
|
|
193
231
|
* round-robin order. A parseable reset time is persisted as exhaustedUntil;
|
|
194
|
-
* otherwise the exhaustion is process-local only.
|
|
195
|
-
* or null when no
|
|
232
|
+
* otherwise the exhaustion is process-local only. A later selection is preserved.
|
|
233
|
+
* Returns the usable active key for retry, or null when no alternative exists.
|
|
196
234
|
*/
|
|
197
|
-
async rotateOnFailure(providerId, errorMessage, now = Date.now()) {
|
|
235
|
+
async rotateOnFailure(providerId, errorMessage, requestFingerprint, now = Date.now()) {
|
|
236
|
+
if (!requestFingerprint)
|
|
237
|
+
return null;
|
|
198
238
|
return this.enqueue(async () => {
|
|
199
|
-
const pool =
|
|
200
|
-
if (!pool)
|
|
239
|
+
const pool = this.data[providerId];
|
|
240
|
+
if (!pool || pool.keys.length < 2)
|
|
201
241
|
return null;
|
|
202
242
|
const currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);
|
|
203
243
|
if (currentIndex === -1)
|
|
204
244
|
return null;
|
|
205
245
|
const current = pool.keys[currentIndex];
|
|
206
|
-
|
|
246
|
+
const matchesRequest = (entry) => subscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;
|
|
247
|
+
if (!current || !pool.keys.some(matchesRequest))
|
|
207
248
|
return null;
|
|
208
249
|
const resetAt = parseResetTimeMs(errorMessage);
|
|
209
250
|
let keys = pool.keys;
|
|
210
251
|
if (resetAt !== undefined && resetAt > now) {
|
|
211
|
-
keys = pool.keys.map((entry) => entry
|
|
252
|
+
keys = pool.keys.map((entry) => matchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry);
|
|
212
253
|
}
|
|
213
254
|
else {
|
|
214
255
|
const set = this.memoryExhausted.get(providerId) ?? new Set();
|
|
215
|
-
set.add(
|
|
256
|
+
set.add(requestFingerprint);
|
|
216
257
|
this.memoryExhausted.set(providerId, set);
|
|
217
258
|
}
|
|
218
|
-
const isExhausted = (entry) => this.memoryExhausted.get(providerId)?.has(entry.
|
|
259
|
+
const isExhausted = (entry) => this.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||
|
|
219
260
|
(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);
|
|
261
|
+
if (keys !== pool.keys)
|
|
262
|
+
this.data = { ...this.data, [providerId]: { ...pool, keys } };
|
|
263
|
+
// Another owner already selected a different credential. Retry it without
|
|
264
|
+
// replacing its selection or mirroring a stale failure over its auth.
|
|
265
|
+
if (!matchesRequest(current))
|
|
266
|
+
return isExhausted(current) ? null : current;
|
|
220
267
|
for (let offset = 1; offset < keys.length; offset++) {
|
|
221
268
|
const candidate = keys[(currentIndex + offset) % keys.length];
|
|
222
269
|
if (!candidate || isExhausted(candidate))
|
|
223
270
|
continue;
|
|
224
271
|
this.data = { ...this.data, [providerId]: { active: candidate.id, keys } };
|
|
225
|
-
await this.persist();
|
|
226
272
|
await this.mirrorActive(providerId, candidate.key);
|
|
227
273
|
return candidate;
|
|
228
274
|
}
|
|
229
|
-
// No alternative: keep the
|
|
230
|
-
// persist its exhaustion state when a reset time was recorded.
|
|
231
|
-
if (keys !== pool.keys) {
|
|
232
|
-
this.data = { ...this.data, [providerId]: { ...pool, keys } };
|
|
233
|
-
await this.persist();
|
|
234
|
-
}
|
|
275
|
+
// No alternative: keep the now-exhausted current key active.
|
|
235
276
|
return null;
|
|
236
277
|
});
|
|
237
278
|
}
|
|
238
279
|
/** Manual reactivate: clears persisted and process-local exhaustion for a key. */
|
|
239
280
|
async clearExhaustion(providerId, id) {
|
|
240
281
|
return this.enqueue(async () => {
|
|
241
|
-
this.memoryExhausted.get(providerId)?.delete(id);
|
|
242
282
|
const pool = await this.ensurePool(providerId);
|
|
243
283
|
if (!pool)
|
|
244
284
|
return;
|
|
245
285
|
const target = pool.keys.find((entry) => entry.id === id);
|
|
286
|
+
if (target)
|
|
287
|
+
this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));
|
|
246
288
|
if (!target || (target.exhaustedUntil === undefined && target.lastError === undefined))
|
|
247
289
|
return;
|
|
248
290
|
const keys = pool.keys.map((entry) => {
|
|
@@ -252,7 +294,6 @@ export class SubscriptionManager {
|
|
|
252
294
|
return rest;
|
|
253
295
|
});
|
|
254
296
|
this.data = { ...this.data, [providerId]: { ...pool, keys } };
|
|
255
|
-
await this.persist();
|
|
256
297
|
});
|
|
257
298
|
}
|
|
258
299
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate loaded = false;\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider ids of keys exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as SubscriptionData;\n\t}\n\n\tprivate ensureLoaded(): void {\n\t\tif (this.loaded) return;\n\t\tthis.loaded = true;\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t} catch {\n\t\t\t// Preserve the empty snapshot; a malformed file is never overwritten by a load.\n\t\t}\n\t}\n\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(fn);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\tprivate persist(): Promise<void> {\n\t\treturn this.storage.withLockAsync(async () => ({\n\t\t\tresult: undefined,\n\t\t\tnext: JSON.stringify(this.data, null, 2),\n\t\t}));\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tthis.ensureLoaded();\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\tawait this.persist();\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tawait this.authStorage.modify(providerId, async () => ({ type: \"api_key\", key }));\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.ensureLoaded();\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the active key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. Returns the new active key,\n\t * or null when no non-exhausted alternative exists (current key stays active).\n\t */\n\tasync rotateOnFailure(providerId: string, errorMessage: string, now: number = Date.now()): Promise<SubEntry | null> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tif (!current) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tentry.id === current.id ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(current.id);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(entry.id) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the (now-exhausted) current key active, but still\n\t\t\t// persist its exhaustion state when a reset time was recorded.\n\t\t\tif (keys !== pool.keys) {\n\t\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\nimport { clearPlanUsageCache } from \"./usage-service.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport interface RequestSubscriptionKey {\n\tproviderId: string;\n\tfingerprint: string;\n}\n\n/** Request identities are non-secret digests kept outside messages and storage. */\nexport function subscriptionKeyFingerprint(providerId: string, key: string): string {\n\treturn createHash(\"sha256\").update(providerId).update(\"\\0\").update(key).digest(\"hex\");\n}\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider fingerprints exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (content === undefined) return {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(content);\n\t\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error();\n\t\t\tfor (const value of Object.values(parsed as Record<string, unknown>)) {\n\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\tconst pool = value as Record<string, unknown>;\n\t\t\t\tif (typeof pool.active !== \"string\" || !Array.isArray(pool.keys)) throw new Error();\n\t\t\t\tconst ids = new Set<string>();\n\t\t\t\tfor (const value of pool.keys as unknown[]) {\n\t\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\t\tconst entry = value as Record<string, unknown>;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof entry.id !== \"string\" ||\n\t\t\t\t\t\tids.has(entry.id) ||\n\t\t\t\t\t\ttypeof entry.name !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.key !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.addedAt !== \"number\" ||\n\t\t\t\t\t\t!Number.isFinite(entry.addedAt) ||\n\t\t\t\t\t\t(entry.exhaustedUntil !== undefined &&\n\t\t\t\t\t\t\t(typeof entry.exhaustedUntil !== \"number\" || !Number.isFinite(entry.exhaustedUntil))) ||\n\t\t\t\t\t\t(entry.lastError !== undefined && typeof entry.lastError !== \"string\")\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error();\n\t\t\t\t\tids.add(entry.id);\n\t\t\t\t}\n\t\t\t\tif (!ids.has(pool.active)) throw new Error();\n\t\t\t}\n\t\t\treturn parsed as SubscriptionData;\n\t\t} catch {\n\t\t\tthrow new Error(\"Subscription storage is malformed. Repair it before changing subscriptions.\");\n\t\t}\n\t}\n\n\t/**\n\t * Lock order is subscriptions then auth. Keep selection and its auth mirror\n\t * under the subscription lock so later selectors cannot overtake a mirror.\n\t * These files are not an atomic commit: a failed mirror aborts the pool write;\n\t * a later pool write failure can leave auth ahead and a repeated selection repairs it.\n\t */\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(() =>\n\t\t\tthis.storage.withLockAsync(async (current) => {\n\t\t\t\tthis.data = this.parseStorageData(current);\n\t\t\t\tconst before = JSON.stringify(this.data, null, 2);\n\t\t\t\tconst result = await fn();\n\t\t\t\tconst after = JSON.stringify(this.data, null, 2);\n\t\t\t\treturn { result, next: before === after ? undefined : after };\n\t\t\t}),\n\t\t);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tclearPlanUsageCache();\n\t\ttry {\n\t\t\tawait this.authStorage.modify(providerId, async (current) => ({\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey,\n\t\t\t\t...(current?.type === \"api_key\" && current.env ? { env: current.env } : {}),\n\t\t\t}));\n\t\t} finally {\n\t\t\tclearPlanUsageCache();\n\t\t}\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tconst removed = pool.keys.find((entry) => entry.id === id)!;\n\t\t\tif (!keys.some((entry) => entry.key === removed.key)) {\n\t\t\t\tthis.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));\n\t\t\t}\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tclearPlanUsageCache();\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\t} finally {\n\t\t\t\t\tclearPlanUsageCache();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the request's key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. A later selection is preserved.\n\t * Returns the usable active key for retry, or null when no alternative exists.\n\t */\n\tasync rotateOnFailure(\n\t\tproviderId: string,\n\t\terrorMessage: string,\n\t\trequestFingerprint: string | undefined,\n\t\tnow: number = Date.now(),\n\t): Promise<SubEntry | null> {\n\t\tif (!requestFingerprint) return null;\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = this.data[providerId];\n\t\t\tif (!pool || pool.keys.length < 2) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tconst matchesRequest = (entry: SubEntry): boolean =>\n\t\t\t\tsubscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;\n\t\t\tif (!current || !pool.keys.some(matchesRequest)) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tmatchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(requestFingerprint);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tif (keys !== pool.keys) this.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t// Another owner already selected a different credential. Retry it without\n\t\t\t// replacing its selection or mirroring a stale failure over its auth.\n\t\t\tif (!matchesRequest(current)) return isExhausted(current) ? null : current;\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the now-exhausted current key active.\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (target) this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n}\n"]}
|
|
@@ -19,7 +19,8 @@ export declare function getProjectTrustOptions(cwd: string, options?: {
|
|
|
19
19
|
}): ProjectTrustOption[];
|
|
20
20
|
/**
|
|
21
21
|
* Returns true when cwd has project-local resources that must be gated by
|
|
22
|
-
* project trust:
|
|
22
|
+
* project trust: settings/resources under cwd/.lunr, executable integration
|
|
23
|
+
* configuration, or .agents/skills in
|
|
23
24
|
* cwd or one of its ancestors. Returns false when no such project resources
|
|
24
25
|
* exist. The user/global ~/.agents/skills directory is always treated as a
|
|
25
26
|
* trusted user resource and is ignored here, even when cwd is $HOME.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAmCD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,EAAE,CA8BpH;AAkFD;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA6BtE;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAE3B;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAErC;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAKnD;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAErD;IAED,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAa7C;CACD","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\nexport interface ProjectTrustStoreEntry {\n\tpath: string;\n\tdecision: boolean;\n}\n\nexport interface ProjectTrustUpdate {\n\tpath: string;\n\tdecision: ProjectTrustDecision;\n}\n\nexport interface ProjectTrustOption {\n\tlabel: string;\n\ttrusted: boolean;\n\tupdates: ProjectTrustUpdate[];\n\tsavedPath?: string;\n}\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [\n\t\"settings.json\",\n\t\"mcp.json\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n] as const;\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction findNearestTrustEntry(data: TrustFile, cwd: string): ProjectTrustStoreEntry | null {\n\tlet currentDir = normalizeCwd(cwd);\n\twhile (true) {\n\t\tconst value = data[currentDir];\n\t\tif (value === true || value === false) {\n\t\t\treturn { path: currentDir, decision: value };\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn null;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport function getProjectTrustParentPath(cwd: string): string | undefined {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst parentDir = dirname(trustPath);\n\treturn parentDir === trustPath ? undefined : parentDir;\n}\n\nexport function getProjectTrustOptions(cwd: string, options?: { includeSessionOnly?: boolean }): ProjectTrustOption[] {\n\tconst trustPath = normalizeCwd(cwd);\n\tconst trustOptions: ProjectTrustOption[] = [\n\t\t{ label: \"Trust\", trusted: true, updates: [{ path: trustPath, decision: true }], savedPath: trustPath },\n\t];\n\tconst parentPath = getProjectTrustParentPath(cwd);\n\tif (parentPath !== undefined) {\n\t\ttrustOptions.push({\n\t\t\tlabel: `Trust parent folder (${parentPath})`,\n\t\t\ttrusted: true,\n\t\t\tupdates: [\n\t\t\t\t{ path: parentPath, decision: true },\n\t\t\t\t{ path: trustPath, decision: null },\n\t\t\t],\n\t\t\tsavedPath: parentPath,\n\t\t});\n\t}\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Trust (this session only)\", trusted: true, updates: [] });\n\t}\n\ttrustOptions.push({\n\t\tlabel: \"Do not trust\",\n\t\ttrusted: false,\n\t\tupdates: [{ path: trustPath, decision: false }],\n\t\tsavedPath: trustPath,\n\t});\n\tif (options?.includeSessionOnly) {\n\t\ttrustOptions.push({ label: \"Do not trust (this session only)\", trusted: false, updates: [] });\n\t}\n\treturn trustOptions;\n}\n\nfunction readTrustFile(path: string): TrustFile {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to read trust store ${path}: ${message}`);\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Returns true when cwd has project-local resources that must be gated by\n * project trust: settings/resources under cwd/.lunr, executable integration\n * configuration, or .agents/skills in\n * cwd or one of its ancestors. Returns false when no such project resources\n * exist. The user/global ~/.agents/skills directory is always treated as a\n * trusted user resource and is ignored here, even when cwd is $HOME.\n */\nexport function hasTrustRequiringProjectResources(cwd: string): boolean {\n\tconst homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));\n\tconst userAgentsSkillsDir = join(homeDir, \".agents\", \"skills\");\n\tlet currentDir = canonicalizePath(resolvePath(cwd));\n\n\tconst configDir = join(currentDir, CONFIG_DIR_NAME);\n\tif (\n\t\t[\".mcp.json\", \".pi/mcp.json\", \".vscode/mcp.json\", \".pi-lsp.json\"].some((entry) =>\n\t\t\texistsSync(join(currentDir, entry)),\n\t\t)\n\t) {\n\t\treturn true;\n\t}\n\tif (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tconst agentsSkillsDir = join(currentDir, \".agents\", \"skills\");\n\t\tif (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn this.getEntry(cwd)?.decision ?? null;\n\t}\n\n\tgetEntry(cwd: string): ProjectTrustStoreEntry | null {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\treturn findNearestTrustEntry(data, cwd);\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tthis.setMany([{ path: cwd, decision }]);\n\t}\n\n\tsetMany(decisions: ProjectTrustUpdate[]): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tfor (const { path, decision } of decisions) {\n\t\t\t\tconst key = normalizeCwd(path);\n\t\t\t\tif (decision === null) {\n\t\t\t\t\tdelete data[key];\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = decision;\n\t\t\t\t}\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
|
|
@@ -6,6 +6,7 @@ import { CONFIG_DIR_NAME } from "../config.js";
|
|
|
6
6
|
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
7
7
|
const TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [
|
|
8
8
|
"settings.json",
|
|
9
|
+
"mcp.json",
|
|
9
10
|
"extensions",
|
|
10
11
|
"skills",
|
|
11
12
|
"prompts",
|
|
@@ -141,7 +142,8 @@ function withTrustFileLock(path, fn) {
|
|
|
141
142
|
}
|
|
142
143
|
/**
|
|
143
144
|
* Returns true when cwd has project-local resources that must be gated by
|
|
144
|
-
* project trust:
|
|
145
|
+
* project trust: settings/resources under cwd/.lunr, executable integration
|
|
146
|
+
* configuration, or .agents/skills in
|
|
145
147
|
* cwd or one of its ancestors. Returns false when no such project resources
|
|
146
148
|
* exist. The user/global ~/.agents/skills directory is always treated as a
|
|
147
149
|
* trusted user resource and is ignored here, even when cwd is $HOME.
|
|
@@ -151,6 +153,9 @@ export function hasTrustRequiringProjectResources(cwd) {
|
|
|
151
153
|
const userAgentsSkillsDir = join(homeDir, ".agents", "skills");
|
|
152
154
|
let currentDir = canonicalizePath(resolvePath(cwd));
|
|
153
155
|
const configDir = join(currentDir, CONFIG_DIR_NAME);
|
|
156
|
+
if ([".mcp.json", ".pi/mcp.json", ".vscode/mcp.json", ".pi-lsp.json"].some((entry) => existsSync(join(currentDir, entry)))) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
154
159
|
if (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {
|
|
155
160
|
return true;
|
|
156
161
|
}
|