rird 2.1.231 → 2.3.0
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/AGENTS.md +86 -0
- package/COMPLETED_TEST_SUITE.txt +280 -0
- package/Dockerfile +18 -0
- package/README.md +397 -6
- package/RIRD_ERROR_HANDLING_SUMMARY.md +307 -0
- package/TESTING.md +512 -0
- package/TEST_IMPLEMENTATION_REPORT.md +463 -0
- package/TEST_SUITE.md +307 -0
- package/TEST_SUMMARY.txt +380 -0
- package/bin/rird-perf.js +37 -0
- package/bin/rird.js +43 -8
- package/bunfig.toml +4 -0
- package/create-wrapper.ps1 +51 -0
- package/docs/ARCHITECTURE.md +768 -0
- package/docs/CLI_REFERENCE.md +681 -0
- package/docs/DOCUMENTATION_MANIFEST.md +392 -0
- package/docs/INDEX.md +295 -0
- package/docs/PRODUCTION_SETUP.md +633 -0
- package/docs/TROUBLESHOOTING.md +914 -0
- package/facebook_ads_library.png +0 -0
- package/nul +0 -0
- package/nul`nif +0 -0
- package/package.json +104 -15
- package/parsers-config.ts +239 -0
- package/rird-1.0.199.tgz +0 -0
- package/rird-1.0.205.tgz +0 -0
- package/script/build-windows.ts +56 -0
- package/script/build.ts +165 -0
- package/{postinstall.mjs → script/postinstall.mjs} +47 -68
- package/script/publish-registries.ts +187 -0
- package/script/publish.ts +85 -0
- package/script/schema.ts +47 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1063 -0
- package/src/acp/session.ts +101 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +367 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +12 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +10 -0
- package/src/agent/prompt/title.txt +36 -0
- package/src/auth/index.ts +70 -0
- package/src/bun/index.ts +114 -0
- package/src/bus/bus-event.ts +43 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +105 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +104 -0
- package/src/cli/cmd/activate.ts +50 -0
- package/src/cli/cmd/agent.ts +256 -0
- package/src/cli/cmd/auth.ts +412 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/debug/config.ts +15 -0
- package/src/cli/cmd/debug/file.ts +91 -0
- package/src/cli/cmd/debug/index.ts +43 -0
- package/src/cli/cmd/debug/lsp.ts +48 -0
- package/src/cli/cmd/debug/ripgrep.ts +83 -0
- package/src/cli/cmd/debug/scrap.ts +15 -0
- package/src/cli/cmd/debug/skill.ts +15 -0
- package/src/cli/cmd/debug/snapshot.ts +48 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1400 -0
- package/src/cli/cmd/import.ts +98 -0
- package/src/cli/cmd/mcp.ts +654 -0
- package/src/cli/cmd/models.ts +68 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +434 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +106 -0
- package/src/cli/cmd/stats.ts +298 -0
- package/src/cli/cmd/tui/app.tsx +694 -0
- package/src/cli/cmd/tui/attach.ts +30 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +236 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +240 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
- package/src/cli/cmd/tui/component/logo.tsx +48 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1087 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/tips.ts +27 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +23 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +101 -0
- package/src/cli/cmd/tui/context/kv.tsx +49 -0
- package/src/cli/cmd/tui/context/local.tsx +345 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +46 -0
- package/src/cli/cmd/tui/context/sdk.tsx +74 -0
- package/src/cli/cmd/tui/context/sync.tsx +372 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +245 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rird.json +245 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1109 -0
- package/src/cli/cmd/tui/event.ts +40 -0
- package/src/cli/cmd/tui/hooks/use-safe-terminal-dimensions.ts +12 -0
- package/src/cli/cmd/tui/routes/home.tsx +138 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +1876 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +320 -0
- package/src/cli/cmd/tui/spawn.ts +60 -0
- package/src/cli/cmd/tui/thread.ts +142 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +333 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +171 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +127 -0
- package/src/cli/cmd/tui/util/editor.ts +32 -0
- package/src/cli/cmd/tui/util/terminal.ts +146 -0
- package/src/cli/cmd/tui/worker.ts +63 -0
- package/src/cli/cmd/uninstall.ts +344 -0
- package/src/cli/cmd/upgrade.ts +127 -0
- package/src/cli/cmd/web.ts +84 -0
- package/src/cli/error.ts +69 -0
- package/src/cli/ui.ts +101 -0
- package/src/cli/upgrade.ts +28 -0
- package/src/command/index.ts +80 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +97 -0
- package/src/config/config.ts +994 -0
- package/src/config/markdown.ts +41 -0
- package/src/env/index.ts +26 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +328 -0
- package/src/file/ripgrep.ts +393 -0
- package/src/file/time.ts +64 -0
- package/src/file/watcher.ts +103 -0
- package/src/flag/flag.ts +84 -0
- package/src/format/formatter.ts +315 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +101 -0
- package/src/id/id.ts +73 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +297 -0
- package/src/index.ts.backup +271 -0
- package/src/installation/index.ts +258 -0
- package/src/lib/IMPLEMENTATION_NOTES.md +345 -0
- package/src/lib/error-handler.ts +225 -0
- package/src/lib/error-testing-guide.md +258 -0
- package/src/lib/errors.ts +285 -0
- package/src/lib/performance.ts +70 -0
- package/src/lib/telemetry.ts +282 -0
- package/src/lsp/client.ts +229 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +116 -0
- package/src/lsp/server.ts +1895 -0
- package/src/mcp/auth.ts +135 -0
- package/src/mcp/index.ts +1117 -0
- package/src/mcp/intent-analyzer.ts +376 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +154 -0
- package/src/patch/index.ts +632 -0
- package/src/permission/index.ts +199 -0
- package/src/plugin/index.ts +91 -0
- package/src/project/bootstrap.ts +33 -0
- package/src/project/instance.ts +78 -0
- package/src/project/project.ts +236 -0
- package/src/project/state.ts +65 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +143 -0
- package/src/provider/models-macro.ts +55 -0
- package/src/provider/models.ts +161 -0
- package/src/provider/provider.ts +1109 -0
- package/src/provider/sdk/openai-compatible/src/README.md +5 -0
- package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
- package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
- package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +455 -0
- package/src/pty/index.ts +231 -0
- package/src/security/guardrails.test.ts +341 -0
- package/src/security/guardrails.ts +570 -0
- package/src/security/index.ts +19 -0
- package/src/server/error.ts +36 -0
- package/src/server/project.ts +79 -0
- package/src/server/server.ts +2641 -0
- package/src/server/tui.ts +71 -0
- package/src/session/compaction.ts +228 -0
- package/src/session/index.ts +464 -0
- package/src/session/llm.ts +201 -0
- package/src/session/message-v2.ts +695 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +409 -0
- package/src/session/prompt/act-switch.txt +5 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +63 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +76 -0
- package/src/session/prompt/codex.txt +304 -0
- package/src/session/prompt/copilot-gpt-5.txt +137 -0
- package/src/session/prompt/gemini.txt +62 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
- package/src/session/prompt/plan.txt +24 -0
- package/src/session/prompt/polaris.txt +88 -0
- package/src/session/prompt/qwen.txt +59 -0
- package/src/session/prompt.ts +1552 -0
- package/src/session/retry.ts +86 -0
- package/src/session/revert.ts +108 -0
- package/src/session/sensitive-filter.test.ts +327 -0
- package/src/session/sensitive-filter.ts +466 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +209 -0
- package/src/session/system.ts +122 -0
- package/src/session/todo.ts +37 -0
- package/src/share/share-next.ts +222 -0
- package/src/share/share.ts +87 -0
- package/src/shell/shell.ts +67 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +83 -0
- package/src/snapshot/index.ts +197 -0
- package/src/storage/storage.ts +226 -0
- package/src/tests/agent.test.ts +308 -0
- package/src/tests/build-guards.test.ts +267 -0
- package/src/tests/config.test.ts +664 -0
- package/src/tests/tool-registry.test.ts +589 -0
- package/src/tool/bash.ts +314 -0
- package/src/tool/bash.txt +158 -0
- package/src/tool/batch.ts +175 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +184 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +675 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/glob.ts +65 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +121 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +110 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp-diagnostics.ts +26 -0
- package/src/tool/lsp-diagnostics.txt +1 -0
- package/src/tool/lsp-hover.ts +31 -0
- package/src/tool/lsp-hover.txt +1 -0
- package/src/tool/lsp.ts +87 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/patch.ts +233 -0
- package/src/tool/patch.txt +1 -0
- package/src/tool/read.ts +219 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +162 -0
- package/src/tool/skill.ts +100 -0
- package/src/tool/task.ts +136 -0
- package/src/tool/task.txt +51 -0
- package/src/tool/todo.ts +39 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +71 -0
- package/src/tool/webfetch.ts +198 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +268 -0
- package/src/tool/websearch.txt +13 -0
- package/src/tool/write.ts +110 -0
- package/src/tool/write.txt +8 -0
- package/src/util/archive.ts +16 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/defer.ts +12 -0
- package/src/util/eventloop.ts +20 -0
- package/src/util/filesystem.ts +83 -0
- package/src/util/fn.ts +11 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +102 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/license.ts +362 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +180 -0
- package/src/util/queue.ts +32 -0
- package/src/util/rpc.ts +42 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/wildcard.ts +54 -0
- package/sst-env.d.ts +9 -0
- package/test/agent/agent.test.ts +146 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/cmd/acp.test.ts +144 -0
- package/test/cli/cmd/run.test.ts +250 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/config/agent-color.test.ts +66 -0
- package/test/config/config.test.ts +536 -0
- package/test/config/markdown.test.ts +89 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/fixture/fixture.ts +37 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/helpers.ts +172 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/installation/installation.test.ts +143 -0
- package/test/keybind.test.ts +421 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/preload.ts +57 -0
- package/test/project/project.test.ts +74 -0
- package/test/provider/provider.test.ts +74 -0
- package/test/provider/transform.test.ts +411 -0
- package/test/session/retry.test.ts +111 -0
- package/test/session/session.test.ts +71 -0
- package/test/skill/skill.test.ts +131 -0
- package/test/snapshot/snapshot.test.ts +940 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/bash.test.ts +434 -0
- package/test/tool/grep.test.ts +108 -0
- package/test/tool/patch.test.ts +259 -0
- package/test/tool/read.test.ts +42 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/license.test.ts +235 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +55 -0
- package/tsconfig.json +16 -0
- package/update-versions.ps1 +65 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent-to-Section Analyzer for automatic MCP tool loading
|
|
3
|
+
*
|
|
4
|
+
* Analyzes user prompts to determine which MCP tool sections should be loaded.
|
|
5
|
+
* This enables lazy loading of MCP tools based on task requirements.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mapping of intent keywords to MCP sections that should be loaded.
|
|
10
|
+
* Each keyword maps to an array of section names.
|
|
11
|
+
*/
|
|
12
|
+
const INTENT_SECTIONS: Record<string, string[]> = {
|
|
13
|
+
// Data extraction intents
|
|
14
|
+
scrape: ["element-extraction", "file-extraction"],
|
|
15
|
+
extract: ["element-extraction", "file-extraction"],
|
|
16
|
+
parse: ["element-extraction", "file-extraction"],
|
|
17
|
+
find: ["element-extraction"],
|
|
18
|
+
search: ["element-interaction", "element-extraction", "browser-management"],
|
|
19
|
+
collect: ["element-extraction", "element-interaction", "browser-management"],
|
|
20
|
+
output: ["element-extraction"],
|
|
21
|
+
download: ["file-extraction"],
|
|
22
|
+
save: ["file-extraction"],
|
|
23
|
+
export: ["file-extraction"],
|
|
24
|
+
|
|
25
|
+
// Form/interaction intents
|
|
26
|
+
form: ["element-interaction", "cookies-storage"],
|
|
27
|
+
fill: ["element-interaction"],
|
|
28
|
+
click: ["element-interaction"],
|
|
29
|
+
"type in": ["element-interaction"],
|
|
30
|
+
"type into": ["element-interaction"],
|
|
31
|
+
typing: ["element-interaction"],
|
|
32
|
+
input: ["element-interaction"],
|
|
33
|
+
submit: ["element-interaction"],
|
|
34
|
+
select: ["element-interaction"],
|
|
35
|
+
press: ["element-interaction"],
|
|
36
|
+
toggle: ["element-interaction"],
|
|
37
|
+
drag: ["element-interaction"],
|
|
38
|
+
drop: ["element-interaction"],
|
|
39
|
+
scroll: ["element-interaction"],
|
|
40
|
+
hover: ["element-interaction"],
|
|
41
|
+
interact: ["element-interaction"],
|
|
42
|
+
|
|
43
|
+
// Debugging intents
|
|
44
|
+
debug: ["debugging", "network-debugging"],
|
|
45
|
+
inspect: ["debugging", "network-debugging", "cdp-functions"],
|
|
46
|
+
analyze: ["debugging", "network-debugging"],
|
|
47
|
+
troubleshoot: ["debugging", "network-debugging"],
|
|
48
|
+
diagnose: ["debugging", "network-debugging"],
|
|
49
|
+
|
|
50
|
+
// Network intents
|
|
51
|
+
network: ["network-debugging", "dynamic-hooks"],
|
|
52
|
+
intercept: ["dynamic-hooks", "network-debugging"],
|
|
53
|
+
request: ["network-debugging", "dynamic-hooks"],
|
|
54
|
+
response: ["network-debugging", "dynamic-hooks"],
|
|
55
|
+
traffic: ["network-debugging", "dynamic-hooks"],
|
|
56
|
+
api: ["network-debugging", "dynamic-hooks"],
|
|
57
|
+
xhr: ["network-debugging", "dynamic-hooks"],
|
|
58
|
+
fetch: ["network-debugging", "dynamic-hooks"],
|
|
59
|
+
websocket: ["network-debugging", "dynamic-hooks"],
|
|
60
|
+
|
|
61
|
+
// CDP/JavaScript intents
|
|
62
|
+
cdp: ["cdp-functions"],
|
|
63
|
+
javascript: ["cdp-functions"],
|
|
64
|
+
js: ["cdp-functions"],
|
|
65
|
+
eval: ["cdp-functions"],
|
|
66
|
+
execute: ["cdp-functions"],
|
|
67
|
+
console: ["cdp-functions", "debugging"],
|
|
68
|
+
script: ["cdp-functions"],
|
|
69
|
+
|
|
70
|
+
// Tab management intents
|
|
71
|
+
tabs: ["tabs"],
|
|
72
|
+
tab: ["tabs"],
|
|
73
|
+
window: ["tabs"],
|
|
74
|
+
browser: ["browser-management", "tabs"],
|
|
75
|
+
spawn: ["browser-management"],
|
|
76
|
+
start: ["browser-management"],
|
|
77
|
+
|
|
78
|
+
// Persistence/Memory intents
|
|
79
|
+
memory: ["persistence"],
|
|
80
|
+
history: ["persistence"],
|
|
81
|
+
knowledge: ["persistence"],
|
|
82
|
+
checkpoint: ["persistence"],
|
|
83
|
+
recall: ["persistence"],
|
|
84
|
+
remember: ["persistence"],
|
|
85
|
+
|
|
86
|
+
// Cookie/storage intents
|
|
87
|
+
cookie: ["cookies-storage"],
|
|
88
|
+
cookies: ["cookies-storage"],
|
|
89
|
+
storage: ["cookies-storage"],
|
|
90
|
+
localstorage: ["cookies-storage"],
|
|
91
|
+
sessionstorage: ["cookies-storage"],
|
|
92
|
+
cache: ["cookies-storage"],
|
|
93
|
+
|
|
94
|
+
// Cloning intents
|
|
95
|
+
clone: ["progressive-cloning", "element-extraction"],
|
|
96
|
+
copy: ["progressive-cloning", "element-extraction"],
|
|
97
|
+
mirror: ["progressive-cloning", "element-extraction"],
|
|
98
|
+
replicate: ["progressive-cloning", "element-extraction"],
|
|
99
|
+
|
|
100
|
+
// File-specific intents
|
|
101
|
+
pdf: ["file-extraction"],
|
|
102
|
+
screenshot: ["element-interaction"],
|
|
103
|
+
image: ["element-extraction", "file-extraction"],
|
|
104
|
+
video: ["file-extraction"],
|
|
105
|
+
audio: ["file-extraction"],
|
|
106
|
+
document: ["file-extraction"],
|
|
107
|
+
|
|
108
|
+
// Authentication intents
|
|
109
|
+
login: ["element-interaction", "cookies-storage"],
|
|
110
|
+
"log in": ["element-interaction", "cookies-storage"],
|
|
111
|
+
"log into": ["element-interaction", "cookies-storage"],
|
|
112
|
+
"logged in": ["element-interaction", "cookies-storage"],
|
|
113
|
+
"logging in": ["element-interaction", "cookies-storage"],
|
|
114
|
+
signin: ["element-interaction", "cookies-storage"],
|
|
115
|
+
"sign in": ["element-interaction", "cookies-storage"],
|
|
116
|
+
"sign into": ["element-interaction", "cookies-storage"],
|
|
117
|
+
auth: ["element-interaction", "cookies-storage"],
|
|
118
|
+
authenticate: ["element-interaction", "cookies-storage"],
|
|
119
|
+
logout: ["element-interaction", "cookies-storage"],
|
|
120
|
+
|
|
121
|
+
// Table/data intents
|
|
122
|
+
table: ["element-extraction"],
|
|
123
|
+
list: ["element-extraction"],
|
|
124
|
+
data: ["element-extraction", "file-extraction"],
|
|
125
|
+
record: ["element-extraction"],
|
|
126
|
+
url: ["element-extraction"],
|
|
127
|
+
link: ["element-extraction"],
|
|
128
|
+
profile: ["element-extraction"],
|
|
129
|
+
lead: ["element-extraction", "element-interaction"],
|
|
130
|
+
prospect: ["element-extraction", "element-interaction"],
|
|
131
|
+
client: ["element-extraction", "element-interaction"],
|
|
132
|
+
advertiser: ["element-extraction", "element-interaction"],
|
|
133
|
+
ad: ["element-extraction", "element-interaction"],
|
|
134
|
+
listing: ["element-extraction", "element-interaction"],
|
|
135
|
+
|
|
136
|
+
// RIRD brand intents
|
|
137
|
+
rird: ["browser-management", "element-interaction", "element-extraction"],
|
|
138
|
+
|
|
139
|
+
// Site-specific intents
|
|
140
|
+
facebook: ["browser-management", "element-interaction", "element-extraction"],
|
|
141
|
+
fb: ["browser-management", "element-interaction", "element-extraction"],
|
|
142
|
+
instagram: ["browser-management", "element-interaction", "element-extraction"],
|
|
143
|
+
ig: ["browser-management", "element-interaction", "element-extraction"],
|
|
144
|
+
linkedin: ["browser-management", "element-interaction", "element-extraction"],
|
|
145
|
+
reddit: ["browser-management", "element-interaction", "element-extraction"],
|
|
146
|
+
twitter: ["browser-management", "element-interaction", "element-extraction"],
|
|
147
|
+
x: ["browser-management", "element-interaction", "element-extraction"],
|
|
148
|
+
maps: ["browser-management", "element-interaction", "element-extraction"],
|
|
149
|
+
google: ["browser-management", "element-interaction"],
|
|
150
|
+
amazon: ["browser-management", "element-interaction", "element-extraction"],
|
|
151
|
+
lib: ["browser-management", "element-interaction", "element-extraction"],
|
|
152
|
+
library: ["browser-management", "element-interaction", "element-extraction"],
|
|
153
|
+
thread: ["browser-management", "element-interaction", "element-extraction"],
|
|
154
|
+
post: ["browser-management", "element-interaction", "element-extraction"],
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Keywords that indicate a simple navigation-only task (layer0 only).
|
|
159
|
+
* These patterns suggest no advanced MCP tools are needed.
|
|
160
|
+
*/
|
|
161
|
+
const SIMPLE_TASK_PATTERNS: RegExp[] = [
|
|
162
|
+
/^(go\s+to|open|visit|navigate\s+to|load)\s+/i,
|
|
163
|
+
/^(show|display|view)\s+(me\s+)?(the\s+)?/i,
|
|
164
|
+
/^what('s|\s+is)\s+(on|at)\s+/i,
|
|
165
|
+
/^(take\s+me\s+to|bring\s+up)\s+/i,
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* URLs or simple navigation patterns
|
|
170
|
+
*/
|
|
171
|
+
const URL_PATTERN = /^https?:\/\/[^\s]+$/i
|
|
172
|
+
const DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/i
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The core/base section that is always loaded
|
|
176
|
+
*/
|
|
177
|
+
export const LAYER0_SECTION = "layer0"
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Result of intent analysis
|
|
181
|
+
*/
|
|
182
|
+
export interface IntentAnalysisResult {
|
|
183
|
+
/** Sections to load (always includes layer0) */
|
|
184
|
+
sections: string[]
|
|
185
|
+
/** Keywords that were matched in the prompt */
|
|
186
|
+
matchedKeywords: string[]
|
|
187
|
+
/** Whether this is a simple task (layer0 only) */
|
|
188
|
+
isSimple: boolean
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Analyzes a user prompt to determine which MCP sections should be loaded.
|
|
193
|
+
*
|
|
194
|
+
* @param prompt - The user's task description or prompt
|
|
195
|
+
* @returns List of section names to load (always includes layer0, deduplicated)
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* analyzeIntent("scrape all product prices from the page")
|
|
199
|
+
* // Returns: ["layer0", "element-extraction", "file-extraction"]
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* analyzeIntent("go to google.com")
|
|
203
|
+
* // Returns: ["layer0"]
|
|
204
|
+
*/
|
|
205
|
+
export function analyzeIntent(prompt: string): string[] {
|
|
206
|
+
const result = analyzeIntentDetailed(prompt)
|
|
207
|
+
return result.sections
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Detailed intent analysis with additional metadata.
|
|
212
|
+
*
|
|
213
|
+
* @param prompt - The user's task description or prompt
|
|
214
|
+
* @returns Detailed analysis result including matched keywords
|
|
215
|
+
*/
|
|
216
|
+
export function analyzeIntentDetailed(prompt: string): IntentAnalysisResult {
|
|
217
|
+
// Check if it's a simple task first
|
|
218
|
+
if (isSimpleTask(prompt)) {
|
|
219
|
+
return {
|
|
220
|
+
sections: [LAYER0_SECTION],
|
|
221
|
+
matchedKeywords: [],
|
|
222
|
+
isSimple: true,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Guard against null/undefined prompt
|
|
227
|
+
if (!prompt || typeof prompt !== "string") {
|
|
228
|
+
return {
|
|
229
|
+
sections: [LAYER0_SECTION],
|
|
230
|
+
matchedKeywords: [],
|
|
231
|
+
isSimple: true,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const normalizedPrompt = prompt.toLowerCase()
|
|
236
|
+
const sections = new Set<string>([LAYER0_SECTION])
|
|
237
|
+
const matchedKeywords: string[] = []
|
|
238
|
+
|
|
239
|
+
// Check each intent keyword
|
|
240
|
+
for (const [keyword, sectionList] of Object.entries(INTENT_SECTIONS)) {
|
|
241
|
+
// Use word boundary matching to avoid partial matches
|
|
242
|
+
const pattern = new RegExp(`\\b${escapeRegex(keyword)}\\b`, "i")
|
|
243
|
+
if (pattern.test(normalizedPrompt)) {
|
|
244
|
+
matchedKeywords.push(keyword)
|
|
245
|
+
for (const section of sectionList) {
|
|
246
|
+
sections.add(section)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
sections: Array.from(sections),
|
|
253
|
+
matchedKeywords,
|
|
254
|
+
isSimple: sections.size === 1,
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Checks if a prompt describes a simple task that only needs layer0 (core) capabilities.
|
|
260
|
+
* Simple tasks are basic navigation operations like "go to google.com" or "open https://example.com".
|
|
261
|
+
*
|
|
262
|
+
* @param prompt - The user's task description or prompt
|
|
263
|
+
* @returns True if the task only needs layer0 capabilities
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* isSimpleTask("go to google.com") // true
|
|
267
|
+
* isSimpleTask("open https://example.com") // true
|
|
268
|
+
* isSimpleTask("scrape all products from the page") // false
|
|
269
|
+
*/
|
|
270
|
+
export function isSimpleTask(prompt: string): boolean {
|
|
271
|
+
const trimmedPrompt = prompt.trim()
|
|
272
|
+
|
|
273
|
+
// Check if it's just a URL
|
|
274
|
+
if (URL_PATTERN.test(trimmedPrompt) || DOMAIN_PATTERN.test(trimmedPrompt)) {
|
|
275
|
+
return true
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check simple task patterns
|
|
279
|
+
for (const pattern of SIMPLE_TASK_PATTERNS) {
|
|
280
|
+
if (pattern.test(trimmedPrompt)) {
|
|
281
|
+
// Extract what comes after the pattern
|
|
282
|
+
const remainder = trimmedPrompt.replace(pattern, "").trim()
|
|
283
|
+
|
|
284
|
+
// If remainder is just a URL or domain, it's simple
|
|
285
|
+
if (URL_PATTERN.test(remainder) || DOMAIN_PATTERN.test(remainder)) {
|
|
286
|
+
return true
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// If remainder is very short (likely just a site name), it's simple
|
|
290
|
+
if (remainder.length < 50 && !containsComplexIntent(remainder)) {
|
|
291
|
+
return true
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Gets all available section names from the intent mapping.
|
|
301
|
+
*
|
|
302
|
+
* @returns Array of unique section names
|
|
303
|
+
*/
|
|
304
|
+
export function getAvailableSections(): string[] {
|
|
305
|
+
const sections = new Set<string>([LAYER0_SECTION])
|
|
306
|
+
for (const sectionList of Object.values(INTENT_SECTIONS)) {
|
|
307
|
+
for (const section of sectionList) {
|
|
308
|
+
sections.add(section)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return Array.from(sections).sort()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Gets all intent keywords that map to a specific section.
|
|
316
|
+
*
|
|
317
|
+
* @param section - The section name to look up
|
|
318
|
+
* @returns Array of keywords that trigger loading of this section
|
|
319
|
+
*/
|
|
320
|
+
export function getKeywordsForSection(section: string): string[] {
|
|
321
|
+
const keywords: string[] = []
|
|
322
|
+
for (const [keyword, sectionList] of Object.entries(INTENT_SECTIONS)) {
|
|
323
|
+
if (sectionList.includes(section)) {
|
|
324
|
+
keywords.push(keyword)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return keywords.sort()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Gets the raw intent-to-section mapping for external use.
|
|
332
|
+
*
|
|
333
|
+
* @returns Copy of the intent sections mapping
|
|
334
|
+
*/
|
|
335
|
+
export function getIntentSectionsMapping(): Record<string, string[]> {
|
|
336
|
+
return { ...INTENT_SECTIONS }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Helper functions
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Escapes special regex characters in a string
|
|
343
|
+
*/
|
|
344
|
+
function escapeRegex(str: string): string {
|
|
345
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Checks if text contains keywords suggesting a complex task
|
|
350
|
+
*/
|
|
351
|
+
function containsComplexIntent(text: string): boolean {
|
|
352
|
+
const normalizedText = text.toLowerCase()
|
|
353
|
+
|
|
354
|
+
// Check for any intent keywords
|
|
355
|
+
for (const keyword of Object.keys(INTENT_SECTIONS)) {
|
|
356
|
+
const pattern = new RegExp(`\\b${escapeRegex(keyword)}\\b`, "i")
|
|
357
|
+
if (pattern.test(normalizedText)) {
|
|
358
|
+
return true
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Check for action words that suggest complexity
|
|
363
|
+
const complexPatterns = [
|
|
364
|
+
/\band\s+then\b/i,
|
|
365
|
+
/\bafter\s+that\b/i,
|
|
366
|
+
/\bfor\s+each\b/i,
|
|
367
|
+
/\ball\s+(the\s+)?/i,
|
|
368
|
+
/\bevery\s+/i,
|
|
369
|
+
/\bmultiple\b/i,
|
|
370
|
+
/\brepeat\b/i,
|
|
371
|
+
/\bloop\b/i,
|
|
372
|
+
/\bwait\s+(for|until)\b/i,
|
|
373
|
+
]
|
|
374
|
+
|
|
375
|
+
return complexPatterns.some((pattern) => pattern.test(normalizedText))
|
|
376
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Log } from "../util/log"
|
|
2
|
+
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH } from "./oauth-provider"
|
|
3
|
+
|
|
4
|
+
const log = Log.create({ service: "mcp.oauth-callback" })
|
|
5
|
+
|
|
6
|
+
const HTML_SUCCESS = `<!DOCTYPE html>
|
|
7
|
+
<html>
|
|
8
|
+
<head>
|
|
9
|
+
<title>RIRD - Authorization Successful</title>
|
|
10
|
+
<style>
|
|
11
|
+
body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1a1a2e; color: #eee; }
|
|
12
|
+
.container { text-align: center; padding: 2rem; }
|
|
13
|
+
h1 { color: #4ade80; margin-bottom: 1rem; }
|
|
14
|
+
p { color: #aaa; }
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<div class="container">
|
|
19
|
+
<h1>Authorization Successful</h1>
|
|
20
|
+
<p>You can close this window and return to RIRD.</p>
|
|
21
|
+
</div>
|
|
22
|
+
<script>setTimeout(() => window.close(), 2000);</script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>`
|
|
25
|
+
|
|
26
|
+
const HTML_ERROR = (error: string) => `<!DOCTYPE html>
|
|
27
|
+
<html>
|
|
28
|
+
<head>
|
|
29
|
+
<title>RIRD - Authorization Failed</title>
|
|
30
|
+
<style>
|
|
31
|
+
body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1a1a2e; color: #eee; }
|
|
32
|
+
.container { text-align: center; padding: 2rem; }
|
|
33
|
+
h1 { color: #f87171; margin-bottom: 1rem; }
|
|
34
|
+
p { color: #aaa; }
|
|
35
|
+
.error { color: #fca5a5; font-family: monospace; margin-top: 1rem; padding: 1rem; background: rgba(248,113,113,0.1); border-radius: 0.5rem; }
|
|
36
|
+
</style>
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<div class="container">
|
|
40
|
+
<h1>Authorization Failed</h1>
|
|
41
|
+
<p>An error occurred during authorization.</p>
|
|
42
|
+
<div class="error">${error}</div>
|
|
43
|
+
</div>
|
|
44
|
+
</body>
|
|
45
|
+
</html>`
|
|
46
|
+
|
|
47
|
+
interface PendingAuth {
|
|
48
|
+
resolve: (code: string) => void
|
|
49
|
+
reject: (error: Error) => void
|
|
50
|
+
timeout: ReturnType<typeof setTimeout>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export namespace McpOAuthCallback {
|
|
54
|
+
let server: ReturnType<typeof Bun.serve> | undefined
|
|
55
|
+
const pendingAuths = new Map<string, PendingAuth>()
|
|
56
|
+
|
|
57
|
+
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes
|
|
58
|
+
|
|
59
|
+
export async function ensureRunning(): Promise<void> {
|
|
60
|
+
if (server) return
|
|
61
|
+
|
|
62
|
+
const running = await isPortInUse()
|
|
63
|
+
if (running) {
|
|
64
|
+
log.info("oauth callback server already running on another instance", { port: OAUTH_CALLBACK_PORT })
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
server = Bun.serve({
|
|
69
|
+
port: OAUTH_CALLBACK_PORT,
|
|
70
|
+
fetch(req) {
|
|
71
|
+
const url = new URL(req.url)
|
|
72
|
+
|
|
73
|
+
if (url.pathname !== OAUTH_CALLBACK_PATH) {
|
|
74
|
+
return new Response("Not found", { status: 404 })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const code = url.searchParams.get("code")
|
|
78
|
+
const state = url.searchParams.get("state")
|
|
79
|
+
const error = url.searchParams.get("error")
|
|
80
|
+
const errorDescription = url.searchParams.get("error_description")
|
|
81
|
+
|
|
82
|
+
log.info("received oauth callback", { hasCode: !!code, state, error })
|
|
83
|
+
|
|
84
|
+
// Enforce state parameter presence
|
|
85
|
+
if (!state) {
|
|
86
|
+
const errorMsg = "Missing required state parameter - potential CSRF attack"
|
|
87
|
+
log.error("oauth callback missing state parameter", { url: url.toString() })
|
|
88
|
+
return new Response(HTML_ERROR(errorMsg), {
|
|
89
|
+
status: 400,
|
|
90
|
+
headers: { "Content-Type": "text/html" },
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (error) {
|
|
95
|
+
const errorMsg = errorDescription || error
|
|
96
|
+
if (pendingAuths.has(state)) {
|
|
97
|
+
const pending = pendingAuths.get(state)!
|
|
98
|
+
clearTimeout(pending.timeout)
|
|
99
|
+
pendingAuths.delete(state)
|
|
100
|
+
pending.reject(new Error(errorMsg))
|
|
101
|
+
}
|
|
102
|
+
return new Response(HTML_ERROR(errorMsg), {
|
|
103
|
+
headers: { "Content-Type": "text/html" },
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!code) {
|
|
108
|
+
return new Response(HTML_ERROR("No authorization code provided"), {
|
|
109
|
+
status: 400,
|
|
110
|
+
headers: { "Content-Type": "text/html" },
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Validate state parameter
|
|
115
|
+
if (!pendingAuths.has(state)) {
|
|
116
|
+
const errorMsg = "Invalid or expired state parameter - potential CSRF attack"
|
|
117
|
+
log.error("oauth callback with invalid state", { state, pendingStates: Array.from(pendingAuths.keys()) })
|
|
118
|
+
return new Response(HTML_ERROR(errorMsg), {
|
|
119
|
+
status: 400,
|
|
120
|
+
headers: { "Content-Type": "text/html" },
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const pending = pendingAuths.get(state)!
|
|
125
|
+
|
|
126
|
+
clearTimeout(pending.timeout)
|
|
127
|
+
pendingAuths.delete(state)
|
|
128
|
+
pending.resolve(code)
|
|
129
|
+
|
|
130
|
+
return new Response(HTML_SUCCESS, {
|
|
131
|
+
headers: { "Content-Type": "text/html" },
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
log.info("oauth callback server started", { port: OAUTH_CALLBACK_PORT })
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function waitForCallback(oauthState: string): Promise<string> {
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const timeout = setTimeout(() => {
|
|
142
|
+
if (pendingAuths.has(oauthState)) {
|
|
143
|
+
pendingAuths.delete(oauthState)
|
|
144
|
+
reject(new Error("OAuth callback timeout - authorization took too long"))
|
|
145
|
+
}
|
|
146
|
+
}, CALLBACK_TIMEOUT_MS)
|
|
147
|
+
|
|
148
|
+
pendingAuths.set(oauthState, { resolve, reject, timeout })
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function cancelPending(mcpName: string): void {
|
|
153
|
+
const pending = pendingAuths.get(mcpName)
|
|
154
|
+
if (pending) {
|
|
155
|
+
clearTimeout(pending.timeout)
|
|
156
|
+
pendingAuths.delete(mcpName)
|
|
157
|
+
pending.reject(new Error("Authorization cancelled"))
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function isPortInUse(): Promise<boolean> {
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
Bun.connect({
|
|
164
|
+
hostname: "127.0.0.1",
|
|
165
|
+
port: OAUTH_CALLBACK_PORT,
|
|
166
|
+
socket: {
|
|
167
|
+
open(socket) {
|
|
168
|
+
socket.end()
|
|
169
|
+
resolve(true)
|
|
170
|
+
},
|
|
171
|
+
error() {
|
|
172
|
+
resolve(false)
|
|
173
|
+
},
|
|
174
|
+
data() {},
|
|
175
|
+
close() {},
|
|
176
|
+
},
|
|
177
|
+
}).catch(() => {
|
|
178
|
+
resolve(false)
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function stop(): Promise<void> {
|
|
184
|
+
if (server) {
|
|
185
|
+
server.stop()
|
|
186
|
+
server = undefined
|
|
187
|
+
log.info("oauth callback server stopped")
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (const [name, pending] of pendingAuths) {
|
|
191
|
+
clearTimeout(pending.timeout)
|
|
192
|
+
pending.reject(new Error("OAuth callback server stopped"))
|
|
193
|
+
}
|
|
194
|
+
pendingAuths.clear()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function isRunning(): boolean {
|
|
198
|
+
return server !== undefined
|
|
199
|
+
}
|
|
200
|
+
}
|