@ashx-j/lunr 0.2.26 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/LICENSE +22 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +6 -10
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
- package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-cron.js +94 -108
- package/dist/builtin-extensions/lunr-cron.js.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js +5 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +2 -2
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
- package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
- package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
- package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts +4 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +6 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +17 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +5 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +78 -42
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts +0 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +134 -28
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +26 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts +7 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js +70 -38
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +9 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +13 -62
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +18 -9
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-tps.js +28 -17
- package/dist/builtin-extensions/pi-tps.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
- package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
- package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +48 -73
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
- package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
- package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/google-vertex.json +1 -1
- package/dist/catalog/providers/google.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/cli/install-cli.d.ts.map +1 -1
- package/dist/cli/install-cli.js +38 -30
- package/dist/cli/install-cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +13 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +75 -11
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +46 -21
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +344 -126
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/browser/runtime.d.ts +2 -0
- package/dist/core/browser/runtime.d.ts.map +1 -1
- package/dist/core/browser/runtime.js +80 -45
- package/dist/core/browser/runtime.js.map +1 -1
- package/dist/core/browser/setup.d.ts.map +1 -1
- package/dist/core/browser/setup.js +12 -1
- package/dist/core/browser/setup.js.map +1 -1
- package/dist/core/cache-stats.d.ts +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +5 -11
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +38 -92
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts.map +1 -1
- package/dist/core/computer-use/adapter.js +7 -2
- package/dist/core/computer-use/adapter.js.map +1 -1
- package/dist/core/computer-use/image.d.ts.map +1 -1
- package/dist/core/computer-use/image.js +17 -5
- package/dist/core/computer-use/image.js.map +1 -1
- package/dist/core/computer-use/schemas.d.ts.map +1 -1
- package/dist/core/computer-use/schemas.js +23 -6
- package/dist/core/computer-use/schemas.js.map +1 -1
- package/dist/core/computer-use/workflow.d.ts.map +1 -1
- package/dist/core/computer-use/workflow.js +459 -119
- package/dist/core/computer-use/workflow.js.map +1 -1
- package/dist/core/context-breakdown.d.ts +2 -2
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +22 -20
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/cron/jobs.d.ts +26 -8
- package/dist/core/cron/jobs.d.ts.map +1 -1
- package/dist/core/cron/jobs.js +357 -232
- package/dist/core/cron/jobs.js.map +1 -1
- package/dist/core/cron/origin-context.d.ts +1 -0
- package/dist/core/cron/origin-context.d.ts.map +1 -1
- package/dist/core/cron/origin-context.js.map +1 -1
- package/dist/core/cron/scheduler.d.ts +22 -17
- package/dist/core/cron/scheduler.d.ts.map +1 -1
- package/dist/core/cron/scheduler.js +153 -73
- package/dist/core/cron/scheduler.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +11 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +4 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +22 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +1 -1
- package/dist/core/large-subagent-launch.d.ts.map +1 -1
- package/dist/core/large-subagent-launch.js +7 -7
- package/dist/core/large-subagent-launch.js.map +1 -1
- package/dist/core/model-registry.d.ts +2 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-runtime.d.ts +4 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +40 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +3 -3
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +11 -35
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts +5 -13
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +126 -116
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-resource-bridge.d.ts +12 -0
- package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
- package/dist/core/prompt-resource-bridge.js +9 -0
- package/dist/core/prompt-resource-bridge.js.map +1 -0
- package/dist/core/protected-targets.d.ts +6 -0
- package/dist/core/protected-targets.d.ts.map +1 -0
- package/dist/core/protected-targets.js +45 -0
- package/dist/core/protected-targets.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +7 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/rollback.d.ts +19 -3
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +172 -56
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +3 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +14 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +111 -33
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-ownership.d.ts.map +1 -1
- package/dist/core/session-ownership.js +8 -3
- package/dist/core/session-ownership.js.map +1 -1
- package/dist/core/session-retention.d.ts.map +1 -1
- package/dist/core/session-retention.js +18 -7
- package/dist/core/session-retention.js.map +1 -1
- package/dist/core/session-startup-settings.d.ts +10 -0
- package/dist/core/session-startup-settings.d.ts.map +1 -0
- package/dist/core/session-startup-settings.js +70 -0
- package/dist/core/session-startup-settings.js.map +1 -0
- package/dist/core/subscriptions.d.ts +16 -7
- package/dist/core/subscriptions.d.ts.map +1 -1
- package/dist/core/subscriptions.js +96 -55
- package/dist/core/subscriptions.js.map +1 -1
- package/dist/core/trust-manager.d.ts +2 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +6 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/update-check.d.ts.map +1 -1
- package/dist/core/update-check.js.map +1 -1
- package/dist/core/usage-accounting.d.ts +47 -0
- package/dist/core/usage-accounting.d.ts.map +1 -0
- package/dist/core/usage-accounting.js +205 -0
- package/dist/core/usage-accounting.js.map +1 -0
- package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
- package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
- package/dist/core/usage-adapters/kimi-coding.js +2 -2
- package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
- package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
- package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
- package/dist/core/usage-adapters/openai-codex.js +3 -3
- package/dist/core/usage-adapters/openai-codex.js.map +1 -1
- package/dist/core/usage-adapters/xai.d.ts +2 -2
- package/dist/core/usage-adapters/xai.d.ts.map +1 -1
- package/dist/core/usage-adapters/xai.js +3 -3
- package/dist/core/usage-adapters/xai.js.map +1 -1
- package/dist/core/usage-adapters/zai.d.ts +2 -2
- package/dist/core/usage-adapters/zai.d.ts.map +1 -1
- package/dist/core/usage-adapters/zai.js +2 -2
- package/dist/core/usage-adapters/zai.js.map +1 -1
- package/dist/core/usage-history.d.ts +1 -18
- package/dist/core/usage-history.d.ts.map +1 -1
- package/dist/core/usage-history.js +46 -114
- package/dist/core/usage-history.js.map +1 -1
- package/dist/core/usage-service.d.ts +9 -2
- package/dist/core/usage-service.d.ts.map +1 -1
- package/dist/core/usage-service.js +95 -50
- package/dist/core/usage-service.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts +9 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +91 -37
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/buttons.d.ts +8 -1
- package/dist/gateway/buttons.d.ts.map +1 -1
- package/dist/gateway/buttons.js +76 -18
- package/dist/gateway/buttons.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -0
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +10 -4
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts +12 -11
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +65 -56
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +2 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/mobile-commands.d.ts +1 -0
- package/dist/gateway/mobile-commands.d.ts.map +1 -1
- package/dist/gateway/mobile-commands.js +69 -15
- package/dist/gateway/mobile-commands.js.map +1 -1
- package/dist/gateway/presenter.d.ts +12 -2
- package/dist/gateway/presenter.d.ts.map +1 -1
- package/dist/gateway/presenter.js +73 -38
- package/dist/gateway/presenter.js.map +1 -1
- package/dist/gateway/router.d.ts +2 -2
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +17 -2
- package/dist/gateway/router.js.map +1 -1
- package/dist/gateway/service.d.ts +2 -0
- package/dist/gateway/service.d.ts.map +1 -1
- package/dist/gateway/service.js +153 -0
- package/dist/gateway/service.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +86 -33
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +6 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-view.js +2 -2
- package/dist/modes/interactive/components/context-view.js.map +1 -1
- package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
- package/dist/modes/interactive/components/copyable-text.js +3 -2
- package/dist/modes/interactive/components/copyable-text.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/usage-view.d.ts +5 -0
- package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/usage-view.js +21 -6
- package/dist/modes/interactive/components/usage-view.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -7
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +151 -131
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
- package/dist/node-runtime/agent-session-services-RTUGP3XR.js +54 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-N4EUD6SB.js} +8 -7
- package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
- package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
- package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
- package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
- package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
- package/dist/node-runtime/chunk-4QRUZ64T.js +1203 -0
- package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
- package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
- package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
- package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-BMRXLQWQ.js} +464 -160
- package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-DFMPRAQQ.js} +162 -82
- package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
- package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-E5DQGNJI.js} +455 -278
- package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
- package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
- package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
- package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
- package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
- package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
- package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
- package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
- package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
- package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
- package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
- package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
- package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
- package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-ICKGAKWX.js} +23 -59
- package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
- package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
- package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
- package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JEK35V2M.js} +45 -31
- package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
- package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
- package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
- package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-PWHTSQC4.js} +83 -21
- package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-QT3F6UEV.js} +233 -169
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-RY3FJ6WI.js} +29 -27
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-STMX7W5I.js} +3 -3
- package/dist/node-runtime/chunk-SWWHZ2RI.js +158 -0
- package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
- package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-U3YVZ6UC.js} +18 -14
- package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
- package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
- package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
- package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
- package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
- package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
- package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
- package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
- package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
- package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-FSBU2EVZ.js → command-YRT7I2BW.js} +5 -5
- package/dist/node-runtime/{commands-NAAL7KNT.js → commands-2ETOWWRA.js} +16 -16
- package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
- package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
- package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
- package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-DBQRRMAY.js} +4 -4
- package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
- package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
- package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
- package/dist/node-runtime/{gateway-DQM333YV.js → gateway-V6K2CDWN.js} +51 -42
- package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
- package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
- package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
- package/dist/node-runtime/{goal-NJEE5CTU.js → goal-J7TYMOBK.js} +14 -8
- package/dist/node-runtime/index.js +51 -48
- package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
- package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-ASZ7APYP.js +75 -0
- package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
- package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
- package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
- package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
- package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-WD3YH4CR.js} +93 -80
- package/dist/node-runtime/main.js +43 -41
- package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-ICUXFW5V.js} +12 -12
- package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
- package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
- package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
- package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
- package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
- package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
- package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
- package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
- package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
- package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-QPU3OXE4.js} +114 -167
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
- package/dist/node-runtime/rpc-entry.js +43 -41
- package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
- package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
- package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
- package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LYH53ESV.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-ZJ3N5Q74.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-UI6SHG3X.js} +412 -288
- package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
- package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
- package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
- package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
- package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
- package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
- package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
- package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +25 -10
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +2 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +33 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/sanitize-terminal-text.d.ts +7 -0
- package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
- package/dist/utils/sanitize-terminal-text.js +67 -0
- package/dist/utils/sanitize-terminal-text.js.map +1 -0
- package/docs/browser.md +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +25 -7
- package/docs/keybindings.md +1 -1
- package/docs/models.md +5 -5
- package/docs/packages.md +1 -1
- package/docs/providers.md +2 -2
- package/docs/rpc.md +7 -7
- package/docs/security.md +10 -5
- package/docs/sessions.md +2 -2
- package/docs/skills.md +1 -1
- package/docs/terminal-setup.md +7 -7
- package/docs/themes.md +1 -1
- package/docs/usage.md +1 -1
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +2 -2
- package/examples/extensions/gondolin/index.ts +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +2 -2
- package/examples/extensions/input-transform-streaming.ts +2 -2
- package/examples/extensions/input-transform.ts +2 -2
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/kimi-deferred-tools.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +3 -3
- package/examples/extensions/overlay-qa-tests.ts +1 -1
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/preset.ts +3 -3
- package/examples/extensions/project-trust.ts +4 -4
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +7 -7
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/shutdown-command.ts +5 -5
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/subagent/README.md +3 -3
- package/examples/extensions/subagent/index.ts +2 -2
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/06-extensions.ts +2 -2
- package/examples/sdk/08-prompt-templates.ts +1 -1
- package/install-lock/package-lock.json +24 -24
- package/install-lock/package.json +2 -2
- package/npm-shrinkwrap.json +60 -24
- package/package.json +9 -9
- package/dist/modes/interactive/components/exit-card.d.ts +0 -22
- package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
- package/dist/modes/interactive/components/exit-card.js +0 -69
- package/dist/modes/interactive/components/exit-card.js.map +0 -1
- package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
- package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
- package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
- package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
- package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
- package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/gateway/router.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway router.\n *\n * Every inbound MessageEvent flows through handleEvent:\n * 1. group gating (allowedChats / freeResponseChats / requireMention,\n * with adapter-supplied metadata.mentionedBot)\n * 2. authorization (authz.ts); denied DMs get a pairing code when the\n * behavior is \"pair\" and the user isn't rate-limited\n * 3. command registry (/new, /undo, /redo, /model, /sessions, /title,\n * /context, /compact, /thinking, /stop, /status, /help, /whoami)\n * 4. normal path: bridge.runTurn with a StreamConsumer when streaming is\n * enabled and the adapter supports edit; the final text is\n * silence-filtered and folded into a successful streaming preview edit.\n * Failed final edits and ordinary answers enter the durable outbox, which\n * acknowledges chunks in order and retries by destination.\n * Errors surface as a compact \"⚠ <one-line>\" — never a stack trace.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { type BridgeSession, type BridgeSessionStatus, QUEUED, type TurnCallbacks } from \"./agent-bridge.ts\";\nimport { registerGatewayApprovalHandler, runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized, requireAuthorized } from \"./authz.ts\";\nimport { CHAT_COMMANDS, runChatCommand, sendCommandReply } from \"./commands.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { bindConversation, conversationBinding } from \"./conversations.ts\";\nimport { resolveWithinRoots } from \"./mobile-commands.ts\";\nimport type { PairingStore } from \"./pairing.ts\";\nimport {\n\tacceptGatewayInput,\n\tgatewayEpoch,\n\tinvalidateGatewayDialogs,\n\tqueueGatewayText,\n\trememberGatewayRole,\n} from \"./presenter.ts\";\nimport { buildSessionKey } from \"./session-keys.ts\";\nimport { getSession as getStoredSession } from \"./store.ts\";\nimport { applySilenceFilter, StreamConsumer } from \"./stream.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\n/** Structural bridge shape (AgentBridge satisfies it; tests fake it). */\nexport interface BridgeLike {\n\trunTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks): Promise<string>;\n\tabort(key: string): Promise<void> | void;\n\treset(key: string): void | Promise<void>;\n\tgetStatus(key: string): BridgeSessionStatus;\n\tgetSession(key: string, create?: boolean): Promise<BridgeSession | null>;\n\tpeekSession?(key: string): BridgeSession | undefined;\n\tswitchSession(key: string, sessionFile: string): Promise<void>;\n\tundo(key: string): Promise<{ userText: string }>;\n\tredo(key: string): Promise<void>;\n}\n\nexport interface RouterDeps {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\tpairing: PairingStore;\n\tbridge: BridgeLike;\n\t/** Reload gateway.json on every inbound event so CLI config edits (e.g. pair approve) are picked up by the running daemon. */\n\treloadConfig?: boolean;\n\tremoteControls?: boolean;\n}\n\nexport interface Router {\n\thandleEvent(event: MessageEvent): Promise<void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction formatPairingCode(code: string): string {\n\treturn code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;\n}\n\nfunction mentionedBot(event: MessageEvent): boolean {\n\treturn event.metadata?.mentionedBot === true;\n}\n\nexport function createRouter(deps: RouterDeps): Router {\n\tconst { adapters, cfg: initialCfg, pairing, bridge, reloadConfig } = deps;\n\n\t// Headless gateway sessions still prompt for large subagent launches in yolo mode.\n\tregisterGatewayApprovalHandler();\n\n\t/** Reload gateway.json on every inbound event so CLI edits (e.g. pair approve) take effect immediately. */\n\tfunction freshCfg(): GatewayConfig {\n\t\tif (!reloadConfig) return initialCfg;\n\t\ttry {\n\t\t\tif (existsSync(gatewayConfigPath())) {\n\t\t\t\treturn loadGatewayConfig();\n\t\t\t}\n\t\t} catch {\n\t\t\t// fall through to initial config\n\t\t}\n\t\treturn initialCfg;\n\t}\n\n\tasync function sendError(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\tmessage: string,\n\t\tepoch: number,\n\t): Promise<void> {\n\t\tawait queueGatewayText(key, event.source, adapter, `⚠ ${oneLine(message)}`, {\n\t\t\tkind: \"notice\",\n\t\t\treplyTo: event.messageId,\n\t\t\tcfg,\n\t\t\tepoch,\n\t\t});\n\t}\n\n\t/** Step 1: group-chat early gating. Returns true when the event must be dropped. */\n\tfunction isGroupGated(event: MessageEvent, cfg: GatewayConfig): boolean {\n\t\tconst { source } = event;\n\t\tif (source.chatType === \"dm\") return false;\n\t\tconst platformCfg = platformConfigFor(cfg, source.platform);\n\t\tif (!platformCfg) return true;\n\t\tconst freeResponse = platformCfg.freeResponseChats.includes(source.chatId);\n\t\tif (\n\t\t\tplatformCfg.allowedChats.length > 0 &&\n\t\t\t!platformCfg.allowedChats.includes(source.chatId) &&\n\t\t\t!freeResponse &&\n\t\t\t!mentionedBot(event)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (platformCfg.requireMention && !freeResponse && !mentionedBot(event)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Step 2: authorization; handles the denied-DM pairing flow. Returns true when denied. */\n\tasync function isDenied(adapter: PlatformAdapter, event: MessageEvent, cfg: GatewayConfig): Promise<boolean> {\n\t\tif (isAuthorized(event.source, cfg, pairing)) return false;\n\t\tconst { source } = event;\n\t\tif (source.chatType !== \"dm\") return true; // denied group: silent\n\t\tif (cfg.unauthorizedDmBehavior !== \"pair\") return true;\n\t\tconst code = pairing.issueCode(source.platform, source.userId);\n\t\tif (code === null) return true; // rate-limited or pending list full: stay silent\n\t\tawait adapter.send(\n\t\t\tsource.chatId,\n\t\t\t`Your lunR pairing code: ${formatPairingCode(code)}. On the computer running lunr, approve access with: lunr gateway pair approve ${source.platform} ${formatPairingCode(code)}. Approval grants full gateway access, including local projects and saved sessions.`,\n\t\t\t{ replyTo: event.messageId, threadId: source.threadId },\n\t\t);\n\t\treturn true;\n\t}\n\n\tfunction workspaceReadiness(event: MessageEvent, key: string, cfg: GatewayConfig): string | undefined {\n\t\tif (getStoredSession(key) || conversationBinding(key)?.cwd) return undefined;\n\t\tconst root = cfg.defaultProject;\n\t\tif (!root) return \"No default project selected. Use /project to choose an approved folder.\";\n\t\ttry {\n\t\t\tconst cwd = resolveWithinRoots(root, cfg.projectRoots ?? []);\n\t\t\tbindConversation(key, event.source, { cwd, owner: event.source.userId });\n\t\t\treturn undefined;\n\t\t} catch {\n\t\t\treturn \"Default project is no longer an approved folder. Use /project to choose an approved folder.\";\n\t\t}\n\t}\n\n\t/** Step 3: command registry. Returns true when the event was consumed. */\n\tasync function handleSlash(adapter: PlatformAdapter, event: MessageEvent, key: string): Promise<boolean> {\n\t\tconst text = event.text.trim();\n\t\tif (!text.startsWith(\"/\")) return false;\n\t\tconst firstToken = text.split(/\\s+/, 1)[0].toLowerCase();\n\t\tconst commandWord = firstToken.split(\"@\")[0].slice(1);\n\t\tconst args = text.slice(firstToken.length).trim();\n\t\tif (deps.remoteControls) {\n\t\t\tconst { handleMobileCommand, cancelMobileTransfer } = await import(\"./mobile-commands.ts\");\n\t\t\tif ([\"stop\", \"stopall\", \"new\", \"cancel\"].includes(commandWord)) cancelMobileTransfer(key);\n\t\t\tif (commandWord === \"cancel\") {\n\t\t\t\tinvalidateGatewayDialogs(key);\n\t\t\t\tawait adapter.send(event.source.chatId, \"Cancelled the pending selection or transfer.\", {\n\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst consumed = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\t\thandleMobileCommand({ key, event, adapter, bridge, cfg: freshCfg() }, commandWord, args),\n\t\t\t);\n\t\t\tif (consumed) return true;\n\t\t\tif (!event.text.startsWith(\"/\")) return false;\n\t\t}\n\t\tif (commandWord === \"start\") {\n\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, freshCfg()) : undefined;\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst session = await bridge.getSession(key, true);\n\t\t\t\tconst models = await session?.modelRuntime.getAvailable();\n\t\t\t\tif (!models?.length)\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"No authenticated model available. Use /login on your computer, then try /start again.\",\n\t\t\t\t\t);\n\t\t\t\telse if (\n\t\t\t\t\t!session?.model ||\n\t\t\t\t\t!models.some((model) => model.id === session.model?.id && model.provider === session.model.provider)\n\t\t\t\t)\n\t\t\t\t\tawait sendCommandReply(adapter, event, \"No available model selected. Use /model here to choose one.\");\n\t\t\t\telse\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"Ready. Send a message to begin, or use /model to choose a model.\",\n\t\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait sendCommandReply(\n\t\t\t\t\tadapter,\n\t\t\t\t\tevent,\n\t\t\t\t\t\"Session setup failed. Run lunr gateway doctor on your computer, then try /start again.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (deps.remoteControls && commandWord === \"model\") {\n\t\t\tconst issue = workspaceReadiness(event, key, freshCfg());\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tconst cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));\n\t\tif (!cmd) {\n\t\t\treturn event.source.chatType !== \"dm\";\n\t\t}\n\t\tconst ctx = {\n\t\t\tevent,\n\t\t\tkey,\n\t\t\tadapter,\n\t\t\tbridge,\n\t\t\tcfg: freshCfg(),\n\t\t\targs,\n\t\t\treply: (message: string) => sendCommandReply(adapter, event, message),\n\t\t};\n\t\treturn runWithApprovalContext({ key, adapter, source: event.source }, () => runChatCommand(cmd, ctx));\n\t}\n\n\t/** A successful preview is finalized by edit; failed edits fall back to a durable full reply. */\n\tasync function deliver(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\ttext: string,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\topts: { reply: boolean; editMessageId?: string; previewTruncated?: boolean },\n\t): Promise<void> {\n\t\tconst filtered = applySilenceFilter(text);\n\t\tif (filtered === null) return;\n\t\tif (opts.editMessageId) {\n\t\t\tconst chunks = splitMessage(filtered, adapter.maxMessageLength);\n\t\t\tconst edit = await adapter\n\t\t\t\t.editMessage(event.source.chatId, opts.editMessageId, opts.previewTruncated ? chunks[0] : filtered)\n\t\t\t\t.catch(() => ({ success: false }));\n\t\t\tif (edit.success) {\n\t\t\t\tif (!opts.previewTruncated) return;\n\t\t\t\tawait queueGatewayText(key, event.source, adapter, chunks.slice(1).join(\"\"), {\n\t\t\t\t\tkind: \"result\",\n\t\t\t\t\tcfg,\n\t\t\t\t\tchunks: chunks.slice(1),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tawait queueGatewayText(key, event.source, adapter, filtered, {\n\t\t\tkind: \"result\",\n\t\t\treplyTo: opts.reply ? event.messageId : undefined,\n\t\t\tcfg,\n\t\t});\n\t}\n\n\tasync function runTurn(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t): Promise<void> {\n\t\tconst epoch = gatewayEpoch(key);\n\t\tconst streaming = cfg.streaming.enabled && typeof adapter.editMessage === \"function\";\n\t\tlet consumer: StreamConsumer | undefined;\n\t\tif (streaming) {\n\t\t\tconsumer = new StreamConsumer({\n\t\t\t\tsendInitial: async (text) => {\n\t\t\t\t\tconst result = await adapter.send(event.source.chatId, text, {\n\t\t\t\t\t\treplyTo: event.messageId,\n\t\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t\t});\n\t\t\t\t\treturn result.success ? (result.messageId ?? null) : null;\n\t\t\t\t},\n\t\t\t\tedit: async (messageId, text) => {\n\t\t\t\t\tconst result = await adapter.editMessage(event.source.chatId, messageId, text);\n\t\t\t\t\tif (!result.success) throw new Error(result.error ?? \"Preview edit failed\");\n\t\t\t\t},\n\t\t\t\tintervalMs: cfg.streaming.editIntervalMs,\n\t\t\t\tthreshold: cfg.streaming.bufferThreshold,\n\t\t\t\tmaxPreview: adapter.maxMessageLength,\n\t\t\t});\n\t\t}\n\n\t\tconst callbacks: TurnCallbacks = {\n\t\t\tonDelta: consumer ? (delta) => consumer.push(delta) : undefined,\n\t\t\tonFollowUpResult: (text) => {\n\t\t\t\tvoid deliver(adapter, event, text, key, cfg, { reply: false }).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] follow-up delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t\tonError: (message) => {\n\t\t\t\tvoid sendError(adapter, event, key, cfg, message, epoch).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] error delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst result = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\tbridge.runTurn(key, event, callbacks),\n\t\t);\n\t\tif (result === QUEUED) return; // queued behind a running turn: no reply\n\t\tif (consumer) await consumer.finalize();\n\t\tawait deliver(adapter, event, result, key, cfg, {\n\t\t\treply: !consumer,\n\t\t\teditMessageId: consumer?.sentMessageId ?? undefined,\n\t\t\tpreviewTruncated: consumer?.truncated ?? false,\n\t\t});\n\t}\n\n\treturn {\n\t\tasync handleEvent(event: MessageEvent): Promise<void> {\n\t\t\tconst adapter = adapters.get(event.source.platform);\n\t\t\tif (!adapter) return;\n\t\t\tconst cfg = freshCfg();\n\t\t\tconst key = buildSessionKey(event.source, { groupSessionsPerUser: cfg.groupSessionsPerUser });\n\t\t\tconst epoch = gatewayEpoch(key);\n\t\t\ttry {\n\t\t\t\tif (isGroupGated(event, cfg)) return;\n\t\t\t\trememberGatewayRole(key, event.source);\n\t\t\t\tif (await isDenied(adapter, event, cfg)) return;\n\t\t\t\tif (deps.remoteControls) {\n\t\t\t\t\tconst binding = conversationBinding(key);\n\t\t\t\t\tif (binding?.owner) {\n\t\t\t\t\t\trequireAuthorized(event.source, cfg);\n\t\t\t\t\t\tif (binding.owner !== event.source.userId)\n\t\t\t\t\t\t\tthrow new Error(\"This session belongs to a different user.\");\n\t\t\t\t\t}\n\t\t\t\t\tbindConversation(key, event.source);\n\t\t\t\t\tif (acceptGatewayInput(key, event)) return;\n\t\t\t\t}\n\t\t\t\tif (await handleSlash(adapter, event, key)) return;\n\t\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, cfg) : undefined;\n\t\t\t\tif (issue) {\n\t\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait adapter.sendTyping(event.source.chatId, event.source.threadId).catch(() => {});\n\t\t\t\tawait runTurn(adapter, event, key, cfg);\n\t\t\t} catch (err) {\n\t\t\t\tawait sendError(adapter, event, key, cfg, err instanceof Error ? err.message : String(err), epoch).catch(\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/gateway/router.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway router.\n *\n * Every inbound MessageEvent flows through handleEvent:\n * 1. group gating (allowedChats / freeResponseChats / requireMention,\n * with adapter-supplied metadata.mentionedBot)\n * 2. authorization (authz.ts); denied DMs get a pairing code when the\n * behavior is \"pair\" and the user isn't rate-limited\n * 3. command registry (/new, /undo, /redo, /model, /sessions, /title,\n * /context, /compact, /thinking, /stop, /status, /help, /whoami)\n * 4. normal path: bridge.runTurn with a StreamConsumer when streaming is\n * enabled and the adapter supports edit; the final text is\n * silence-filtered and folded into a successful streaming preview edit.\n * Failed final edits and ordinary answers enter the durable outbox, which\n * acknowledges chunks in order and retries by destination.\n * Errors surface as a compact \"⚠ <one-line>\" — never a stack trace.\n */\n\nimport { existsSync } from \"node:fs\";\nimport {\n\ttype BridgeSession,\n\ttype BridgeSessionStatus,\n\tQUEUED,\n\ttype ResetOptions,\n\ttype TurnCallbacks,\n} from \"./agent-bridge.ts\";\nimport { registerGatewayApprovalHandler, runWithApprovalContext } from \"./approval.ts\";\nimport { isAuthorized, requireAuthorized } from \"./authz.ts\";\nimport { CHAT_COMMANDS, runChatCommand, sendCommandReply } from \"./commands.ts\";\nimport { type GatewayConfig, gatewayConfigPath, loadGatewayConfig, platformConfigFor } from \"./config.ts\";\nimport { bindConversation, conversationBinding } from \"./conversations.ts\";\nimport { resolveWithinRoots } from \"./mobile-commands.ts\";\nimport type { PairingStore } from \"./pairing.ts\";\nimport {\n\tacceptGatewayInput,\n\tcanDeliverGatewayText,\n\tgatewayEpoch,\n\tinvalidateGatewayDialogs,\n\tqueueGatewayText,\n\trememberGatewayRole,\n} from \"./presenter.ts\";\nimport { buildSessionKey } from \"./session-keys.ts\";\nimport { getSession as getStoredSession } from \"./store.ts\";\nimport { applySilenceFilter, StreamConsumer } from \"./stream.ts\";\nimport { splitMessage } from \"./text.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\n/** Structural bridge shape (AgentBridge satisfies it; tests fake it). */\nexport interface BridgeLike {\n\trunTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks): Promise<string>;\n\tabort(key: string): Promise<void> | void;\n\treset(key: string, options?: ResetOptions): void | Promise<void>;\n\tgetStatus(key: string): BridgeSessionStatus;\n\tgetSession(key: string, create?: boolean): Promise<BridgeSession | null>;\n\tpeekSession?(key: string): BridgeSession | undefined;\n\tswitchSession(key: string, sessionFile: string): Promise<void>;\n\tundo(key: string): Promise<{ userText: string }>;\n\tredo(key: string): Promise<void>;\n}\n\nexport interface RouterDeps {\n\tadapters: Map<string, PlatformAdapter>;\n\tcfg: GatewayConfig;\n\tpairing: PairingStore;\n\tbridge: BridgeLike;\n\t/** Reload gateway.json on every inbound event so CLI config edits (e.g. pair approve) are picked up by the running daemon. */\n\treloadConfig?: boolean;\n\tremoteControls?: boolean;\n}\n\nexport interface Router {\n\thandleEvent(event: MessageEvent): Promise<void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction formatPairingCode(code: string): string {\n\treturn code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;\n}\n\nfunction mentionedBot(event: MessageEvent): boolean {\n\treturn event.metadata?.mentionedBot === true;\n}\n\nexport function createRouter(deps: RouterDeps): Router {\n\tconst { adapters, cfg: initialCfg, pairing, bridge, reloadConfig } = deps;\n\n\t// Headless gateway sessions still prompt for large subagent launches in yolo mode.\n\tregisterGatewayApprovalHandler();\n\n\t/** Reload gateway.json on every inbound event so CLI edits (e.g. pair approve) take effect immediately. */\n\tfunction freshCfg(): GatewayConfig {\n\t\tif (!reloadConfig) return initialCfg;\n\t\ttry {\n\t\t\tif (existsSync(gatewayConfigPath())) {\n\t\t\t\treturn loadGatewayConfig();\n\t\t\t}\n\t\t} catch {\n\t\t\t// fall through to initial config\n\t\t}\n\t\treturn initialCfg;\n\t}\n\n\tasync function sendError(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\tmessage: string,\n\t\tepoch: number,\n\t): Promise<void> {\n\t\tawait queueGatewayText(key, event.source, adapter, `⚠ ${oneLine(message)}`, {\n\t\t\tkind: \"notice\",\n\t\t\treplyTo: event.messageId,\n\t\t\tcfg,\n\t\t\tepoch,\n\t\t});\n\t}\n\n\t/** Step 1: group-chat early gating. Returns true when the event must be dropped. */\n\tfunction isGroupGated(event: MessageEvent, cfg: GatewayConfig): boolean {\n\t\tconst { source } = event;\n\t\tif (source.chatType === \"dm\") return false;\n\t\tconst platformCfg = platformConfigFor(cfg, source.platform);\n\t\tif (!platformCfg) return true;\n\t\tconst freeResponse = platformCfg.freeResponseChats.includes(source.chatId);\n\t\tif (\n\t\t\tplatformCfg.allowedChats.length > 0 &&\n\t\t\t!platformCfg.allowedChats.includes(source.chatId) &&\n\t\t\t!freeResponse &&\n\t\t\t!mentionedBot(event)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (platformCfg.requireMention && !freeResponse && !mentionedBot(event)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Step 2: authorization; handles the denied-DM pairing flow. Returns true when denied. */\n\tasync function isDenied(adapter: PlatformAdapter, event: MessageEvent, cfg: GatewayConfig): Promise<boolean> {\n\t\tif (isAuthorized(event.source, cfg, pairing)) return false;\n\t\tconst { source } = event;\n\t\tif (source.chatType !== \"dm\") return true; // denied group: silent\n\t\tif (cfg.unauthorizedDmBehavior !== \"pair\") return true;\n\t\tconst code = pairing.issueCode(source.platform, source.userId);\n\t\tif (code === null) return true; // rate-limited or pending list full: stay silent\n\t\tawait adapter.send(\n\t\t\tsource.chatId,\n\t\t\t`Your lunR pairing code: ${formatPairingCode(code)}. On the computer running lunr, approve access with: lunr gateway pair approve ${source.platform} ${formatPairingCode(code)}. Approval grants full gateway access, including local projects and saved sessions.`,\n\t\t\t{ replyTo: event.messageId, threadId: source.threadId },\n\t\t);\n\t\treturn true;\n\t}\n\n\tfunction workspaceReadiness(event: MessageEvent, key: string, cfg: GatewayConfig): string | undefined {\n\t\tif (getStoredSession(key) || conversationBinding(key)?.cwd) return undefined;\n\t\tconst root = cfg.defaultProject;\n\t\tif (!root) return \"No default project selected. Use /project to choose an approved folder.\";\n\t\ttry {\n\t\t\tconst cwd = resolveWithinRoots(root, cfg.projectRoots ?? []);\n\t\t\tbindConversation(key, event.source, { cwd, owner: event.source.userId });\n\t\t\treturn undefined;\n\t\t} catch {\n\t\t\treturn \"Default project is no longer an approved folder. Use /project to choose an approved folder.\";\n\t\t}\n\t}\n\n\t/** Step 3: command registry. Returns true when the event was consumed. */\n\tasync function handleSlash(adapter: PlatformAdapter, event: MessageEvent, key: string): Promise<boolean> {\n\t\tconst text = event.text.trim();\n\t\tif (!text.startsWith(\"/\")) return false;\n\t\tconst firstToken = text.split(/\\s+/, 1)[0].toLowerCase();\n\t\tconst commandWord = firstToken.split(\"@\")[0].slice(1);\n\t\tconst args = text.slice(firstToken.length).trim();\n\t\tif (deps.remoteControls) {\n\t\t\tconst { handleMobileCommand, cancelMobileTransfer } = await import(\"./mobile-commands.ts\");\n\t\t\tif ([\"stop\", \"stopall\", \"new\", \"cancel\"].includes(commandWord)) cancelMobileTransfer(key);\n\t\t\tif (commandWord === \"cancel\") {\n\t\t\t\tinvalidateGatewayDialogs(key);\n\t\t\t\tawait adapter.send(event.source.chatId, \"Cancelled the pending selection or transfer.\", {\n\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst consumed = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\t\thandleMobileCommand({ key, event, adapter, bridge, cfg: freshCfg() }, commandWord, args),\n\t\t\t);\n\t\t\tif (consumed) return true;\n\t\t\tif (!event.text.startsWith(\"/\")) return false;\n\t\t}\n\t\tif (commandWord === \"start\") {\n\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, freshCfg()) : undefined;\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst session = await bridge.getSession(key, true);\n\t\t\t\tconst models = await session?.modelRuntime.getAvailable();\n\t\t\t\tif (!models?.length)\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"No authenticated model available. Use /login on your computer, then try /start again.\",\n\t\t\t\t\t);\n\t\t\t\telse if (\n\t\t\t\t\t!session?.model ||\n\t\t\t\t\t!models.some((model) => model.id === session.model?.id && model.provider === session.model.provider)\n\t\t\t\t)\n\t\t\t\t\tawait sendCommandReply(adapter, event, \"No available model selected. Use /model here to choose one.\");\n\t\t\t\telse\n\t\t\t\t\tawait sendCommandReply(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\"Ready. Send a message to begin, or use /model to choose a model.\",\n\t\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait sendCommandReply(\n\t\t\t\t\tadapter,\n\t\t\t\t\tevent,\n\t\t\t\t\t\"Session setup failed. Run lunr gateway doctor on your computer, then try /start again.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (deps.remoteControls && commandWord === \"model\") {\n\t\t\tconst issue = workspaceReadiness(event, key, freshCfg());\n\t\t\tif (issue) {\n\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tconst cmd = CHAT_COMMANDS.find((c) => c.name === commandWord || c.aliases?.includes(commandWord));\n\t\tif (!cmd) {\n\t\t\treturn event.source.chatType !== \"dm\";\n\t\t}\n\t\tconst ctx = {\n\t\t\tevent,\n\t\t\tkey,\n\t\t\tadapter,\n\t\t\tbridge,\n\t\t\tcfg: freshCfg(),\n\t\t\targs,\n\t\t\treply: (message: string) => sendCommandReply(adapter, event, message),\n\t\t};\n\t\treturn runWithApprovalContext({ key, adapter, source: event.source }, () => runChatCommand(cmd, ctx));\n\t}\n\n\t/** A successful preview is finalized by edit; failed edits fall back to a durable full reply. */\n\tasync function deliver(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\ttext: string,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t\topts: { reply: boolean; editMessageId?: string; previewTruncated?: boolean; valid?: () => boolean },\n\t): Promise<void> {\n\t\tif (opts.valid && !opts.valid()) return;\n\t\tif (!canDeliverGatewayText(key, event.source, cfg, pairing)) return;\n\t\tconst filtered = applySilenceFilter(text);\n\t\tif (filtered === null) return;\n\t\tif (opts.editMessageId) {\n\t\t\tconst chunks = splitMessage(filtered, adapter.maxMessageLength);\n\t\t\tconst edit = await adapter\n\t\t\t\t.editMessage(event.source.chatId, opts.editMessageId, opts.previewTruncated ? chunks[0] : filtered)\n\t\t\t\t.catch(() => ({ success: false }));\n\t\t\tif (edit.success) {\n\t\t\t\tif (!opts.previewTruncated) return;\n\t\t\t\tawait queueGatewayText(key, event.source, adapter, chunks.slice(1).join(\"\"), {\n\t\t\t\t\tkind: \"result\",\n\t\t\t\t\tcfg,\n\t\t\t\t\tchunks: chunks.slice(1),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tawait queueGatewayText(key, event.source, adapter, filtered, {\n\t\t\tkind: \"result\",\n\t\t\treplyTo: opts.reply ? event.messageId : undefined,\n\t\t\tcfg,\n\t\t});\n\t}\n\n\tasync function runTurn(\n\t\tadapter: PlatformAdapter,\n\t\tevent: MessageEvent,\n\t\tkey: string,\n\t\tcfg: GatewayConfig,\n\t): Promise<void> {\n\t\tconst epoch = gatewayEpoch(key);\n\t\tconst streaming = cfg.streaming.enabled && typeof adapter.editMessage === \"function\";\n\t\tlet denied = false;\n\t\tconst valid = () => {\n\t\t\tif (!denied) denied = !canDeliverGatewayText(key, event.source, cfg, pairing);\n\t\t\treturn !denied;\n\t\t};\n\t\tlet consumer: StreamConsumer | undefined;\n\t\tif (streaming) {\n\t\t\tconsumer = new StreamConsumer({\n\t\t\t\tsendInitial: async (text) => {\n\t\t\t\t\tif (!valid() || gatewayEpoch(key) !== epoch) return null;\n\t\t\t\t\tconst result = await adapter.send(event.source.chatId, text, {\n\t\t\t\t\t\treplyTo: event.messageId,\n\t\t\t\t\t\tthreadId: event.source.threadId,\n\t\t\t\t\t});\n\t\t\t\t\treturn result.success ? (result.messageId ?? null) : null;\n\t\t\t\t},\n\t\t\t\tedit: async (messageId, text) => {\n\t\t\t\t\tif (!valid() || gatewayEpoch(key) !== epoch) return;\n\t\t\t\t\tconst result = await adapter.editMessage(event.source.chatId, messageId, text);\n\t\t\t\t\tif (!result.success) throw new Error(result.error ?? \"Preview edit failed\");\n\t\t\t\t},\n\t\t\t\tintervalMs: cfg.streaming.editIntervalMs,\n\t\t\t\tthreshold: cfg.streaming.bufferThreshold,\n\t\t\t\tmaxPreview: adapter.maxMessageLength,\n\t\t\t});\n\t\t}\n\n\t\tconst callbacks: TurnCallbacks = {\n\t\t\tonDelta: consumer ? (delta) => consumer.push(delta) : undefined,\n\t\t\tonFollowUpResult: (text) => {\n\t\t\t\tvoid deliver(adapter, event, text, key, cfg, { reply: false }).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] follow-up delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t\tonError: (message) => {\n\t\t\t\tvoid sendError(adapter, event, key, cfg, message, epoch).catch((error) =>\n\t\t\t\t\tconsole.error(\"[gateway] error delivery failed:\", error),\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst result = await runWithApprovalContext({ key, adapter, source: event.source }, () =>\n\t\t\tbridge.runTurn(key, event, callbacks),\n\t\t);\n\t\tif (result === QUEUED) return; // queued behind a running turn: no reply\n\t\tif (consumer) await consumer.finalize();\n\t\tawait deliver(adapter, event, result, key, cfg, {\n\t\t\treply: !consumer,\n\t\t\teditMessageId: consumer?.sentMessageId ?? undefined,\n\t\t\tpreviewTruncated: consumer?.truncated ?? false,\n\t\t\tvalid,\n\t\t});\n\t}\n\n\treturn {\n\t\tasync handleEvent(event: MessageEvent): Promise<void> {\n\t\t\tconst adapter = adapters.get(event.source.platform);\n\t\t\tif (!adapter) return;\n\t\t\tconst cfg = freshCfg();\n\t\t\tconst key = buildSessionKey(event.source, { groupSessionsPerUser: cfg.groupSessionsPerUser });\n\t\t\tconst epoch = gatewayEpoch(key);\n\t\t\ttry {\n\t\t\t\tif (isGroupGated(event, cfg)) return;\n\t\t\t\trememberGatewayRole(key, event.source);\n\t\t\t\tif (await isDenied(adapter, event, cfg)) return;\n\t\t\t\tif (deps.remoteControls) {\n\t\t\t\t\tconst binding = conversationBinding(key);\n\t\t\t\t\tif (binding?.owner) {\n\t\t\t\t\t\trequireAuthorized(event.source, cfg);\n\t\t\t\t\t\tif (binding.owner !== event.source.userId)\n\t\t\t\t\t\t\tthrow new Error(\"This session belongs to a different user.\");\n\t\t\t\t\t}\n\t\t\t\t\tbindConversation(key, event.source);\n\t\t\t\t\tif (acceptGatewayInput(key, event)) return;\n\t\t\t\t}\n\t\t\t\tif (await handleSlash(adapter, event, key)) return;\n\t\t\t\tconst issue = deps.remoteControls ? workspaceReadiness(event, key, cfg) : undefined;\n\t\t\t\tif (issue) {\n\t\t\t\t\tawait sendCommandReply(adapter, event, issue);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait adapter.sendTyping(event.source.chatId, event.source.threadId).catch(() => {});\n\t\t\t\tawait runTurn(adapter, event, key, cfg);\n\t\t\t} catch (err) {\n\t\t\t\tawait sendError(adapter, event, key, cfg, err instanceof Error ? err.message : String(err), epoch).catch(\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
@@ -24,6 +24,8 @@ export declare function claimGateway(onStop: () => void): {
|
|
|
24
24
|
};
|
|
25
25
|
export declare function startGatewayService(): Promise<string>;
|
|
26
26
|
export declare function stopGatewayService(): Promise<string>;
|
|
27
|
+
/** Remove only this profile's gateway and startup entry, before uninstall deletes control data. */
|
|
28
|
+
export declare function teardownGatewayService(): Promise<void>;
|
|
27
29
|
export interface StartupSpec {
|
|
28
30
|
path: string;
|
|
29
31
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gateway/service.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AACnD,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,WAAW,CAAC;CACrB;AACD,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,cAA+C,CAAC;AAC7E,eAAO,MAAM,cAAc,cAAgD,CAAC;AAK5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAK7D;AAUD,wBAAgB,mBAAmB,IAAI,sBAAsB,CAM5D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQlD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,SAAe,GAAG,oBAAoB,GAAG,SAAS,CA4BvF;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG;IACjD,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3F,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB,CA8EA;AAUD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAsD3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS1D;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAWD,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,EACjC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnG,WAAW,CA+Db;AASD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDvE;AAuBD,wBAAgB,iBAAiB,IAAI,MAAM,CAY1C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAUvC","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir, userInfo } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport type StartupMode = \"off\" | \"login\" | \"boot\";\nexport interface GatewayServiceSettings {\n\tstartup: StartupMode;\n}\nexport interface GatewayRuntimeStatus {\n\tversion: 1;\n\tinstance: string;\n\tpid: number;\n\tstartedAt: string;\n\tupdatedAt: string;\n\tstate: \"starting\" | \"ready\" | \"stopping\";\n\tplatforms: Record<string, string>;\n}\nconst servicePrefix = \"lunr-gateway\";\nexport const serviceDirectory = () => join(getAgentDir(), \"gateway-service\");\nexport const gatewayLogPath = () => join(serviceDirectory(), \"gateway.log\");\nconst statusPath = () => join(serviceDirectory(), \"status.json\");\nconst controlPath = () => join(serviceDirectory(), \"control.json\");\nconst settingsPath = () => join(serviceDirectory(), \"service.json\");\n\nexport function atomicJson(path: string, value: unknown): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\tconst temp = `${path}.${randomUUID()}.tmp`;\n\twriteFileSync(temp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n\trenameSync(temp, path);\n}\n\nfunction readJson(path: string): unknown {\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function loadServiceSettings(): GatewayServiceSettings {\n\tconst raw = readJson(settingsPath());\n\tif (raw && typeof raw === \"object\" && \"startup\" in raw && (raw.startup === \"login\" || raw.startup === \"boot\")) {\n\t\treturn { startup: raw.startup };\n\t}\n\treturn { startup: \"off\" };\n}\n\nexport function processExists(pid: number): boolean {\n\tif (!Number.isSafeInteger(pid) || pid <= 0) return false;\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn !(error && typeof error === \"object\" && \"code\" in error && error.code === \"ESRCH\");\n\t}\n}\n\nexport function readGatewayStatus(file = statusPath()): GatewayRuntimeStatus | undefined {\n\tconst raw = readJson(file);\n\tif (\n\t\t!raw ||\n\t\ttypeof raw !== \"object\" ||\n\t\t!(\"version\" in raw) ||\n\t\traw.version !== 1 ||\n\t\t!(\"pid\" in raw) ||\n\t\ttypeof raw.pid !== \"number\"\n\t)\n\t\treturn undefined;\n\tif (\n\t\t!(\"instance\" in raw) ||\n\t\ttypeof raw.instance !== \"string\" ||\n\t\t!(\"updatedAt\" in raw) ||\n\t\ttypeof raw.updatedAt !== \"string\"\n\t)\n\t\treturn undefined;\n\tif (!(\"state\" in raw) || ![\"starting\", \"ready\", \"stopping\"].includes(String(raw.state))) return undefined;\n\tif (\n\t\t!(\"platforms\" in raw) ||\n\t\t!raw.platforms ||\n\t\ttypeof raw.platforms !== \"object\" ||\n\t\t!(\"startedAt\" in raw) ||\n\t\ttypeof raw.startedAt !== \"string\"\n\t)\n\t\treturn undefined;\n\treturn raw as GatewayRuntimeStatus;\n}\n\nexport function gatewayIsRunning(): boolean {\n\tconst status = readGatewayStatus();\n\treturn !!status && processExists(status.pid);\n}\n\nexport function claimGateway(onStop: () => void): {\n\tupdate: (platforms: Record<string, string>, state?: GatewayRuntimeStatus[\"state\"]) => void;\n\trelease: () => void;\n} {\n\tconst dir = serviceDirectory();\n\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\tconst lock = join(dir, \"lock\");\n\ttry {\n\t\tmkdirSync(lock);\n\t} catch (error) {\n\t\tif (!error || typeof error !== \"object\" || !(\"code\" in error) || error.code !== \"EEXIST\") throw error;\n\t\tconst previous = readGatewayStatus(join(lock, \"owner.json\"));\n\t\tif (!previous || processExists(previous.pid))\n\t\t\tthrow new Error(\"Gateway already running or startup lock is unresolved. Run lunr gateway doctor.\");\n\t\t// Recovery is serialized; a late recoverer must not remove a new owner's lock.\n\t\tconst recovery = join(dir, \"recovery\");\n\t\tmkdirSync(recovery);\n\t\ttry {\n\t\t\tconst current = readGatewayStatus(join(lock, \"owner.json\"));\n\t\t\tif (!current || current.instance !== previous.instance || processExists(current.pid))\n\t\t\t\tthrow new Error(\"Gateway ownership changed. Try again.\");\n\t\t\trmSync(lock, { recursive: true });\n\t\t\tmkdirSync(lock);\n\t\t} finally {\n\t\t\trmSync(recovery, { recursive: true, force: true });\n\t\t}\n\t}\n\tconst status: GatewayRuntimeStatus = {\n\t\tversion: 1,\n\t\tinstance: randomUUID(),\n\t\tpid: process.pid,\n\t\tstartedAt: new Date().toISOString(),\n\t\tupdatedAt: new Date().toISOString(),\n\t\tstate: \"starting\",\n\t\tplatforms: {},\n\t};\n\tatomicJson(join(lock, \"owner.json\"), status);\n\tconst save = () => {\n\t\tstatus.updatedAt = new Date().toISOString();\n\t\tatomicJson(statusPath(), status);\n\t};\n\tsave();\n\tlet stopping = false;\n\tconst timer = setInterval(() => {\n\t\tsave();\n\t\tconst control = readJson(controlPath());\n\t\tif (\n\t\t\t!stopping &&\n\t\t\tcontrol &&\n\t\t\ttypeof control === \"object\" &&\n\t\t\t\"instance\" in control &&\n\t\t\tcontrol.instance === status.instance &&\n\t\t\t\"action\" in control &&\n\t\t\tcontrol.action === \"stop\"\n\t\t) {\n\t\t\tstopping = true;\n\t\t\tstatus.state = \"stopping\";\n\t\t\tonStop();\n\t\t}\n\t}, 500);\n\treturn {\n\t\tupdate(platforms, state = \"ready\") {\n\t\t\tif (stopping && state !== \"stopping\") {\n\t\t\t\tstopping = false;\n\t\t\t\tconst control = readJson(controlPath());\n\t\t\t\tif (control && typeof control === \"object\" && \"instance\" in control && control.instance === status.instance)\n\t\t\t\t\trmSync(controlPath(), { force: true });\n\t\t\t}\n\t\t\tstatus.platforms = { ...platforms };\n\t\t\tstatus.state = state;\n\t\t\tsave();\n\t\t},\n\t\trelease() {\n\t\t\tclearInterval(timer);\n\t\t\tif (readGatewayStatus()?.instance === status.instance) {\n\t\t\t\trmSync(statusPath(), { force: true });\n\t\t\t\trmSync(controlPath(), { force: true });\n\t\t\t\trmSync(lock, { recursive: true, force: true });\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction cliPath(): string {\n\tconst candidate = process.argv[1];\n\tif (candidate && existsSync(candidate)) return resolve(candidate);\n\treturn fileURLToPath(new URL(\"../cli.js\", import.meta.url));\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport async function startGatewayService(): Promise<string> {\n\tif (gatewayIsRunning()) {\n\t\tif (readGatewayStatus()?.state === \"ready\") return \"Gateway is already running.\";\n\t\tthrow new Error(\n\t\t\t`Gateway process is running but no bot is connected. ${serviceStatusText()}\\nInspect ${gatewayLogPath()}`,\n\t\t);\n\t}\n\tmkdirSync(serviceDirectory(), { recursive: true, mode: 0o700 });\n\tconst startup = loadServiceSettings().startup;\n\tif (startup !== \"off\") {\n\t\tconst user = userInfo();\n\t\tconst spec = startupSpec(process.platform, startup, {\n\t\t\thome: homedir(),\n\t\t\tagentDir: getAgentDir(),\n\t\t\tnode: process.execPath,\n\t\t\tcli: cliPath(),\n\t\t\tusername: user.username,\n\t\t\tuid: user.uid,\n\t\t});\n\t\texecute(spec.start);\n\t\tfor (let i = 0; i < 100; i++) {\n\t\t\tif (readGatewayStatus()?.state === \"ready\" && gatewayIsRunning()) return \"Gateway started.\";\n\t\t\tawait sleep(200);\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Startup service did not connect within 20 seconds. ${serviceStatusText()}\\nRun lunr gateway doctor and inspect ${gatewayLogPath()}`,\n\t\t);\n\t}\n\tconst { openSync, closeSync } = await import(\"node:fs\");\n\tconst log = openSync(gatewayLogPath(), \"a\", 0o600);\n\tconst child = spawn(process.execPath, [cliPath(), \"gateway\", \"run\"], {\n\t\tcwd: homedir(),\n\t\tdetached: true,\n\t\twindowsHide: true,\n\t\tstdio: [\"ignore\", log, log],\n\t\tenv: { ...process.env, PI_CODING_AGENT_DIR: getAgentDir() },\n\t});\n\tcloseSync(log);\n\tlet spawnError: Error | undefined;\n\tchild.on(\"error\", (error) => {\n\t\tspawnError = error;\n\t});\n\tchild.unref();\n\tfor (let i = 0; i < 100; i++) {\n\t\tif (spawnError) throw spawnError;\n\t\tconst status = readGatewayStatus();\n\t\tif (status && status.pid === child.pid && status.state === \"ready\") return \"Gateway started.\";\n\t\tif (child.exitCode !== null)\n\t\t\tthrow new Error(`Gateway exited with code ${child.exitCode}. See ${gatewayLogPath()}`);\n\t\tawait sleep(200);\n\t}\n\tthrow new Error(\n\t\t`Gateway did not connect within 20 seconds. ${serviceStatusText()}\\nCheck lunr gateway status and ${gatewayLogPath()}`,\n\t);\n}\n\nexport async function stopGatewayService(): Promise<string> {\n\tconst status = readGatewayStatus();\n\tif (!status || !processExists(status.pid)) return \"Gateway is not running.\";\n\tatomicJson(controlPath(), { instance: status.instance, action: \"stop\" });\n\tfor (let i = 0; i < 150; i++) {\n\t\tif (readGatewayStatus()?.instance !== status.instance || !processExists(status.pid)) return \"Gateway stopped.\";\n\t\tawait sleep(200);\n\t}\n\tthrow new Error(\"Gateway is still stopping after 30 seconds. Inspect the log; no unrelated process was terminated.\");\n}\n\nexport interface StartupSpec {\n\tpath: string;\n\tcontent: string;\n\tinstall: string[][];\n\tremove: string[][];\n\tverify: string[];\n\tstart: string[];\n}\nfunction xml(value: string): string {\n\treturn value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\");\n}\nfunction systemdArg(value: string): string {\n\treturn `\"${value.replaceAll(\"%\", \"%%\").replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"')}\"`;\n}\nfunction ps(value: string): string {\n\treturn `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nexport function startupSpec(\n\tplatform: NodeJS.Platform,\n\tmode: Exclude<StartupMode, \"off\">,\n\toptions: { home: string; agentDir: string; node: string; cli: string; username: string; uid: number },\n): StartupSpec {\n\tconst { home, agentDir, node, cli, username, uid } = options;\n\tif ([home, agentDir, node, cli, username].some((value) => /[\\r\\n\\0]/.test(value)))\n\t\tthrow new Error(\"Startup paths and account names must not contain control characters.\");\n\tconst suffix = createHash(\"sha256\").update(resolve(agentDir)).digest(\"hex\").slice(0, 10);\n\tconst serviceName = `${servicePrefix}-${suffix}`;\n\tconst log = join(agentDir, \"gateway-service\", \"gateway.log\");\n\tif (platform === \"linux\") {\n\t\tconst path = join(home, \".config\", \"systemd\", \"user\", `${serviceName}.service`);\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent: `[Unit]\\nDescription=lunR chat gateway\\nAfter=network-online.target\\n\\n[Service]\\nType=simple\\nExecStart=${systemdArg(node)} ${systemdArg(cli)} gateway run\\nWorkingDirectory=${systemdArg(home)}\\nEnvironment=${systemdArg(`PI_CODING_AGENT_DIR=${agentDir}`)}\\nRestart=on-failure\\nRestartSec=5\\nTimeoutStopSec=30\\nStandardOutput=append:${log.replaceAll(\"%\", \"%%\")}\\nStandardError=append:${log.replaceAll(\"%\", \"%%\")}\\nUMask=0077\\n\\n[Install]\\nWantedBy=default.target\\n`,\n\t\t\tinstall: [\n\t\t\t\t...(mode === \"boot\" ? [[\"loginctl\", \"enable-linger\", username]] : []),\n\t\t\t\t[\"systemctl\", \"--user\", \"daemon-reload\"],\n\t\t\t\t[\"systemctl\", \"--user\", \"enable\", `${serviceName}.service`],\n\t\t\t],\n\t\t\tremove: [[\"systemctl\", \"--user\", \"disable\", `${serviceName}.service`]],\n\t\t\tverify: [\"systemctl\", \"--user\", \"is-enabled\", `${serviceName}.service`],\n\t\t\tstart: [\"systemctl\", \"--user\", \"start\", `${serviceName}.service`],\n\t\t};\n\t}\n\tif (platform === \"darwin\") {\n\t\tconst label = `dev.lunr.gateway.${suffix}`;\n\t\tconst path =\n\t\t\tmode === \"boot\"\n\t\t\t\t? `/Library/LaunchDaemons/${label}.plist`\n\t\t\t\t: join(home, \"Library\", \"LaunchAgents\", `${label}.plist`);\n\t\tconst domain = mode === \"boot\" ? \"system\" : `gui/${uid}`;\n\t\tconst command = (args: string[]) => (mode === \"boot\" ? [\"sudo\", \"launchctl\", ...args] : [\"launchctl\", ...args]);\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\\n<plist version=\"1.0\"><dict><key>Label</key><string>${label}</string><key>ProgramArguments</key><array>${[node, cli, \"gateway\", \"run\"].map((v) => `<string>${xml(v)}</string>`).join(\"\")}</array><key>WorkingDirectory</key><string>${xml(home)}</string>${mode === \"boot\" ? `<key>UserName</key><string>${xml(username)}</string>` : \"\"}<key>EnvironmentVariables</key><dict><key>PI_CODING_AGENT_DIR</key><string>${xml(agentDir)}</string><key>HOME</key><string>${xml(home)}</string></dict><key>RunAtLoad</key><true/><key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict><key>StandardOutPath</key><string>${xml(log)}</string><key>StandardErrorPath</key><string>${xml(log)}</string></dict></plist>\\n`,\n\t\t\tinstall: [command([\"bootstrap\", domain, path])],\n\t\t\tremove: [command([\"bootout\", `${domain}/${label}`])],\n\t\t\tverify: command([\"print\", `${domain}/${label}`]),\n\t\t\tstart: command([\"kickstart\", `${domain}/${label}`]),\n\t\t};\n\t}\n\tif (platform === \"win32\") {\n\t\tconst path = join(agentDir, \"gateway-service\", \"startup.ps1\");\n\t\tconst content = `$env:PI_CODING_AGENT_DIR = ${ps(agentDir)}\\nSet-Location -LiteralPath ${ps(home)}\\n& ${ps(node)} ${ps(cli)} gateway run *>> ${ps(log)}\\nexit $LASTEXITCODE\\n`;\n\t\tconst action = `New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ${ps(`-NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"${path}\"`)}`;\n\t\tconst trigger =\n\t\t\tmode === \"boot\"\n\t\t\t\t? \"New-ScheduledTaskTrigger -AtStartup\"\n\t\t\t\t: `New-ScheduledTaskTrigger -AtLogOn -User ${ps(username)}`;\n\t\tconst register = `$ErrorActionPreference='Stop'; $a=${action}; $t=${trigger}; $s=New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; ${mode === \"boot\" ? `$c=Get-Credential -UserName ${ps(username)} -Message 'Windows needs your account credentials to run lunR before login'; if(!$c){throw 'Cancelled'}; Register-ScheduledTask -TaskName '${serviceName}' -Action $a -Trigger $t -Settings $s -User $c.UserName -Password $c.GetNetworkCredential().Password -Force | Out-Null` : `Register-ScheduledTask -TaskName '${serviceName}' -Action $a -Trigger $t -Settings $s -User ${ps(username)} -Force | Out-Null`}`;\n\t\tconst command = (script: string) => [\"powershell.exe\", \"-NoProfile\", \"-Command\", script];\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent,\n\t\t\tinstall: [command(register)],\n\t\t\tremove: [\n\t\t\t\tcommand(\n\t\t\t\t\t`$ErrorActionPreference='Stop'; Unregister-ScheduledTask -TaskName '${serviceName}' -Confirm:$false`,\n\t\t\t\t),\n\t\t\t],\n\t\t\tverify: command(`$ErrorActionPreference='Stop'; Get-ScheduledTask -TaskName '${serviceName}' | Out-Null`),\n\t\t\tstart: command(`$ErrorActionPreference='Stop'; Start-ScheduledTask -TaskName '${serviceName}'`),\n\t\t};\n\t}\n\tthrow new Error(`Automatic startup is not supported on ${platform}. Run lunr gateway start manually.`);\n}\n\nfunction execute(command: string[]): void {\n\tconst [file, ...args] = command;\n\tconst result = spawnSync(file, args, { stdio: \"inherit\", windowsHide: true, timeout: 120_000 });\n\tif (result.error) throw result.error;\n\tif (result.status !== 0) throw new Error(`${file} failed. Check OS permissions and run lunr gateway doctor.`);\n}\n\nexport async function configureStartup(mode: StartupMode): Promise<void> {\n\tconst previous = loadServiceSettings().startup;\n\tif (process.platform === \"linux\" && mode === \"login\") {\n\t\tconst linger = spawnSync(\"loginctl\", [\"show-user\", userInfo().username, \"--property=Linger\", \"--value\"], {\n\t\t\tencoding: \"utf8\",\n\t\t\ttimeout: 10_000,\n\t\t});\n\t\tif (linger.stdout?.trim() === \"yes\")\n\t\t\tthrow new Error(\n\t\t\t\t\"Linux user services already start at boot because lingering is enabled. Choose boot, or disable lingering yourself before choosing login. lunR will not change other user services.\",\n\t\t\t);\n\t}\n\tconst user = userInfo();\n\tconst options = {\n\t\thome: homedir(),\n\t\tagentDir: getAgentDir(),\n\t\tnode: process.execPath,\n\t\tcli: cliPath(),\n\t\tusername: user.username,\n\t\tuid: user.uid,\n\t};\n\tif (previous !== \"off\") {\n\t\tconst old = startupSpec(process.platform, previous, options);\n\t\tfor (const command of old.remove) execute(command);\n\t\tif (process.platform === \"darwin\" && previous === \"boot\") execute([\"sudo\", \"rm\", \"-f\", old.path]);\n\t\telse rmSync(old.path, { force: true });\n\t\tatomicJson(settingsPath(), { startup: \"off\" });\n\t}\n\tif (mode === \"off\") {\n\t\tatomicJson(settingsPath(), { startup: \"off\" });\n\t\treturn;\n\t}\n\tconst spec = startupSpec(process.platform, mode, options);\n\tmkdirSync(serviceDirectory(), { recursive: true, mode: 0o700 });\n\tif (process.platform === \"darwin\" && mode === \"boot\") {\n\t\tconst temporary = join(serviceDirectory(), \"install.plist\");\n\t\twriteFileSync(temporary, spec.content, { mode: 0o600 });\n\t\ttry {\n\t\t\texecute([\"sudo\", \"install\", \"-o\", \"root\", \"-g\", \"wheel\", \"-m\", \"644\", temporary, spec.path]);\n\t\t} finally {\n\t\t\trmSync(temporary, { force: true });\n\t\t}\n\t} else {\n\t\tmkdirSync(dirname(spec.path), { recursive: true, mode: 0o700 });\n\t\twriteFileSync(spec.path, spec.content, { mode: 0o600 });\n\t}\n\tfor (const command of spec.install) execute(command);\n\texecute(spec.verify);\n\tatomicJson(settingsPath(), { startup: mode });\n}\n\nfunction startupVerification(): string {\n\tconst mode = loadServiceSettings().startup;\n\tif (mode === \"off\") return \"\";\n\ttry {\n\t\tconst user = userInfo();\n\t\tconst spec = startupSpec(process.platform, mode, {\n\t\t\thome: homedir(),\n\t\t\tagentDir: getAgentDir(),\n\t\t\tnode: process.execPath,\n\t\t\tcli: cliPath(),\n\t\t\tusername: user.username,\n\t\t\tuid: user.uid,\n\t\t});\n\t\tconst [command, ...args] = spec.verify[0] === \"sudo\" ? spec.verify.slice(1) : spec.verify;\n\t\tconst result = spawnSync(command, args, { encoding: \"utf8\", windowsHide: true, timeout: 5000 });\n\t\treturn result.status === 0 ? \" (native service verified)\" : \" (native service missing or unavailable)\";\n\t} catch {\n\t\treturn \" (native service could not be checked)\";\n\t}\n}\n\nexport function serviceStatusText(): string {\n\tconst status = readGatewayStatus();\n\tconst alive = !!status && processExists(status.pid);\n\tconst fresh = !!status && Date.now() - Date.parse(status.updatedAt) < 5000;\n\treturn [\n\t\t`Gateway: ${alive ? `${status.state}${fresh ? \"\" : \" (not responding)\"}` : \"stopped\"}`,\n\t\t`Automatic startup: ${loadServiceSettings().startup}${startupVerification()}`,\n\t\t...(alive\n\t\t\t? [`PID: ${status.pid}`, ...Object.entries(status.platforms).map(([name, state]) => `${name}: ${state}`)]\n\t\t\t: []),\n\t\t`Log: ${gatewayLogPath()}`,\n\t].join(\"\\n\");\n}\n\nexport function readGatewayLog(): string {\n\ttry {\n\t\treturn readFileSync(gatewayLogPath(), \"utf8\")\n\t\t\t.split(\"\\n\")\n\t\t\t.slice(-100)\n\t\t\t.join(\"\\n\")\n\t\t\t.replace(/bot\\d+:[A-Za-z0-9_-]+/g, \"bot[redacted]\");\n\t} catch {\n\t\treturn \"No gateway log yet.\";\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gateway/service.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AACnD,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,WAAW,CAAC;CACrB;AACD,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,cAA+C,CAAC;AAC7E,eAAO,MAAM,cAAc,cAAgD,CAAC;AAK5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAK7D;AAUD,wBAAgB,mBAAmB,IAAI,sBAAsB,CAM5D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQlD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,SAAe,GAAG,oBAAoB,GAAG,SAAS,CA4BvF;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG;IACjD,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3F,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB,CA8EA;AAUD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAsD3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS1D;AAED,mGAAmG;AACnG,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyH5D;AAsCD,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAWD,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,EACjC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnG,WAAW,CA+Db;AASD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDvE;AAuBD,wBAAgB,iBAAiB,IAAI,MAAM,CAY1C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAUvC","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir, userInfo } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir } from \"../config.ts\";\n\nexport type StartupMode = \"off\" | \"login\" | \"boot\";\nexport interface GatewayServiceSettings {\n\tstartup: StartupMode;\n}\nexport interface GatewayRuntimeStatus {\n\tversion: 1;\n\tinstance: string;\n\tpid: number;\n\tstartedAt: string;\n\tupdatedAt: string;\n\tstate: \"starting\" | \"ready\" | \"stopping\";\n\tplatforms: Record<string, string>;\n}\nconst servicePrefix = \"lunr-gateway\";\nexport const serviceDirectory = () => join(getAgentDir(), \"gateway-service\");\nexport const gatewayLogPath = () => join(serviceDirectory(), \"gateway.log\");\nconst statusPath = () => join(serviceDirectory(), \"status.json\");\nconst controlPath = () => join(serviceDirectory(), \"control.json\");\nconst settingsPath = () => join(serviceDirectory(), \"service.json\");\n\nexport function atomicJson(path: string, value: unknown): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\tconst temp = `${path}.${randomUUID()}.tmp`;\n\twriteFileSync(temp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n\trenameSync(temp, path);\n}\n\nfunction readJson(path: string): unknown {\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function loadServiceSettings(): GatewayServiceSettings {\n\tconst raw = readJson(settingsPath());\n\tif (raw && typeof raw === \"object\" && \"startup\" in raw && (raw.startup === \"login\" || raw.startup === \"boot\")) {\n\t\treturn { startup: raw.startup };\n\t}\n\treturn { startup: \"off\" };\n}\n\nexport function processExists(pid: number): boolean {\n\tif (!Number.isSafeInteger(pid) || pid <= 0) return false;\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn !(error && typeof error === \"object\" && \"code\" in error && error.code === \"ESRCH\");\n\t}\n}\n\nexport function readGatewayStatus(file = statusPath()): GatewayRuntimeStatus | undefined {\n\tconst raw = readJson(file);\n\tif (\n\t\t!raw ||\n\t\ttypeof raw !== \"object\" ||\n\t\t!(\"version\" in raw) ||\n\t\traw.version !== 1 ||\n\t\t!(\"pid\" in raw) ||\n\t\ttypeof raw.pid !== \"number\"\n\t)\n\t\treturn undefined;\n\tif (\n\t\t!(\"instance\" in raw) ||\n\t\ttypeof raw.instance !== \"string\" ||\n\t\t!(\"updatedAt\" in raw) ||\n\t\ttypeof raw.updatedAt !== \"string\"\n\t)\n\t\treturn undefined;\n\tif (!(\"state\" in raw) || ![\"starting\", \"ready\", \"stopping\"].includes(String(raw.state))) return undefined;\n\tif (\n\t\t!(\"platforms\" in raw) ||\n\t\t!raw.platforms ||\n\t\ttypeof raw.platforms !== \"object\" ||\n\t\t!(\"startedAt\" in raw) ||\n\t\ttypeof raw.startedAt !== \"string\"\n\t)\n\t\treturn undefined;\n\treturn raw as GatewayRuntimeStatus;\n}\n\nexport function gatewayIsRunning(): boolean {\n\tconst status = readGatewayStatus();\n\treturn !!status && processExists(status.pid);\n}\n\nexport function claimGateway(onStop: () => void): {\n\tupdate: (platforms: Record<string, string>, state?: GatewayRuntimeStatus[\"state\"]) => void;\n\trelease: () => void;\n} {\n\tconst dir = serviceDirectory();\n\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\tconst lock = join(dir, \"lock\");\n\ttry {\n\t\tmkdirSync(lock);\n\t} catch (error) {\n\t\tif (!error || typeof error !== \"object\" || !(\"code\" in error) || error.code !== \"EEXIST\") throw error;\n\t\tconst previous = readGatewayStatus(join(lock, \"owner.json\"));\n\t\tif (!previous || processExists(previous.pid))\n\t\t\tthrow new Error(\"Gateway already running or startup lock is unresolved. Run lunr gateway doctor.\");\n\t\t// Recovery is serialized; a late recoverer must not remove a new owner's lock.\n\t\tconst recovery = join(dir, \"recovery\");\n\t\tmkdirSync(recovery);\n\t\ttry {\n\t\t\tconst current = readGatewayStatus(join(lock, \"owner.json\"));\n\t\t\tif (!current || current.instance !== previous.instance || processExists(current.pid))\n\t\t\t\tthrow new Error(\"Gateway ownership changed. Try again.\");\n\t\t\trmSync(lock, { recursive: true });\n\t\t\tmkdirSync(lock);\n\t\t} finally {\n\t\t\trmSync(recovery, { recursive: true, force: true });\n\t\t}\n\t}\n\tconst status: GatewayRuntimeStatus = {\n\t\tversion: 1,\n\t\tinstance: randomUUID(),\n\t\tpid: process.pid,\n\t\tstartedAt: new Date().toISOString(),\n\t\tupdatedAt: new Date().toISOString(),\n\t\tstate: \"starting\",\n\t\tplatforms: {},\n\t};\n\tatomicJson(join(lock, \"owner.json\"), status);\n\tconst save = () => {\n\t\tstatus.updatedAt = new Date().toISOString();\n\t\tatomicJson(statusPath(), status);\n\t};\n\tsave();\n\tlet stopping = false;\n\tconst timer = setInterval(() => {\n\t\tsave();\n\t\tconst control = readJson(controlPath());\n\t\tif (\n\t\t\t!stopping &&\n\t\t\tcontrol &&\n\t\t\ttypeof control === \"object\" &&\n\t\t\t\"instance\" in control &&\n\t\t\tcontrol.instance === status.instance &&\n\t\t\t\"action\" in control &&\n\t\t\tcontrol.action === \"stop\"\n\t\t) {\n\t\t\tstopping = true;\n\t\t\tstatus.state = \"stopping\";\n\t\t\tonStop();\n\t\t}\n\t}, 500);\n\treturn {\n\t\tupdate(platforms, state = \"ready\") {\n\t\t\tif (stopping && state !== \"stopping\") {\n\t\t\t\tstopping = false;\n\t\t\t\tconst control = readJson(controlPath());\n\t\t\t\tif (control && typeof control === \"object\" && \"instance\" in control && control.instance === status.instance)\n\t\t\t\t\trmSync(controlPath(), { force: true });\n\t\t\t}\n\t\t\tstatus.platforms = { ...platforms };\n\t\t\tstatus.state = state;\n\t\t\tsave();\n\t\t},\n\t\trelease() {\n\t\t\tclearInterval(timer);\n\t\t\tif (readGatewayStatus()?.instance === status.instance) {\n\t\t\t\trmSync(statusPath(), { force: true });\n\t\t\t\trmSync(controlPath(), { force: true });\n\t\t\t\trmSync(lock, { recursive: true, force: true });\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction cliPath(): string {\n\tconst candidate = process.argv[1];\n\tif (candidate && existsSync(candidate)) return resolve(candidate);\n\treturn fileURLToPath(new URL(\"../cli.js\", import.meta.url));\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport async function startGatewayService(): Promise<string> {\n\tif (gatewayIsRunning()) {\n\t\tif (readGatewayStatus()?.state === \"ready\") return \"Gateway is already running.\";\n\t\tthrow new Error(\n\t\t\t`Gateway process is running but no bot is connected. ${serviceStatusText()}\\nInspect ${gatewayLogPath()}`,\n\t\t);\n\t}\n\tmkdirSync(serviceDirectory(), { recursive: true, mode: 0o700 });\n\tconst startup = loadServiceSettings().startup;\n\tif (startup !== \"off\") {\n\t\tconst user = userInfo();\n\t\tconst spec = startupSpec(process.platform, startup, {\n\t\t\thome: homedir(),\n\t\t\tagentDir: getAgentDir(),\n\t\t\tnode: process.execPath,\n\t\t\tcli: cliPath(),\n\t\t\tusername: user.username,\n\t\t\tuid: user.uid,\n\t\t});\n\t\texecute(spec.start);\n\t\tfor (let i = 0; i < 100; i++) {\n\t\t\tif (readGatewayStatus()?.state === \"ready\" && gatewayIsRunning()) return \"Gateway started.\";\n\t\t\tawait sleep(200);\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Startup service did not connect within 20 seconds. ${serviceStatusText()}\\nRun lunr gateway doctor and inspect ${gatewayLogPath()}`,\n\t\t);\n\t}\n\tconst { openSync, closeSync } = await import(\"node:fs\");\n\tconst log = openSync(gatewayLogPath(), \"a\", 0o600);\n\tconst child = spawn(process.execPath, [cliPath(), \"gateway\", \"run\"], {\n\t\tcwd: homedir(),\n\t\tdetached: true,\n\t\twindowsHide: true,\n\t\tstdio: [\"ignore\", log, log],\n\t\tenv: { ...process.env, PI_CODING_AGENT_DIR: getAgentDir() },\n\t});\n\tcloseSync(log);\n\tlet spawnError: Error | undefined;\n\tchild.on(\"error\", (error) => {\n\t\tspawnError = error;\n\t});\n\tchild.unref();\n\tfor (let i = 0; i < 100; i++) {\n\t\tif (spawnError) throw spawnError;\n\t\tconst status = readGatewayStatus();\n\t\tif (status && status.pid === child.pid && status.state === \"ready\") return \"Gateway started.\";\n\t\tif (child.exitCode !== null)\n\t\t\tthrow new Error(`Gateway exited with code ${child.exitCode}. See ${gatewayLogPath()}`);\n\t\tawait sleep(200);\n\t}\n\tthrow new Error(\n\t\t`Gateway did not connect within 20 seconds. ${serviceStatusText()}\\nCheck lunr gateway status and ${gatewayLogPath()}`,\n\t);\n}\n\nexport async function stopGatewayService(): Promise<string> {\n\tconst status = readGatewayStatus();\n\tif (!status || !processExists(status.pid)) return \"Gateway is not running.\";\n\tatomicJson(controlPath(), { instance: status.instance, action: \"stop\" });\n\tfor (let i = 0; i < 150; i++) {\n\t\tif (readGatewayStatus()?.instance !== status.instance || !processExists(status.pid)) return \"Gateway stopped.\";\n\t\tawait sleep(200);\n\t}\n\tthrow new Error(\"Gateway is still stopping after 30 seconds. Inspect the log; no unrelated process was terminated.\");\n}\n\n/** Remove only this profile's gateway and startup entry, before uninstall deletes control data. */\nexport async function teardownGatewayService(): Promise<void> {\n\tconst dir = serviceDirectory();\n\tconst ownerPath = join(dir, \"lock\", \"owner.json\");\n\tconst status = readGatewayStatus();\n\tconst lockOwner = readGatewayStatus(ownerPath);\n\tif ((existsSync(statusPath()) && !status) || (existsSync(join(dir, \"lock\")) && !lockOwner))\n\t\tthrow new Error(\"Gateway ownership is unresolved. Profile retained; run lunr gateway doctor.\");\n\tif (status && lockOwner && (status.instance !== lockOwner.instance || status.pid !== lockOwner.pid))\n\t\tthrow new Error(\"Gateway status and lock ownership disagree. Profile retained.\");\n\tconst owner = status ?? lockOwner;\n\tif (owner && processExists(owner.pid) && !lockOwner)\n\t\tthrow new Error(\"Running gateway has no ownership lock. Profile retained.\");\n\n\tconst rawSettings = readJson(settingsPath());\n\tif (\n\t\texistsSync(settingsPath()) &&\n\t\t(!rawSettings ||\n\t\t\ttypeof rawSettings !== \"object\" ||\n\t\t\t!(\"startup\" in rawSettings) ||\n\t\t\t![\"off\", \"login\", \"boot\"].includes(String(rawSettings.startup)))\n\t)\n\t\tthrow new Error(\"Gateway startup settings are unresolved. Profile retained.\");\n\tconst mode = loadServiceSettings().startup;\n\tconst user = userInfo();\n\tconst options = {\n\t\thome: homedir(),\n\t\tagentDir: getAgentDir(),\n\t\tnode: process.execPath,\n\t\tcli: cliPath(),\n\t\tusername: user.username,\n\t\tuid: user.uid,\n\t};\n\tconst spec = mode === \"off\" ? undefined : startupSpec(process.platform, mode, options);\n\tconst startupFileMissing = !!spec && !existsSync(spec.path);\n\tif (!spec) {\n\t\tif (\n\t\t\t[\"linux\", \"darwin\", \"win32\"].includes(process.platform) &&\n\t\t\t([\"login\", \"boot\"] as const).some((candidate) =>\n\t\t\t\texistsSync(startupSpec(process.platform, candidate, options).path),\n\t\t\t)\n\t\t)\n\t\t\tthrow new Error(\"Gateway startup file exists without active ownership settings. Profile retained.\");\n\t} else if (startupFileMissing) {\n\t\t// Retry only verification. A missing file never authorizes removing a registration.\n\t\tconfirmStartupRemoval(spec);\n\t} else {\n\t\tif (readFileSync(spec.path, \"utf8\") !== spec.content)\n\t\t\tthrow new Error(\"Gateway startup file does not match this installation. Profile retained.\");\n\t\tif (process.platform === \"linux\") {\n\t\t\tconst unit = spec.verify.at(-1)!;\n\t\t\tconst registration = serviceQuery([\n\t\t\t\t\"systemctl\",\n\t\t\t\t\"--user\",\n\t\t\t\t\"show\",\n\t\t\t\tunit,\n\t\t\t\t\"--property=FragmentPath\",\n\t\t\t\t\"--property=DropInPaths\",\n\t\t\t]);\n\t\t\tconst properties = new Map(\n\t\t\t\tregistration.stdout\n\t\t\t\t\t.trim()\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.map((line) => {\n\t\t\t\t\t\tconst separator = line.indexOf(\"=\");\n\t\t\t\t\t\treturn [line.slice(0, separator), line.slice(separator + 1)];\n\t\t\t\t\t}),\n\t\t\t);\n\t\t\tif (\n\t\t\t\tregistration.status !== 0 ||\n\t\t\t\tproperties.get(\"FragmentPath\") !== spec.path ||\n\t\t\t\tproperties.get(\"DropInPaths\") !== \"\"\n\t\t\t)\n\t\t\t\tthrow new Error(\"Gateway systemd ownership could not be verified. Profile retained.\");\n\t\t} else if (process.platform === \"win32\") {\n\t\t\tconst name = `${servicePrefix}-${createHash(\"sha256\").update(resolve(getAgentDir())).digest(\"hex\").slice(0, 10)}`;\n\t\t\tconst actionArgs = `-NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"${spec.path}\"`;\n\t\t\texecute([\n\t\t\t\t\"powershell.exe\",\n\t\t\t\t\"-NoProfile\",\n\t\t\t\t\"-Command\",\n\t\t\t\t`$ErrorActionPreference='Stop'; $t=Get-ScheduledTask -TaskName ${ps(name)}; if($t.Actions.Count -ne 1 -or $t.Actions[0].Execute -ne 'powershell.exe' -or $t.Actions[0].Arguments -ne ${ps(actionArgs)}){throw 'Gateway task ownership mismatch'}`,\n\t\t\t]);\n\t\t}\n\t}\n\n\t// Request graceful shutdown before unregistering; no PID signal can hit a reused process.\n\tif (owner && processExists(owner.pid)) {\n\t\tatomicJson(controlPath(), { instance: owner.instance, action: \"stop\" });\n\t}\n\tif (spec && !startupFileMissing) {\n\t\tif (process.platform === \"linux\") {\n\t\t\tconst unit = spec.verify.at(-1)!;\n\t\t\texecute([\"systemctl\", \"--user\", \"disable\", \"--now\", unit]);\n\t\t\tconst disabled = serviceQuery(spec.verify);\n\t\t\tif (disabled.status !== 1 || disabled.stdout.trim() !== \"disabled\")\n\t\t\t\tthrow new Error(\"Gateway startup disable could not be confirmed. Profile retained.\");\n\t\t} else {\n\t\t\tfor (const command of spec.remove) {\n\t\t\t\tconst removed = serviceQuery(command);\n\t\t\t\tif (\n\t\t\t\t\tremoved.status !== 0 &&\n\t\t\t\t\t!(process.platform === \"darwin\" && /Could not find (?:specified )?service/i.test(removed.stderr))\n\t\t\t\t)\n\t\t\t\t\tthrow new Error(\"Gateway startup removal failed. Profile retained.\");\n\t\t\t}\n\t\t}\n\t\tif (process.platform === \"darwin\" && mode === \"boot\") execute([\"sudo\", \"rm\", \"-f\", spec.path]);\n\t\telse rmSync(spec.path);\n\t\tconfirmStartupRemoval(spec);\n\t}\n\tif (spec) {\n\t\tatomicJson(settingsPath(), { startup: \"off\" });\n\t}\n\tfor (let i = 0; owner && processExists(owner.pid); i++) {\n\t\tif (i >= 150) throw new Error(\"Gateway shutdown could not be confirmed after 30 seconds. Profile retained.\");\n\t\tawait sleep(200);\n\t}\n\tconst remaining = readGatewayStatus();\n\tconst remainingLock = readGatewayStatus(ownerPath);\n\tif ((remaining && processExists(remaining.pid)) || (remainingLock && processExists(remainingLock.pid)))\n\t\tthrow new Error(\"Gateway ownership changed during teardown. Profile retained.\");\n}\n\n/** Refresh and verify absence without unregistering an unverified startup entry. */\nfunction confirmStartupRemoval(spec: StartupSpec): void {\n\tif (process.platform === \"linux\") {\n\t\texecute([\"systemctl\", \"--user\", \"daemon-reload\"]);\n\t\tconst removed = serviceQuery([\n\t\t\t\"systemctl\",\n\t\t\t\"--user\",\n\t\t\t\"show\",\n\t\t\tspec.verify.at(-1)!,\n\t\t\t\"--property=LoadState\",\n\t\t\t\"--value\",\n\t\t]);\n\t\tif (removed.status !== 0 || removed.stdout.trim() !== \"not-found\")\n\t\t\tthrow new Error(\"Gateway startup removal could not be confirmed. Profile retained.\");\n\t} else if (process.platform === \"darwin\") {\n\t\tconst removed = serviceQuery(spec.verify);\n\t\tif (removed.status === 0 || !/Could not find (?:specified )?service/i.test(removed.stderr))\n\t\t\tthrow new Error(\"Gateway launchd removal could not be confirmed. Profile retained.\");\n\t} else {\n\t\tconst name = `${servicePrefix}-${createHash(\"sha256\").update(resolve(getAgentDir())).digest(\"hex\").slice(0, 10)}`;\n\t\texecute([\n\t\t\t\"powershell.exe\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-Command\",\n\t\t\t`$ErrorActionPreference='Stop'; if(Get-ScheduledTask | Where-Object {$_.TaskName -eq ${ps(name)}}){throw 'Gateway task still registered'}`,\n\t\t]);\n\t}\n}\n\nfunction serviceQuery(command: string[]) {\n\tconst [file, ...args] = command;\n\tconst result = spawnSync(file, args, { encoding: \"utf8\", windowsHide: true, timeout: 120_000 });\n\tif (result.error) throw result.error;\n\treturn result;\n}\n\nexport interface StartupSpec {\n\tpath: string;\n\tcontent: string;\n\tinstall: string[][];\n\tremove: string[][];\n\tverify: string[];\n\tstart: string[];\n}\nfunction xml(value: string): string {\n\treturn value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\");\n}\nfunction systemdArg(value: string): string {\n\treturn `\"${value.replaceAll(\"%\", \"%%\").replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"')}\"`;\n}\nfunction ps(value: string): string {\n\treturn `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nexport function startupSpec(\n\tplatform: NodeJS.Platform,\n\tmode: Exclude<StartupMode, \"off\">,\n\toptions: { home: string; agentDir: string; node: string; cli: string; username: string; uid: number },\n): StartupSpec {\n\tconst { home, agentDir, node, cli, username, uid } = options;\n\tif ([home, agentDir, node, cli, username].some((value) => /[\\r\\n\\0]/.test(value)))\n\t\tthrow new Error(\"Startup paths and account names must not contain control characters.\");\n\tconst suffix = createHash(\"sha256\").update(resolve(agentDir)).digest(\"hex\").slice(0, 10);\n\tconst serviceName = `${servicePrefix}-${suffix}`;\n\tconst log = join(agentDir, \"gateway-service\", \"gateway.log\");\n\tif (platform === \"linux\") {\n\t\tconst path = join(home, \".config\", \"systemd\", \"user\", `${serviceName}.service`);\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent: `[Unit]\\nDescription=lunR chat gateway\\nAfter=network-online.target\\n\\n[Service]\\nType=simple\\nExecStart=${systemdArg(node)} ${systemdArg(cli)} gateway run\\nWorkingDirectory=${systemdArg(home)}\\nEnvironment=${systemdArg(`PI_CODING_AGENT_DIR=${agentDir}`)}\\nRestart=on-failure\\nRestartSec=5\\nTimeoutStopSec=30\\nStandardOutput=append:${log.replaceAll(\"%\", \"%%\")}\\nStandardError=append:${log.replaceAll(\"%\", \"%%\")}\\nUMask=0077\\n\\n[Install]\\nWantedBy=default.target\\n`,\n\t\t\tinstall: [\n\t\t\t\t...(mode === \"boot\" ? [[\"loginctl\", \"enable-linger\", username]] : []),\n\t\t\t\t[\"systemctl\", \"--user\", \"daemon-reload\"],\n\t\t\t\t[\"systemctl\", \"--user\", \"enable\", `${serviceName}.service`],\n\t\t\t],\n\t\t\tremove: [[\"systemctl\", \"--user\", \"disable\", `${serviceName}.service`]],\n\t\t\tverify: [\"systemctl\", \"--user\", \"is-enabled\", `${serviceName}.service`],\n\t\t\tstart: [\"systemctl\", \"--user\", \"start\", `${serviceName}.service`],\n\t\t};\n\t}\n\tif (platform === \"darwin\") {\n\t\tconst label = `dev.lunr.gateway.${suffix}`;\n\t\tconst path =\n\t\t\tmode === \"boot\"\n\t\t\t\t? `/Library/LaunchDaemons/${label}.plist`\n\t\t\t\t: join(home, \"Library\", \"LaunchAgents\", `${label}.plist`);\n\t\tconst domain = mode === \"boot\" ? \"system\" : `gui/${uid}`;\n\t\tconst command = (args: string[]) => (mode === \"boot\" ? [\"sudo\", \"launchctl\", ...args] : [\"launchctl\", ...args]);\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\\n<plist version=\"1.0\"><dict><key>Label</key><string>${label}</string><key>ProgramArguments</key><array>${[node, cli, \"gateway\", \"run\"].map((v) => `<string>${xml(v)}</string>`).join(\"\")}</array><key>WorkingDirectory</key><string>${xml(home)}</string>${mode === \"boot\" ? `<key>UserName</key><string>${xml(username)}</string>` : \"\"}<key>EnvironmentVariables</key><dict><key>PI_CODING_AGENT_DIR</key><string>${xml(agentDir)}</string><key>HOME</key><string>${xml(home)}</string></dict><key>RunAtLoad</key><true/><key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict><key>StandardOutPath</key><string>${xml(log)}</string><key>StandardErrorPath</key><string>${xml(log)}</string></dict></plist>\\n`,\n\t\t\tinstall: [command([\"bootstrap\", domain, path])],\n\t\t\tremove: [command([\"bootout\", `${domain}/${label}`])],\n\t\t\tverify: command([\"print\", `${domain}/${label}`]),\n\t\t\tstart: command([\"kickstart\", `${domain}/${label}`]),\n\t\t};\n\t}\n\tif (platform === \"win32\") {\n\t\tconst path = join(agentDir, \"gateway-service\", \"startup.ps1\");\n\t\tconst content = `$env:PI_CODING_AGENT_DIR = ${ps(agentDir)}\\nSet-Location -LiteralPath ${ps(home)}\\n& ${ps(node)} ${ps(cli)} gateway run *>> ${ps(log)}\\nexit $LASTEXITCODE\\n`;\n\t\tconst action = `New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ${ps(`-NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"${path}\"`)}`;\n\t\tconst trigger =\n\t\t\tmode === \"boot\"\n\t\t\t\t? \"New-ScheduledTaskTrigger -AtStartup\"\n\t\t\t\t: `New-ScheduledTaskTrigger -AtLogOn -User ${ps(username)}`;\n\t\tconst register = `$ErrorActionPreference='Stop'; $a=${action}; $t=${trigger}; $s=New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; ${mode === \"boot\" ? `$c=Get-Credential -UserName ${ps(username)} -Message 'Windows needs your account credentials to run lunR before login'; if(!$c){throw 'Cancelled'}; Register-ScheduledTask -TaskName '${serviceName}' -Action $a -Trigger $t -Settings $s -User $c.UserName -Password $c.GetNetworkCredential().Password -Force | Out-Null` : `Register-ScheduledTask -TaskName '${serviceName}' -Action $a -Trigger $t -Settings $s -User ${ps(username)} -Force | Out-Null`}`;\n\t\tconst command = (script: string) => [\"powershell.exe\", \"-NoProfile\", \"-Command\", script];\n\t\treturn {\n\t\t\tpath,\n\t\t\tcontent,\n\t\t\tinstall: [command(register)],\n\t\t\tremove: [\n\t\t\t\tcommand(\n\t\t\t\t\t`$ErrorActionPreference='Stop'; Unregister-ScheduledTask -TaskName '${serviceName}' -Confirm:$false`,\n\t\t\t\t),\n\t\t\t],\n\t\t\tverify: command(`$ErrorActionPreference='Stop'; Get-ScheduledTask -TaskName '${serviceName}' | Out-Null`),\n\t\t\tstart: command(`$ErrorActionPreference='Stop'; Start-ScheduledTask -TaskName '${serviceName}'`),\n\t\t};\n\t}\n\tthrow new Error(`Automatic startup is not supported on ${platform}. Run lunr gateway start manually.`);\n}\n\nfunction execute(command: string[]): void {\n\tconst [file, ...args] = command;\n\tconst result = spawnSync(file, args, { stdio: \"inherit\", windowsHide: true, timeout: 120_000 });\n\tif (result.error) throw result.error;\n\tif (result.status !== 0) throw new Error(`${file} failed. Check OS permissions and run lunr gateway doctor.`);\n}\n\nexport async function configureStartup(mode: StartupMode): Promise<void> {\n\tconst previous = loadServiceSettings().startup;\n\tif (process.platform === \"linux\" && mode === \"login\") {\n\t\tconst linger = spawnSync(\"loginctl\", [\"show-user\", userInfo().username, \"--property=Linger\", \"--value\"], {\n\t\t\tencoding: \"utf8\",\n\t\t\ttimeout: 10_000,\n\t\t});\n\t\tif (linger.stdout?.trim() === \"yes\")\n\t\t\tthrow new Error(\n\t\t\t\t\"Linux user services already start at boot because lingering is enabled. Choose boot, or disable lingering yourself before choosing login. lunR will not change other user services.\",\n\t\t\t);\n\t}\n\tconst user = userInfo();\n\tconst options = {\n\t\thome: homedir(),\n\t\tagentDir: getAgentDir(),\n\t\tnode: process.execPath,\n\t\tcli: cliPath(),\n\t\tusername: user.username,\n\t\tuid: user.uid,\n\t};\n\tif (previous !== \"off\") {\n\t\tconst old = startupSpec(process.platform, previous, options);\n\t\tfor (const command of old.remove) execute(command);\n\t\tif (process.platform === \"darwin\" && previous === \"boot\") execute([\"sudo\", \"rm\", \"-f\", old.path]);\n\t\telse rmSync(old.path, { force: true });\n\t\tatomicJson(settingsPath(), { startup: \"off\" });\n\t}\n\tif (mode === \"off\") {\n\t\tatomicJson(settingsPath(), { startup: \"off\" });\n\t\treturn;\n\t}\n\tconst spec = startupSpec(process.platform, mode, options);\n\tmkdirSync(serviceDirectory(), { recursive: true, mode: 0o700 });\n\tif (process.platform === \"darwin\" && mode === \"boot\") {\n\t\tconst temporary = join(serviceDirectory(), \"install.plist\");\n\t\twriteFileSync(temporary, spec.content, { mode: 0o600 });\n\t\ttry {\n\t\t\texecute([\"sudo\", \"install\", \"-o\", \"root\", \"-g\", \"wheel\", \"-m\", \"644\", temporary, spec.path]);\n\t\t} finally {\n\t\t\trmSync(temporary, { force: true });\n\t\t}\n\t} else {\n\t\tmkdirSync(dirname(spec.path), { recursive: true, mode: 0o700 });\n\t\twriteFileSync(spec.path, spec.content, { mode: 0o600 });\n\t}\n\tfor (const command of spec.install) execute(command);\n\texecute(spec.verify);\n\tatomicJson(settingsPath(), { startup: mode });\n}\n\nfunction startupVerification(): string {\n\tconst mode = loadServiceSettings().startup;\n\tif (mode === \"off\") return \"\";\n\ttry {\n\t\tconst user = userInfo();\n\t\tconst spec = startupSpec(process.platform, mode, {\n\t\t\thome: homedir(),\n\t\t\tagentDir: getAgentDir(),\n\t\t\tnode: process.execPath,\n\t\t\tcli: cliPath(),\n\t\t\tusername: user.username,\n\t\t\tuid: user.uid,\n\t\t});\n\t\tconst [command, ...args] = spec.verify[0] === \"sudo\" ? spec.verify.slice(1) : spec.verify;\n\t\tconst result = spawnSync(command, args, { encoding: \"utf8\", windowsHide: true, timeout: 5000 });\n\t\treturn result.status === 0 ? \" (native service verified)\" : \" (native service missing or unavailable)\";\n\t} catch {\n\t\treturn \" (native service could not be checked)\";\n\t}\n}\n\nexport function serviceStatusText(): string {\n\tconst status = readGatewayStatus();\n\tconst alive = !!status && processExists(status.pid);\n\tconst fresh = !!status && Date.now() - Date.parse(status.updatedAt) < 5000;\n\treturn [\n\t\t`Gateway: ${alive ? `${status.state}${fresh ? \"\" : \" (not responding)\"}` : \"stopped\"}`,\n\t\t`Automatic startup: ${loadServiceSettings().startup}${startupVerification()}`,\n\t\t...(alive\n\t\t\t? [`PID: ${status.pid}`, ...Object.entries(status.platforms).map(([name, state]) => `${name}: ${state}`)]\n\t\t\t: []),\n\t\t`Log: ${gatewayLogPath()}`,\n\t].join(\"\\n\");\n}\n\nexport function readGatewayLog(): string {\n\ttry {\n\t\treturn readFileSync(gatewayLogPath(), \"utf8\")\n\t\t\t.split(\"\\n\")\n\t\t\t.slice(-100)\n\t\t\t.join(\"\\n\")\n\t\t\t.replace(/bot\\d+:[A-Za-z0-9_-]+/g, \"bot[redacted]\");\n\t} catch {\n\t\treturn \"No gateway log yet.\";\n\t}\n}\n"]}
|
package/dist/gateway/service.js
CHANGED
|
@@ -223,6 +223,159 @@ export async function stopGatewayService() {
|
|
|
223
223
|
}
|
|
224
224
|
throw new Error("Gateway is still stopping after 30 seconds. Inspect the log; no unrelated process was terminated.");
|
|
225
225
|
}
|
|
226
|
+
/** Remove only this profile's gateway and startup entry, before uninstall deletes control data. */
|
|
227
|
+
export async function teardownGatewayService() {
|
|
228
|
+
const dir = serviceDirectory();
|
|
229
|
+
const ownerPath = join(dir, "lock", "owner.json");
|
|
230
|
+
const status = readGatewayStatus();
|
|
231
|
+
const lockOwner = readGatewayStatus(ownerPath);
|
|
232
|
+
if ((existsSync(statusPath()) && !status) || (existsSync(join(dir, "lock")) && !lockOwner))
|
|
233
|
+
throw new Error("Gateway ownership is unresolved. Profile retained; run lunr gateway doctor.");
|
|
234
|
+
if (status && lockOwner && (status.instance !== lockOwner.instance || status.pid !== lockOwner.pid))
|
|
235
|
+
throw new Error("Gateway status and lock ownership disagree. Profile retained.");
|
|
236
|
+
const owner = status ?? lockOwner;
|
|
237
|
+
if (owner && processExists(owner.pid) && !lockOwner)
|
|
238
|
+
throw new Error("Running gateway has no ownership lock. Profile retained.");
|
|
239
|
+
const rawSettings = readJson(settingsPath());
|
|
240
|
+
if (existsSync(settingsPath()) &&
|
|
241
|
+
(!rawSettings ||
|
|
242
|
+
typeof rawSettings !== "object" ||
|
|
243
|
+
!("startup" in rawSettings) ||
|
|
244
|
+
!["off", "login", "boot"].includes(String(rawSettings.startup))))
|
|
245
|
+
throw new Error("Gateway startup settings are unresolved. Profile retained.");
|
|
246
|
+
const mode = loadServiceSettings().startup;
|
|
247
|
+
const user = userInfo();
|
|
248
|
+
const options = {
|
|
249
|
+
home: homedir(),
|
|
250
|
+
agentDir: getAgentDir(),
|
|
251
|
+
node: process.execPath,
|
|
252
|
+
cli: cliPath(),
|
|
253
|
+
username: user.username,
|
|
254
|
+
uid: user.uid,
|
|
255
|
+
};
|
|
256
|
+
const spec = mode === "off" ? undefined : startupSpec(process.platform, mode, options);
|
|
257
|
+
const startupFileMissing = !!spec && !existsSync(spec.path);
|
|
258
|
+
if (!spec) {
|
|
259
|
+
if (["linux", "darwin", "win32"].includes(process.platform) &&
|
|
260
|
+
["login", "boot"].some((candidate) => existsSync(startupSpec(process.platform, candidate, options).path)))
|
|
261
|
+
throw new Error("Gateway startup file exists without active ownership settings. Profile retained.");
|
|
262
|
+
}
|
|
263
|
+
else if (startupFileMissing) {
|
|
264
|
+
// Retry only verification. A missing file never authorizes removing a registration.
|
|
265
|
+
confirmStartupRemoval(spec);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
if (readFileSync(spec.path, "utf8") !== spec.content)
|
|
269
|
+
throw new Error("Gateway startup file does not match this installation. Profile retained.");
|
|
270
|
+
if (process.platform === "linux") {
|
|
271
|
+
const unit = spec.verify.at(-1);
|
|
272
|
+
const registration = serviceQuery([
|
|
273
|
+
"systemctl",
|
|
274
|
+
"--user",
|
|
275
|
+
"show",
|
|
276
|
+
unit,
|
|
277
|
+
"--property=FragmentPath",
|
|
278
|
+
"--property=DropInPaths",
|
|
279
|
+
]);
|
|
280
|
+
const properties = new Map(registration.stdout
|
|
281
|
+
.trim()
|
|
282
|
+
.split("\n")
|
|
283
|
+
.map((line) => {
|
|
284
|
+
const separator = line.indexOf("=");
|
|
285
|
+
return [line.slice(0, separator), line.slice(separator + 1)];
|
|
286
|
+
}));
|
|
287
|
+
if (registration.status !== 0 ||
|
|
288
|
+
properties.get("FragmentPath") !== spec.path ||
|
|
289
|
+
properties.get("DropInPaths") !== "")
|
|
290
|
+
throw new Error("Gateway systemd ownership could not be verified. Profile retained.");
|
|
291
|
+
}
|
|
292
|
+
else if (process.platform === "win32") {
|
|
293
|
+
const name = `${servicePrefix}-${createHash("sha256").update(resolve(getAgentDir())).digest("hex").slice(0, 10)}`;
|
|
294
|
+
const actionArgs = `-NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File "${spec.path}"`;
|
|
295
|
+
execute([
|
|
296
|
+
"powershell.exe",
|
|
297
|
+
"-NoProfile",
|
|
298
|
+
"-Command",
|
|
299
|
+
`$ErrorActionPreference='Stop'; $t=Get-ScheduledTask -TaskName ${ps(name)}; if($t.Actions.Count -ne 1 -or $t.Actions[0].Execute -ne 'powershell.exe' -or $t.Actions[0].Arguments -ne ${ps(actionArgs)}){throw 'Gateway task ownership mismatch'}`,
|
|
300
|
+
]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Request graceful shutdown before unregistering; no PID signal can hit a reused process.
|
|
304
|
+
if (owner && processExists(owner.pid)) {
|
|
305
|
+
atomicJson(controlPath(), { instance: owner.instance, action: "stop" });
|
|
306
|
+
}
|
|
307
|
+
if (spec && !startupFileMissing) {
|
|
308
|
+
if (process.platform === "linux") {
|
|
309
|
+
const unit = spec.verify.at(-1);
|
|
310
|
+
execute(["systemctl", "--user", "disable", "--now", unit]);
|
|
311
|
+
const disabled = serviceQuery(spec.verify);
|
|
312
|
+
if (disabled.status !== 1 || disabled.stdout.trim() !== "disabled")
|
|
313
|
+
throw new Error("Gateway startup disable could not be confirmed. Profile retained.");
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
for (const command of spec.remove) {
|
|
317
|
+
const removed = serviceQuery(command);
|
|
318
|
+
if (removed.status !== 0 &&
|
|
319
|
+
!(process.platform === "darwin" && /Could not find (?:specified )?service/i.test(removed.stderr)))
|
|
320
|
+
throw new Error("Gateway startup removal failed. Profile retained.");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (process.platform === "darwin" && mode === "boot")
|
|
324
|
+
execute(["sudo", "rm", "-f", spec.path]);
|
|
325
|
+
else
|
|
326
|
+
rmSync(spec.path);
|
|
327
|
+
confirmStartupRemoval(spec);
|
|
328
|
+
}
|
|
329
|
+
if (spec) {
|
|
330
|
+
atomicJson(settingsPath(), { startup: "off" });
|
|
331
|
+
}
|
|
332
|
+
for (let i = 0; owner && processExists(owner.pid); i++) {
|
|
333
|
+
if (i >= 150)
|
|
334
|
+
throw new Error("Gateway shutdown could not be confirmed after 30 seconds. Profile retained.");
|
|
335
|
+
await sleep(200);
|
|
336
|
+
}
|
|
337
|
+
const remaining = readGatewayStatus();
|
|
338
|
+
const remainingLock = readGatewayStatus(ownerPath);
|
|
339
|
+
if ((remaining && processExists(remaining.pid)) || (remainingLock && processExists(remainingLock.pid)))
|
|
340
|
+
throw new Error("Gateway ownership changed during teardown. Profile retained.");
|
|
341
|
+
}
|
|
342
|
+
/** Refresh and verify absence without unregistering an unverified startup entry. */
|
|
343
|
+
function confirmStartupRemoval(spec) {
|
|
344
|
+
if (process.platform === "linux") {
|
|
345
|
+
execute(["systemctl", "--user", "daemon-reload"]);
|
|
346
|
+
const removed = serviceQuery([
|
|
347
|
+
"systemctl",
|
|
348
|
+
"--user",
|
|
349
|
+
"show",
|
|
350
|
+
spec.verify.at(-1),
|
|
351
|
+
"--property=LoadState",
|
|
352
|
+
"--value",
|
|
353
|
+
]);
|
|
354
|
+
if (removed.status !== 0 || removed.stdout.trim() !== "not-found")
|
|
355
|
+
throw new Error("Gateway startup removal could not be confirmed. Profile retained.");
|
|
356
|
+
}
|
|
357
|
+
else if (process.platform === "darwin") {
|
|
358
|
+
const removed = serviceQuery(spec.verify);
|
|
359
|
+
if (removed.status === 0 || !/Could not find (?:specified )?service/i.test(removed.stderr))
|
|
360
|
+
throw new Error("Gateway launchd removal could not be confirmed. Profile retained.");
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const name = `${servicePrefix}-${createHash("sha256").update(resolve(getAgentDir())).digest("hex").slice(0, 10)}`;
|
|
364
|
+
execute([
|
|
365
|
+
"powershell.exe",
|
|
366
|
+
"-NoProfile",
|
|
367
|
+
"-Command",
|
|
368
|
+
`$ErrorActionPreference='Stop'; if(Get-ScheduledTask | Where-Object {$_.TaskName -eq ${ps(name)}}){throw 'Gateway task still registered'}`,
|
|
369
|
+
]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function serviceQuery(command) {
|
|
373
|
+
const [file, ...args] = command;
|
|
374
|
+
const result = spawnSync(file, args, { encoding: "utf8", windowsHide: true, timeout: 120_000 });
|
|
375
|
+
if (result.error)
|
|
376
|
+
throw result.error;
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
226
379
|
function xml(value) {
|
|
227
380
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
228
381
|
}
|