saeeol 1.2.4 → 1.2.7
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/bin/saeeol.cjs +2 -1
- package/package.json +15 -12
- package/src/ltm/config.ts +15 -15
- package/src/ltm/events.ts +1 -1
- package/src/ltm/index.ts +1 -1
- package/src/ltm/pipeline.ts +23 -23
- package/src/ltm/scheduler.ts +91 -17
- package/src/ltm/store.ts +9 -7
- package/src/ltm/types.ts +15 -15
- package/src/provider/local/embedder.ts +24 -24
- package/src/provider/models-snapshot.d.ts +2 -0
- package/src/provider/models-snapshot.js +3 -0
- package/src/session/message/message-errors.ts +1 -1
- package/src/session/message/message-parts.ts +1 -1
- package/src/session/message/message-transform.ts +1 -1
- package/src/session/message/message-types.ts +1 -1
- package/src/tool/core/tool.ts +1 -1
- package/AGENTS.md +0 -72
- package/BUN_SHELL_MIGRATION_PLAN.md +0 -136
- package/Dockerfile +0 -18
- package/assets/saeeol.ico +0 -0
- package/bin/saeeol +0 -187
- package/bunfig.toml +0 -7
- package/database.db +0 -0
- package/drizzle.config.ts +0 -10
- package/git +0 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +0 -90
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +0 -796
- package/migration/20260211171708_add_project_commands/migration.sql +0 -1
- package/migration/20260211171708_add_project_commands/snapshot.json +0 -806
- package/migration/20260213144116_wakeful_the_professor/migration.sql +0 -11
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +0 -897
- package/migration/20260225215848_workspace/migration.sql +0 -7
- package/migration/20260225215848_workspace/snapshot.json +0 -959
- package/migration/20260227213759_add_session_workspace_id/migration.sql +0 -2
- package/migration/20260227213759_add_session_workspace_id/snapshot.json +0 -983
- package/migration/20260228203230_blue_harpoon/migration.sql +0 -17
- package/migration/20260228203230_blue_harpoon/snapshot.json +0 -1102
- package/migration/20260303231226_add_workspace_fields/migration.sql +0 -5
- package/migration/20260303231226_add_workspace_fields/snapshot.json +0 -1013
- package/migration/20260309230000_move_org_to_state/migration.sql +0 -3
- package/migration/20260309230000_move_org_to_state/snapshot.json +0 -1156
- package/migration/20260312043431_session_message_cursor/migration.sql +0 -4
- package/migration/20260312043431_session_message_cursor/snapshot.json +0 -1168
- package/migration/20260323234822_events/migration.sql +0 -13
- package/migration/20260323234822_events/snapshot.json +0 -1271
- package/migration/20260410174513_workspace-name/migration.sql +0 -16
- package/migration/20260410174513_workspace-name/snapshot.json +0 -1271
- package/migration/20260413175956_chief_energizer/migration.sql +0 -13
- package/migration/20260413175956_chief_energizer/snapshot.json +0 -1399
- package/migration/20260423070820_add_icon_url_override/migration.sql +0 -2
- package/migration/20260423070820_add_icon_url_override/snapshot.json +0 -1409
- package/migration/20260428004200_add_session_path/migration.sql +0 -1
- package/migration/20260428004200_add_session_path/snapshot.json +0 -1419
- package/npm/bin/saeeol +0 -42
- package/npm/package.json +0 -39
- package/npm/postinstall.js +0 -162
- package/parsers-config.ts +0 -289
- package/script/build.ts +0 -393
- package/script/check-migrations.ts +0 -16
- package/script/fix-node-pty.ts +0 -34
- package/script/generate.ts +0 -23
- package/script/postinstall.mjs +0 -189
- package/script/publish.ts +0 -200
- package/script/run-workspace-server +0 -106
- package/script/schema.ts +0 -63
- package/script/test-runner.ts +0 -420
- package/script/time.ts +0 -6
- package/script/trace-imports.ts +0 -153
- package/script/upgrade-opentui.ts +0 -64
- package/scripts/diff-sdk-types.sh +0 -52
- package/specs/effect/facades.md +0 -221
- package/specs/effect/http-api.md +0 -401
- package/specs/effect/instance-context.md +0 -309
- package/specs/effect/loose-ends.md +0 -34
- package/specs/effect/migration.md +0 -299
- package/specs/effect/routes.md +0 -64
- package/specs/effect/schema.md +0 -399
- package/specs/effect/server-package.md +0 -668
- package/specs/effect/tools.md +0 -90
- package/specs/tui-plugins.md +0 -433
- package/specs/v2/api.ts +0 -67
- package/specs/v2/keymappings.md +0 -10
- package/specs/v2/message-shape.md +0 -136
- package/src/tool/apply_patch.txt +0 -33
- package/src/tool/bash.txt +0 -119
- package/src/tool/edit.txt +0 -10
- package/src/tool/glob.txt +0 -6
- package/src/tool/grep.txt +0 -8
- package/src/tool/lsp.txt +0 -24
- package/src/tool/plan-enter.txt +0 -14
- package/src/tool/plan-exit.txt +0 -13
- package/src/tool/question.txt +0 -11
- package/src/tool/read.txt +0 -14
- package/src/tool/recall.txt +0 -12
- package/src/tool/skill.txt +0 -5
- package/src/tool/task.txt +0 -57
- package/src/tool/todowrite.txt +0 -167
- package/src/tool/warpgrep.txt +0 -10
- package/src/tool/webfetch.txt +0 -13
- package/src/tool/websearch.txt +0 -14
- package/src/tool/write.txt +0 -8
- package/sst-env.d.ts +0 -10
- package/test/AGENTS.md +0 -133
- package/test/account/repo.test.ts +0 -352
- package/test/account/service.test.ts +0 -456
- package/test/acp/agent-interface.test.ts +0 -51
- package/test/acp/event-subscription.test.ts +0 -725
- package/test/agent/agent.test.ts +0 -890
- package/test/auth/auth.test.ts +0 -86
- package/test/bun/registry.test.ts +0 -75
- package/test/bus/bus-effect.test.ts +0 -161
- package/test/bus/bus-integration.test.ts +0 -87
- package/test/bus/bus.test.ts +0 -219
- package/test/cli/account.test.ts +0 -26
- package/test/cli/auto-mode.test.ts +0 -75
- package/test/cli/bin-saeeol.test.ts +0 -8
- package/test/cli/cmd/tui/prompt-part.test.ts +0 -47
- package/test/cli/cmd/tui/prompt-traits.test.ts +0 -38
- package/test/cli/cmd/tui/sync.test.tsx +0 -159
- package/test/cli/error.test.ts +0 -18
- package/test/cli/github-action.test.ts +0 -198
- package/test/cli/github-remote.test.ts +0 -85
- package/test/cli/import.test.ts +0 -97
- package/test/cli/install-artifact.test.ts +0 -72
- package/test/cli/plugin-auth-picker.test.ts +0 -120
- package/test/cli/pr.test.ts +0 -59
- package/test/cli/tui/editor-context-zed.test.ts +0 -356
- package/test/cli/tui/editor-context.test.tsx +0 -228
- package/test/cli/tui/keybind-plugin.test.ts +0 -90
- package/test/cli/tui/markdown.test.ts +0 -161
- package/test/cli/tui/plugin-add.test.ts +0 -111
- package/test/cli/tui/plugin-install.test.ts +0 -87
- package/test/cli/tui/plugin-lifecycle.test.ts +0 -224
- package/test/cli/tui/plugin-loader-entrypoint.test.ts +0 -484
- package/test/cli/tui/plugin-loader-pure.test.ts +0 -71
- package/test/cli/tui/plugin-loader.test.ts +0 -816
- package/test/cli/tui/plugin-toggle.test.ts +0 -157
- package/test/cli/tui/revert-diff.test.ts +0 -35
- package/test/cli/tui/slot-replace.test.tsx +0 -47
- package/test/cli/tui/theme-store.test.ts +0 -54
- package/test/cli/tui/thread.test.ts +0 -28
- package/test/cli/tui/transcript.test.ts +0 -426
- package/test/cli/tui/usage.test.ts +0 -60
- package/test/cli/tui/use-event.test.tsx +0 -175
- package/test/config/agent-color.test.ts +0 -67
- package/test/config/config.test.ts +0 -2544
- package/test/config/fixtures/empty-frontmatter.md +0 -4
- package/test/config/fixtures/frontmatter.md +0 -28
- package/test/config/fixtures/markdown-header.md +0 -11
- package/test/config/fixtures/no-frontmatter.md +0 -1
- package/test/config/fixtures/weird-model-id.md +0 -13
- package/test/config/lsp.test.ts +0 -87
- package/test/config/markdown.test.ts +0 -228
- package/test/config/plugin.test.ts +0 -0
- package/test/config/tui.test.ts +0 -624
- package/test/control-plane/adapters.test.ts +0 -71
- package/test/control-plane/workspace.test.ts +0 -1526
- package/test/effect/app-runtime-logger.test.ts +0 -98
- package/test/effect/config-service.test.ts +0 -65
- package/test/effect/instance-state.test.ts +0 -394
- package/test/effect/run-service.test.ts +0 -89
- package/test/effect/runner.test.ts +0 -523
- package/test/fake/provider.ts +0 -82
- package/test/file/fsmonitor.test.ts +0 -68
- package/test/file/ignore.test.ts +0 -10
- package/test/file/index.test.ts +0 -954
- package/test/file/path-traversal.test.ts +0 -205
- package/test/file/ripgrep.test.ts +0 -226
- package/test/file/watcher.test.ts +0 -249
- package/test/filesystem/filesystem.test.ts +0 -319
- package/test/fixture/db.ts +0 -11
- package/test/fixture/fixture.test.ts +0 -26
- package/test/fixture/fixture.ts +0 -175
- package/test/fixture/flock-worker.ts +0 -72
- package/test/fixture/log-init-worker.ts +0 -62
- package/test/fixture/lsp/fake-lsp-server.js +0 -249
- package/test/fixture/plug-worker.ts +0 -93
- package/test/fixture/plugin-meta-worker.ts +0 -19
- package/test/fixture/skills/agents-sdk/SKILL.md +0 -152
- package/test/fixture/skills/cloudflare/SKILL.md +0 -211
- package/test/fixture/skills/index.json +0 -6
- package/test/fixture/tui-plugin.ts +0 -323
- package/test/fixture/tui-runtime.ts +0 -31
- package/test/format/format.test.ts +0 -272
- package/test/git/git.test.ts +0 -128
- package/test/ide/ide.test.ts +0 -82
- package/test/installation/installation.test.ts +0 -168
- package/test/keybind.test.ts +0 -421
- package/test/lib/effect.ts +0 -53
- package/test/lib/filesystem.ts +0 -10
- package/test/lib/llm-server.ts +0 -778
- package/test/lib/websocket.ts +0 -46
- package/test/lsp/client.test.ts +0 -482
- package/test/lsp/index.test.ts +0 -160
- package/test/lsp/launch.test.ts +0 -22
- package/test/lsp/lifecycle.test.ts +0 -184
- package/test/mcp/headers.test.ts +0 -178
- package/test/mcp/lifecycle.test.ts +0 -787
- package/test/mcp/oauth-auto-connect.test.ts +0 -311
- package/test/mcp/oauth-browser.test.ts +0 -276
- package/test/mcp/oauth-callback.test.ts +0 -34
- package/test/memory/abort-leak-webfetch.ts +0 -49
- package/test/memory/abort-leak.test.ts +0 -128
- package/test/patch/patch.test.ts +0 -348
- package/test/permission/arity.test.ts +0 -33
- package/test/permission/next.test.ts +0 -1227
- package/test/permission/next.toConfig.test.ts +0 -110
- package/test/permission-task.test.ts +0 -326
- package/test/plugin/auth-override.test.ts +0 -79
- package/test/plugin/cloudflare.test.ts +0 -68
- package/test/plugin/codex.test.ts +0 -123
- package/test/plugin/github-copilot-models.test.ts +0 -261
- package/test/plugin/install-concurrency.test.ts +0 -140
- package/test/plugin/install.test.ts +0 -570
- package/test/plugin/loader-shared.test.ts +0 -1169
- package/test/plugin/meta.test.ts +0 -137
- package/test/plugin/shared.test.ts +0 -88
- package/test/plugin/trigger.test.ts +0 -102
- package/test/plugin/workspace-adapter.test.ts +0 -109
- package/test/preload.ts +0 -77
- package/test/project/instance.test.ts +0 -276
- package/test/project/migrate-global.test.ts +0 -152
- package/test/project/project.test.ts +0 -600
- package/test/project/vcs.test.ts +0 -286
- package/test/project/worktree-remove.test.ts +0 -126
- package/test/project/worktree.test.ts +0 -223
- package/test/provider/amazon-bedrock.test.ts +0 -462
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +0 -523
- package/test/provider/copilot/copilot-chat-model.test.ts +0 -592
- package/test/provider/gitlab-duo.test.ts +0 -413
- package/test/provider/local.test.ts +0 -208
- package/test/provider/models.test.ts +0 -261
- package/test/provider/provider-category.test.ts +0 -190
- package/test/provider/provider.test.ts +0 -2758
- package/test/provider/transform.test.ts +0 -3681
- package/test/pty/pty-output-isolation.test.ts +0 -147
- package/test/pty/pty-session.test.ts +0 -102
- package/test/pty/pty-shell.test.ts +0 -104
- package/test/question/question.test.ts +0 -490
- package/test/saeeol/agent-global-config-dirs.test.ts +0 -24
- package/test/saeeol/agent-manager-tool.test.ts +0 -71
- package/test/saeeol/agent-permission-overrides.test.ts +0 -75
- package/test/saeeol/agent-skill-permissions.test.ts +0 -37
- package/test/saeeol/ask-agent-permissions.test.ts +0 -303
- package/test/saeeol/bash-hierarchy.test.ts +0 -64
- package/test/saeeol/bash-permission-metadata.test.ts +0 -66
- package/test/saeeol/bash-security-extended.test.ts +0 -243
- package/test/saeeol/bedrock-claude-empty-content.test.ts +0 -138
- package/test/saeeol/boxes-integration.test.ts +0 -415
- package/test/saeeol/builtin-skills.test.ts +0 -75
- package/test/saeeol/cleanup.ts +0 -28
- package/test/saeeol/cli/dev-setup.test.ts +0 -74
- package/test/saeeol/cli/roll-call.test.ts +0 -161
- package/test/saeeol/cli-run-auto-helper.test.ts +0 -58
- package/test/saeeol/codex-auth-refresh.test.ts +0 -124
- package/test/saeeol/commit-message/generate.test.ts +0 -188
- package/test/saeeol/commit-message/git-context.test.ts +0 -303
- package/test/saeeol/commit-message-windows.test.ts +0 -38
- package/test/saeeol/compaction-payload-recovery.test.ts +0 -406
- package/test/saeeol/compaction-preservation-audit.test.ts +0 -122
- package/test/saeeol/compaction-skip-guard.test.ts +0 -224
- package/test/saeeol/compaction-smart-select.test.ts +0 -100
- package/test/saeeol/config/config.test.ts +0 -166
- package/test/saeeol/config/indexing-default-plugin.test.ts +0 -82
- package/test/saeeol/config/opentelemetry-default.test.ts +0 -29
- package/test/saeeol/config-gitignore.test.ts +0 -70
- package/test/saeeol/config-injector.test.ts +0 -305
- package/test/saeeol/config-resilience.test.ts +0 -234
- package/test/saeeol/config-validation.test.ts +0 -183
- package/test/saeeol/cost-propagation.test.ts +0 -94
- package/test/saeeol/cost-tracker-extended.test.ts +0 -141
- package/test/saeeol/cost-tracker.test.ts +0 -64
- package/test/saeeol/custom-provider-delete.test.ts +0 -149
- package/test/saeeol/diff-full.test.ts +0 -226
- package/test/saeeol/edit-permission-filediff.test.ts +0 -223
- package/test/saeeol/encoding.test.ts +0 -364
- package/test/saeeol/enhance-prompt.test.ts +0 -61
- package/test/saeeol/ensure-plan-dir.test.ts +0 -32
- package/test/saeeol/errors.test.ts +0 -144
- package/test/saeeol/external-directory-boundary.test.ts +0 -96
- package/test/saeeol/gateway-headers.test.ts +0 -88
- package/test/saeeol/help.test.ts +0 -191
- package/test/saeeol/ignore-migrator.test.ts +0 -308
- package/test/saeeol/indexing-auth.test.ts +0 -45
- package/test/saeeol/indexing-feature.test.ts +0 -44
- package/test/saeeol/indexing-label.test.ts +0 -70
- package/test/saeeol/indexing-startup.test.ts +0 -381
- package/test/saeeol/indexing-worktree.test.ts +0 -73
- package/test/saeeol/instruction.test.ts +0 -136
- package/test/saeeol/lancedb-runtime.test.ts +0 -116
- package/test/saeeol/loader-auth.test.ts +0 -168
- package/test/saeeol/local-model.test.ts +0 -621
- package/test/saeeol/logo.test.ts +0 -31
- package/test/saeeol/lsp-typescript-lightweight.test.ts +0 -89
- package/test/saeeol/mcp-branding.test.ts +0 -33
- package/test/saeeol/mcp-docker-rm.test.ts +0 -32
- package/test/saeeol/mcp-migrator.test.ts +0 -736
- package/test/saeeol/mcp-oauth-callback.test.ts +0 -33
- package/test/saeeol/memory-io.test.ts +0 -198
- package/test/saeeol/memory-paths.test.ts +0 -87
- package/test/saeeol/memory-security.test.ts +0 -166
- package/test/saeeol/model-cache-org.test.ts +0 -164
- package/test/saeeol/model-info-panel-utils.test.ts +0 -52
- package/test/saeeol/model-info-panel.types.test.ts +0 -7
- package/test/saeeol/models-401-fallback.test.ts +0 -52
- package/test/saeeol/modes-migrator.test.ts +0 -320
- package/test/saeeol/nvidia-headers.test.ts +0 -74
- package/test/saeeol/patch-jsonc.test.ts +0 -73
- package/test/saeeol/patch.test.ts +0 -172
- package/test/saeeol/paths.test.ts +0 -265
- package/test/saeeol/permission/config-paths.test.ts +0 -174
- package/test/saeeol/permission/env-read.test.ts +0 -149
- package/test/saeeol/permission/external-directory-allow.test.ts +0 -327
- package/test/saeeol/permission/next.always-rules.test.ts +0 -882
- package/test/saeeol/permission/next.reply-http.test.ts +0 -205
- package/test/saeeol/permission/next.reply-routing.test.ts +0 -184
- package/test/saeeol/plan-exit-detection.test.ts +0 -494
- package/test/saeeol/plan-followup.test.ts +0 -1376
- package/test/saeeol/project-config-update.test.ts +0 -120
- package/test/saeeol/project-id.test.ts +0 -455
- package/test/saeeol/provider-cost.test.ts +0 -171
- package/test/saeeol/provider-list-failed-state.test.ts +0 -100
- package/test/saeeol/question-dismiss-all.test.ts +0 -174
- package/test/saeeol/read-directory.test.ts +0 -116
- package/test/saeeol/rules-migrator.test.ts +0 -257
- package/test/saeeol/run-auto.test.ts +0 -176
- package/test/saeeol/run-network.test.ts +0 -224
- package/test/saeeol/semantic-search.test.ts +0 -186
- package/test/saeeol/server/permission-allow-everything.test.ts +0 -125
- package/test/saeeol/session/instruction-substitution.test.ts +0 -72
- package/test/saeeol/session/platform-attribution.test.ts +0 -118
- package/test/saeeol/session/session.test.ts +0 -105
- package/test/saeeol/session-compaction-cap.test.ts +0 -399
- package/test/saeeol/session-compaction-chunks.test.ts +0 -501
- package/test/saeeol/session-compaction-safety.test.ts +0 -481
- package/test/saeeol/session-fork-remap.test.ts +0 -251
- package/test/saeeol/session-import-service.test.ts +0 -114
- package/test/saeeol/session-list.test.ts +0 -47
- package/test/saeeol/session-message-metadata.test.ts +0 -128
- package/test/saeeol/session-overflow.test.ts +0 -78
- package/test/saeeol/session-processor-empty-tool-calls.test.ts +0 -571
- package/test/saeeol/session-processor-network-offline.test.ts +0 -204
- package/test/saeeol/session-processor-retry-limit.test.ts +0 -238
- package/test/saeeol/session-processor-review-telemetry.test.ts +0 -82
- package/test/saeeol/session-prompt-compaction-safety.test.ts +0 -517
- package/test/saeeol/session-prompt-queue.test.ts +0 -815
- package/test/saeeol/sessions/inflight-cache.test.ts +0 -157
- package/test/saeeol/sessions/ingest-queue.test.ts +0 -402
- package/test/saeeol/sessions/remote-protocol.test.ts +0 -258
- package/test/saeeol/sessions/remote-sender.test.ts +0 -1036
- package/test/saeeol/sessions/remote-ws.test.ts +0 -367
- package/test/saeeol/sessions/sessions-enable-remote.test.disable +0 -181
- package/test/saeeol/slot-prop-reactivity.test.ts +0 -142
- package/test/saeeol/snapshot-cache.test.ts +0 -84
- package/test/saeeol/snapshot-freeze-repro.test.ts +0 -100
- package/test/saeeol/snapshot-track-timeout.test.ts +0 -519
- package/test/saeeol/stats-subagent-cost.test.ts +0 -123
- package/test/saeeol/suggestion/auto-dismiss.test.ts +0 -65
- package/test/saeeol/suggestion/suggestion.test.ts +0 -145
- package/test/saeeol/suggestion/tool.test.ts +0 -298
- package/test/saeeol/summary-file-diff.test.ts +0 -28
- package/test/saeeol/system-prompt.test.ts +0 -142
- package/test/saeeol/task-nesting.test.ts +0 -193
- package/test/saeeol/telemetry/feedback.test.ts +0 -8
- package/test/saeeol/todo-view.test.ts +0 -57
- package/test/saeeol/tool-encoding.test.ts +0 -455
- package/test/saeeol/tool-registry-indexing-import-failure.test.ts +0 -49
- package/test/saeeol/tool-registry-indexing.test.ts +0 -236
- package/test/saeeol/tool-registry-semantic-import-failure.test.ts +0 -55
- package/test/saeeol/tool-task-model.test.ts +0 -352
- package/test/saeeol/transform-opus-4.7.test.ts +0 -89
- package/test/saeeol/tui-diff.test.ts +0 -91
- package/test/saeeol/tui-sync.test.ts +0 -80
- package/test/saeeol/util/url.test.ts +0 -141
- package/test/saeeol/workflows-migrator.test.ts +0 -261
- package/test/saeeol/worktree-diff-summary.test.ts +0 -64
- package/test/saeeol/worktree-diff.test.ts +0 -223
- package/test/saeeol/worktree-remove-lock.test.ts +0 -82
- package/test/server/AGENTS.md +0 -15
- package/test/server/contract.test.ts +0 -249
- package/test/server/experimental-session-list.test.ts +0 -157
- package/test/server/global-session-list.test.ts +0 -155
- package/test/server/httpapi-authorization.test.ts +0 -103
- package/test/server/httpapi-bridge.test.ts +0 -440
- package/test/server/httpapi-config.test.ts +0 -67
- package/test/server/httpapi-cors.test.ts +0 -89
- package/test/server/httpapi-event.test.ts +0 -57
- package/test/server/httpapi-experimental.test.ts +0 -219
- package/test/server/httpapi-file.test.ts +0 -79
- package/test/server/httpapi-instance-context.test.ts +0 -237
- package/test/server/httpapi-instance.legacy.test.ts +0 -140
- package/test/server/httpapi-instance.test.ts +0 -83
- package/test/server/httpapi-json-parity.test.ts +0 -263
- package/test/server/httpapi-mcp-oauth.test.ts +0 -76
- package/test/server/httpapi-mcp.test.ts +0 -189
- package/test/server/httpapi-provider.test.ts +0 -153
- package/test/server/httpapi-pty-websocket.test.ts +0 -16
- package/test/server/httpapi-pty.test.ts +0 -175
- package/test/server/httpapi-raw-route-auth.test.ts +0 -89
- package/test/server/httpapi-sdk.test.ts +0 -679
- package/test/server/httpapi-session.test.ts +0 -464
- package/test/server/httpapi-sync.test.ts +0 -130
- package/test/server/httpapi-tui.test.ts +0 -121
- package/test/server/httpapi-workspace-routing.test.ts +0 -471
- package/test/server/httpapi-workspace.test.ts +0 -427
- package/test/server/project-init-git.test.ts +0 -113
- package/test/server/proxy-util.test.ts +0 -113
- package/test/server/session-actions.test.ts +0 -49
- package/test/server/session-list.test.ts +0 -238
- package/test/server/session-messages.test.ts +0 -167
- package/test/server/session-select.test.ts +0 -100
- package/test/server/trace-attributes.test.ts +0 -76
- package/test/server/workspace-proxy.test.ts +0 -165
- package/test/server/workspace-routing.test.ts +0 -85
- package/test/session/compaction.test.ts +0 -2420
- package/test/session/instruction.test.ts +0 -247
- package/test/session/llm.test.ts +0 -1273
- package/test/session/message-v2.test.ts +0 -1291
- package/test/session/messages-pagination.test.ts +0 -1173
- package/test/session/network.test.ts +0 -249
- package/test/session/processor-effect.test.ts +0 -847
- package/test/session/prompt.test.ts +0 -2131
- package/test/session/retry.test.ts +0 -340
- package/test/session/revert-compact.test.ts +0 -639
- package/test/session/schema-decoding.test.ts +0 -311
- package/test/session/session-entry-stepper.test.ts +0 -917
- package/test/session/session-schema.test.ts +0 -76
- package/test/session/snapshot-tool-race.test.ts +0 -257
- package/test/session/structured-output-integration.test.ts +0 -265
- package/test/session/structured-output.test.ts +0 -381
- package/test/session/system.test.ts +0 -73
- package/test/share/share-next.test.ts +0 -333
- package/test/shell/shell.test.ts +0 -99
- package/test/skill/discovery.test.ts +0 -116
- package/test/skill/skill.test.ts +0 -393
- package/test/snapshot/snapshot.test.ts +0 -1531
- package/test/storage/db.test.ts +0 -23
- package/test/storage/json-migration.test.ts +0 -832
- package/test/storage/storage.test.ts +0 -293
- package/test/suggestion/suggestion.test.ts +0 -1
- package/test/sync/index.test.ts +0 -256
- package/test/tool/__snapshots__/parameters.test.ts.snap +0 -500
- package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
- package/test/tool/apply_patch.test.ts +0 -614
- package/test/tool/bash.test.ts +0 -1225
- package/test/tool/diagnostics-filter.test.ts +0 -55
- package/test/tool/edit.test.ts +0 -754
- package/test/tool/external-directory.test.ts +0 -169
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +0 -65179
- package/test/tool/glob.test.ts +0 -107
- package/test/tool/grep.test.ts +0 -114
- package/test/tool/lsp.test.ts +0 -187
- package/test/tool/parameters.test.ts +0 -243
- package/test/tool/question.test.ts +0 -129
- package/test/tool/read.test.ts +0 -500
- package/test/tool/recall.test.ts +0 -151
- package/test/tool/registry.test.ts +0 -203
- package/test/tool/skill.test.ts +0 -135
- package/test/tool/suggest.test.ts +0 -1
- package/test/tool/task.test.ts +0 -612
- package/test/tool/tool-define.test.ts +0 -99
- package/test/tool/truncation.test.ts +0 -260
- package/test/tool/webfetch.test.ts +0 -103
- package/test/tool/write.test.ts +0 -291
- package/test/util/data-url.test.ts +0 -14
- package/test/util/effect-zod.test.ts +0 -754
- package/test/util/error.test.ts +0 -38
- package/test/util/filesystem.test.ts +0 -656
- package/test/util/format.test.ts +0 -59
- package/test/util/glob.test.ts +0 -164
- package/test/util/iife.test.ts +0 -36
- package/test/util/lazy.test.ts +0 -50
- package/test/util/lock.test.ts +0 -72
- package/test/util/log.test.ts +0 -86
- package/test/util/module.test.ts +0 -59
- package/test/util/process.test.ts +0 -128
- package/test/util/timeout.test.ts +0 -21
- package/test/util/which.test.ts +0 -100
- package/test/util/wildcard.test.ts +0 -90
- package/test/workspace/workspace-restore.test.ts +0 -296
- package/tsconfig.json +0 -19
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { test, expect, mock, beforeEach } from "bun:test"
|
|
2
|
-
import { Effect } from "effect"
|
|
3
|
-
import { createServer } from "http"
|
|
4
|
-
|
|
5
|
-
// Mock UnauthorizedError to match the SDK's class
|
|
6
|
-
class MockUnauthorizedError extends Error {
|
|
7
|
-
constructor(message?: string) {
|
|
8
|
-
super(message ?? "Unauthorized")
|
|
9
|
-
this.name = "UnauthorizedError"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Track what options were passed to each transport constructor
|
|
14
|
-
const transportCalls: Array<{
|
|
15
|
-
type: "streamable" | "sse"
|
|
16
|
-
url: string
|
|
17
|
-
options: { authProvider?: unknown }
|
|
18
|
-
}> = []
|
|
19
|
-
|
|
20
|
-
// Controls whether the mock transport simulates a 401 that triggers the SDK
|
|
21
|
-
// auth flow (which calls provider.state()) or a simple UnauthorizedError.
|
|
22
|
-
let simulateAuthFlow = true
|
|
23
|
-
let connectSucceedsImmediately = false
|
|
24
|
-
|
|
25
|
-
// Mock the transport constructors to simulate OAuth auto-auth on 401
|
|
26
|
-
void mock.module("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
|
|
27
|
-
StreamableHTTPClientTransport: class MockStreamableHTTP {
|
|
28
|
-
authProvider:
|
|
29
|
-
| {
|
|
30
|
-
state?: () => Promise<string>
|
|
31
|
-
redirectToAuthorization?: (url: URL) => Promise<void>
|
|
32
|
-
saveCodeVerifier?: (v: string) => Promise<void>
|
|
33
|
-
}
|
|
34
|
-
| undefined
|
|
35
|
-
constructor(url: URL, options?: { authProvider?: unknown }) {
|
|
36
|
-
this.authProvider = options?.authProvider as typeof this.authProvider
|
|
37
|
-
transportCalls.push({
|
|
38
|
-
type: "streamable",
|
|
39
|
-
url: url.toString(),
|
|
40
|
-
options: options ?? {},
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
async start() {
|
|
44
|
-
if (connectSucceedsImmediately) return
|
|
45
|
-
|
|
46
|
-
// Simulate what the real SDK transport does on 401:
|
|
47
|
-
// It calls auth() which eventually calls provider.state(), then
|
|
48
|
-
// provider.redirectToAuthorization(), then throws UnauthorizedError.
|
|
49
|
-
if (simulateAuthFlow && this.authProvider) {
|
|
50
|
-
// The SDK calls provider.state() to get the OAuth state parameter
|
|
51
|
-
if (this.authProvider.state) {
|
|
52
|
-
await this.authProvider.state()
|
|
53
|
-
}
|
|
54
|
-
// The SDK calls saveCodeVerifier before redirecting
|
|
55
|
-
if (this.authProvider.saveCodeVerifier) {
|
|
56
|
-
await this.authProvider.saveCodeVerifier("test-verifier")
|
|
57
|
-
}
|
|
58
|
-
// The SDK calls redirectToAuthorization to redirect the user
|
|
59
|
-
if (this.authProvider.redirectToAuthorization) {
|
|
60
|
-
await this.authProvider.redirectToAuthorization(new URL("https://auth.example.com/authorize?state=test"))
|
|
61
|
-
}
|
|
62
|
-
throw new MockUnauthorizedError()
|
|
63
|
-
}
|
|
64
|
-
throw new MockUnauthorizedError()
|
|
65
|
-
}
|
|
66
|
-
async finishAuth(_code: string) {}
|
|
67
|
-
},
|
|
68
|
-
}))
|
|
69
|
-
|
|
70
|
-
void mock.module("@modelcontextprotocol/sdk/client/sse.js", () => ({
|
|
71
|
-
SSEClientTransport: class MockSSE {
|
|
72
|
-
constructor(url: URL, options?: { authProvider?: unknown }) {
|
|
73
|
-
transportCalls.push({
|
|
74
|
-
type: "sse",
|
|
75
|
-
url: url.toString(),
|
|
76
|
-
options: options ?? {},
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
async start() {
|
|
80
|
-
throw new Error("Mock SSE transport cannot connect")
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
}))
|
|
84
|
-
|
|
85
|
-
// Mock the MCP SDK Client
|
|
86
|
-
void mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({
|
|
87
|
-
Client: class MockClient {
|
|
88
|
-
async connect(transport: { start: () => Promise<void> }) {
|
|
89
|
-
await transport.start()
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
setNotificationHandler() {}
|
|
93
|
-
|
|
94
|
-
async listTools() {
|
|
95
|
-
return { tools: [{ name: "test_tool", inputSchema: { type: "object", properties: {} } }] }
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async close() {}
|
|
99
|
-
},
|
|
100
|
-
}))
|
|
101
|
-
|
|
102
|
-
// Mock UnauthorizedError in the auth module so instanceof checks work
|
|
103
|
-
void mock.module("@modelcontextprotocol/sdk/client/auth.js", () => ({
|
|
104
|
-
UnauthorizedError: MockUnauthorizedError,
|
|
105
|
-
}))
|
|
106
|
-
|
|
107
|
-
beforeEach(() => {
|
|
108
|
-
transportCalls.length = 0
|
|
109
|
-
simulateAuthFlow = true
|
|
110
|
-
connectSucceedsImmediately = false
|
|
111
|
-
})
|
|
112
|
-
async function occupy(port: number) {
|
|
113
|
-
const srv = createServer()
|
|
114
|
-
const bound = await new Promise<boolean>((resolve, reject) => {
|
|
115
|
-
const fail = (err: Error & { code?: string }) => {
|
|
116
|
-
srv.off("error", fail)
|
|
117
|
-
if (err.code === "EADDRINUSE") {
|
|
118
|
-
resolve(false)
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
reject(err)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
srv.once("error", fail)
|
|
125
|
-
srv.listen(port, "127.0.0.1", () => {
|
|
126
|
-
srv.off("error", fail)
|
|
127
|
-
resolve(true)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
async [Symbol.asyncDispose]() {
|
|
133
|
-
if (!bound) return
|
|
134
|
-
await new Promise<void>((resolve) => srv.close(() => resolve()))
|
|
135
|
-
},
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Import modules after mocking
|
|
140
|
-
const { MCP } = await import("../../src/mcp/index")
|
|
141
|
-
const { Instance } = await import("../../src/project/instance")
|
|
142
|
-
const { tmpdir } = await import("../fixture/fixture")
|
|
143
|
-
const { OAUTH_CALLBACK_PORT } = await import("../../src/mcp/oauth-provider")
|
|
144
|
-
|
|
145
|
-
test("first connect to OAuth server shows needs_auth instead of failed", async () => {
|
|
146
|
-
await using tmp = await tmpdir({
|
|
147
|
-
init: async (dir) => {
|
|
148
|
-
await Bun.write(
|
|
149
|
-
`${dir}/saeeol.json`,
|
|
150
|
-
JSON.stringify({
|
|
151
|
-
$schema: "https://saeeol.ai/config.json",
|
|
152
|
-
mcp: {
|
|
153
|
-
"test-oauth": {
|
|
154
|
-
type: "remote",
|
|
155
|
-
url: "https://example.com/mcp",
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
}),
|
|
159
|
-
)
|
|
160
|
-
},
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
await Instance.provide({
|
|
164
|
-
directory: tmp.path,
|
|
165
|
-
fn: async () => {
|
|
166
|
-
const result = await Effect.runPromise(
|
|
167
|
-
MCP.Service.use((mcp) =>
|
|
168
|
-
mcp.add("test-oauth", {
|
|
169
|
-
type: "remote",
|
|
170
|
-
url: "https://example.com/mcp",
|
|
171
|
-
}),
|
|
172
|
-
).pipe(Effect.provide(MCP.defaultLayer)),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const serverStatus = result.status as Record<string, { status: string; error?: string }>
|
|
176
|
-
|
|
177
|
-
// The server should be detected as needing auth, NOT as failed.
|
|
178
|
-
// Before the fix, provider.state() would throw a plain Error
|
|
179
|
-
// ("No OAuth state saved for MCP server: test-oauth") which was
|
|
180
|
-
// not caught as UnauthorizedError, causing status to be "failed".
|
|
181
|
-
expect(serverStatus["test-oauth"]).toBeDefined()
|
|
182
|
-
expect(serverStatus["test-oauth"].status).toBe("needs_auth")
|
|
183
|
-
},
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
test("state() generates a new state when none is saved", async () => {
|
|
188
|
-
const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider")
|
|
189
|
-
const { McpAuth } = await import("../../src/mcp/auth")
|
|
190
|
-
|
|
191
|
-
await using tmp = await tmpdir()
|
|
192
|
-
|
|
193
|
-
await Instance.provide({
|
|
194
|
-
directory: tmp.path,
|
|
195
|
-
fn: async () => {
|
|
196
|
-
const auth = await Effect.runPromise(
|
|
197
|
-
Effect.gen(function* () {
|
|
198
|
-
return yield* McpAuth.Service
|
|
199
|
-
}).pipe(Effect.provide(McpAuth.defaultLayer)),
|
|
200
|
-
)
|
|
201
|
-
const provider = new McpOAuthProvider(
|
|
202
|
-
"test-state-gen",
|
|
203
|
-
"https://example.com/mcp",
|
|
204
|
-
{},
|
|
205
|
-
{ onRedirect: async () => {} },
|
|
206
|
-
auth,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
const entryBefore = await Effect.runPromise(
|
|
210
|
-
McpAuth.Service.use((auth) => auth.get("test-state-gen")).pipe(Effect.provide(McpAuth.defaultLayer)),
|
|
211
|
-
)
|
|
212
|
-
expect(entryBefore?.oauthState).toBeUndefined()
|
|
213
|
-
|
|
214
|
-
// state() should generate and return a new state, not throw
|
|
215
|
-
const state = await provider.state()
|
|
216
|
-
expect(typeof state).toBe("string")
|
|
217
|
-
expect(state.length).toBe(64) // 32 bytes as hex
|
|
218
|
-
|
|
219
|
-
// The generated state should be persisted
|
|
220
|
-
const entryAfter = await Effect.runPromise(
|
|
221
|
-
McpAuth.Service.use((auth) => auth.get("test-state-gen")).pipe(Effect.provide(McpAuth.defaultLayer)),
|
|
222
|
-
)
|
|
223
|
-
expect(entryAfter?.oauthState).toBe(state)
|
|
224
|
-
},
|
|
225
|
-
})
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
test("state() returns existing state when one is saved", async () => {
|
|
229
|
-
const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider")
|
|
230
|
-
const { McpAuth } = await import("../../src/mcp/auth")
|
|
231
|
-
|
|
232
|
-
await using tmp = await tmpdir()
|
|
233
|
-
|
|
234
|
-
await Instance.provide({
|
|
235
|
-
directory: tmp.path,
|
|
236
|
-
fn: async () => {
|
|
237
|
-
const auth = await Effect.runPromise(
|
|
238
|
-
Effect.gen(function* () {
|
|
239
|
-
return yield* McpAuth.Service
|
|
240
|
-
}).pipe(Effect.provide(McpAuth.defaultLayer)),
|
|
241
|
-
)
|
|
242
|
-
const provider = new McpOAuthProvider(
|
|
243
|
-
"test-state-existing",
|
|
244
|
-
"https://example.com/mcp",
|
|
245
|
-
{},
|
|
246
|
-
{ onRedirect: async () => {} },
|
|
247
|
-
auth,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
// Pre-save a state
|
|
251
|
-
const existingState = "pre-saved-state-value"
|
|
252
|
-
await Effect.runPromise(
|
|
253
|
-
McpAuth.Service.use((auth) => auth.updateOAuthState("test-state-existing", existingState)).pipe(
|
|
254
|
-
Effect.provide(McpAuth.defaultLayer),
|
|
255
|
-
),
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
// state() should return the existing state
|
|
259
|
-
const state = await provider.state()
|
|
260
|
-
expect(state).toBe(existingState)
|
|
261
|
-
},
|
|
262
|
-
})
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
test("authenticate() stores a connected client when auth completes without redirect", async () => {
|
|
266
|
-
await using tmp = await tmpdir({
|
|
267
|
-
init: async (dir) => {
|
|
268
|
-
await Bun.write(
|
|
269
|
-
`${dir}/saeeol.json`,
|
|
270
|
-
JSON.stringify({
|
|
271
|
-
$schema: "https://saeeol.ai/config.json",
|
|
272
|
-
mcp: {
|
|
273
|
-
"test-oauth-connect": {
|
|
274
|
-
type: "remote",
|
|
275
|
-
url: "https://example.com/mcp",
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
}),
|
|
279
|
-
)
|
|
280
|
-
},
|
|
281
|
-
})
|
|
282
|
-
await using port = await occupy(OAUTH_CALLBACK_PORT)
|
|
283
|
-
void port
|
|
284
|
-
|
|
285
|
-
await Instance.provide({
|
|
286
|
-
directory: tmp.path,
|
|
287
|
-
fn: async () => {
|
|
288
|
-
await Effect.runPromise(
|
|
289
|
-
MCP.Service.use((mcp) =>
|
|
290
|
-
Effect.gen(function* () {
|
|
291
|
-
const added = yield* mcp.add("test-oauth-connect", {
|
|
292
|
-
type: "remote",
|
|
293
|
-
url: "https://example.com/mcp",
|
|
294
|
-
})
|
|
295
|
-
const before = added.status as Record<string, { status: string; error?: string }>
|
|
296
|
-
expect(before["test-oauth-connect"]?.status).toBe("needs_auth")
|
|
297
|
-
|
|
298
|
-
simulateAuthFlow = false
|
|
299
|
-
connectSucceedsImmediately = true
|
|
300
|
-
|
|
301
|
-
const result = yield* mcp.authenticate("test-oauth-connect")
|
|
302
|
-
expect(result.status).toBe("connected")
|
|
303
|
-
|
|
304
|
-
const after = yield* mcp.status()
|
|
305
|
-
expect(after["test-oauth-connect"]?.status).toBe("connected")
|
|
306
|
-
}),
|
|
307
|
-
).pipe(Effect.provide(MCP.defaultLayer)),
|
|
308
|
-
)
|
|
309
|
-
},
|
|
310
|
-
})
|
|
311
|
-
})
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import { test, expect, mock, beforeEach } from "bun:test"
|
|
2
|
-
import { EventEmitter } from "events"
|
|
3
|
-
import { Effect } from "effect"
|
|
4
|
-
import type { MCP as MCPNS } from "../../src/mcp/index"
|
|
5
|
-
|
|
6
|
-
// Track open() calls and control failure behavior
|
|
7
|
-
let openShouldFail = false
|
|
8
|
-
let openCalledWith: string | undefined
|
|
9
|
-
|
|
10
|
-
void mock.module("open", () => ({
|
|
11
|
-
default: async (url: string) => {
|
|
12
|
-
openCalledWith = url
|
|
13
|
-
|
|
14
|
-
// Return a mock subprocess that emits an error if openShouldFail is true
|
|
15
|
-
const subprocess = new EventEmitter()
|
|
16
|
-
if (openShouldFail) {
|
|
17
|
-
// its listener. The previous setTimeout(10) raced listener attachment
|
|
18
|
-
// on slow Windows CI; emit() before `.on("error", ...)` was silently
|
|
19
|
-
// lost and BrowserOpenFailed was never published.
|
|
20
|
-
const err = new Error("spawn xdg-open ENOENT")
|
|
21
|
-
const originalOn = subprocess.on.bind(subprocess)
|
|
22
|
-
subprocess.on = function (event, listener) {
|
|
23
|
-
const ret = originalOn(event, listener)
|
|
24
|
-
if (event === "error") queueMicrotask(() => (listener as (e: Error) => void).call(subprocess, err))
|
|
25
|
-
return ret
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return subprocess
|
|
29
|
-
},
|
|
30
|
-
}))
|
|
31
|
-
|
|
32
|
-
// Mock UnauthorizedError
|
|
33
|
-
class MockUnauthorizedError extends Error {
|
|
34
|
-
constructor() {
|
|
35
|
-
super("Unauthorized")
|
|
36
|
-
this.name = "UnauthorizedError"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Track what options were passed to each transport constructor
|
|
41
|
-
const transportCalls: Array<{
|
|
42
|
-
type: "streamable" | "sse"
|
|
43
|
-
url: string
|
|
44
|
-
options: { authProvider?: unknown }
|
|
45
|
-
}> = []
|
|
46
|
-
|
|
47
|
-
// Mock the transport constructors
|
|
48
|
-
void mock.module("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
|
|
49
|
-
StreamableHTTPClientTransport: class MockStreamableHTTP {
|
|
50
|
-
url: string
|
|
51
|
-
authProvider: { redirectToAuthorization?: (url: URL) => Promise<void> } | undefined
|
|
52
|
-
constructor(url: URL, options?: { authProvider?: { redirectToAuthorization?: (url: URL) => Promise<void> } }) {
|
|
53
|
-
this.url = url.toString()
|
|
54
|
-
this.authProvider = options?.authProvider
|
|
55
|
-
transportCalls.push({
|
|
56
|
-
type: "streamable",
|
|
57
|
-
url: url.toString(),
|
|
58
|
-
options: options ?? {},
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
async start() {
|
|
62
|
-
// Simulate OAuth redirect by calling the authProvider's redirectToAuthorization
|
|
63
|
-
if (this.authProvider?.redirectToAuthorization) {
|
|
64
|
-
await this.authProvider.redirectToAuthorization(new URL("https://auth.example.com/authorize?client_id=test"))
|
|
65
|
-
}
|
|
66
|
-
throw new MockUnauthorizedError()
|
|
67
|
-
}
|
|
68
|
-
async finishAuth(_code: string) {
|
|
69
|
-
// Mock successful auth completion
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
}))
|
|
73
|
-
|
|
74
|
-
void mock.module("@modelcontextprotocol/sdk/client/sse.js", () => ({
|
|
75
|
-
SSEClientTransport: class MockSSE {
|
|
76
|
-
constructor(url: URL) {
|
|
77
|
-
transportCalls.push({
|
|
78
|
-
type: "sse",
|
|
79
|
-
url: url.toString(),
|
|
80
|
-
options: {},
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
async start() {
|
|
84
|
-
throw new Error("Mock SSE transport cannot connect")
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
}))
|
|
88
|
-
|
|
89
|
-
// Mock the MCP SDK Client to trigger OAuth flow
|
|
90
|
-
void mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({
|
|
91
|
-
Client: class MockClient {
|
|
92
|
-
async connect(transport: { start: () => Promise<void> }) {
|
|
93
|
-
await transport.start()
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
}))
|
|
97
|
-
// Mock UnauthorizedError in the auth module
|
|
98
|
-
void mock.module("@modelcontextprotocol/sdk/client/auth.js", () => ({
|
|
99
|
-
UnauthorizedError: MockUnauthorizedError,
|
|
100
|
-
}))
|
|
101
|
-
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
openShouldFail = false
|
|
104
|
-
openCalledWith = undefined
|
|
105
|
-
transportCalls.length = 0
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
async function waitFor(fn: () => boolean, timeout = 5_000) {
|
|
109
|
-
const deadline = Date.now() + timeout
|
|
110
|
-
while (!fn() && Date.now() < deadline) {
|
|
111
|
-
await new Promise((resolve) => setTimeout(resolve, 25))
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Import modules after mocking
|
|
116
|
-
const { MCP } = await import("../../src/mcp/index")
|
|
117
|
-
const { AppRuntime } = await import("../../src/effect/app-runtime")
|
|
118
|
-
const { Bus } = await import("../../src/bus")
|
|
119
|
-
const { McpOAuthCallback } = await import("../../src/mcp/oauth-callback")
|
|
120
|
-
const { Instance } = await import("../../src/project/instance")
|
|
121
|
-
const { tmpdir } = await import("../fixture/fixture")
|
|
122
|
-
const service = MCP.Service as unknown as Effect.Effect<MCPNS.Interface, never, never>
|
|
123
|
-
|
|
124
|
-
test("BrowserOpenFailed event is published when open() throws", async () => {
|
|
125
|
-
await using tmp = await tmpdir({
|
|
126
|
-
init: async (dir) => {
|
|
127
|
-
await Bun.write(
|
|
128
|
-
`${dir}/saeeol.json`,
|
|
129
|
-
JSON.stringify({
|
|
130
|
-
$schema: "https://app.saeeol.ai/config.json",
|
|
131
|
-
mcp: {
|
|
132
|
-
"test-oauth-server": {
|
|
133
|
-
type: "remote",
|
|
134
|
-
url: "https://example.com/mcp",
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
}),
|
|
138
|
-
)
|
|
139
|
-
},
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
await Instance.provide({
|
|
143
|
-
directory: tmp.path,
|
|
144
|
-
fn: async () => {
|
|
145
|
-
openShouldFail = true
|
|
146
|
-
|
|
147
|
-
const events: Array<{ mcpName: string; url: string }> = []
|
|
148
|
-
const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => {
|
|
149
|
-
events.push(evt.properties)
|
|
150
|
-
})
|
|
151
|
-
// Run authenticate with a timeout to avoid waiting forever for the callback
|
|
152
|
-
// Attach a handler immediately so callback shutdown rejections
|
|
153
|
-
// don't show up as unhandled between tests.
|
|
154
|
-
const authPromise = AppRuntime.runPromise(
|
|
155
|
-
Effect.gen(function* () {
|
|
156
|
-
const mcp = yield* service
|
|
157
|
-
return yield* mcp.authenticate("test-oauth-server")
|
|
158
|
-
}),
|
|
159
|
-
).catch(() => undefined)
|
|
160
|
-
|
|
161
|
-
await waitFor(() => events.length > 0)
|
|
162
|
-
|
|
163
|
-
// Stop the callback server and cancel any pending auth
|
|
164
|
-
await McpOAuthCallback.stop()
|
|
165
|
-
|
|
166
|
-
await authPromise
|
|
167
|
-
|
|
168
|
-
unsubscribe()
|
|
169
|
-
|
|
170
|
-
// Verify the BrowserOpenFailed event was published
|
|
171
|
-
expect(events.length).toBe(1)
|
|
172
|
-
expect(events[0].mcpName).toBe("test-oauth-server")
|
|
173
|
-
expect(events[0].url).toContain("https://")
|
|
174
|
-
},
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
test("BrowserOpenFailed event is NOT published when open() succeeds", async () => {
|
|
179
|
-
await using tmp = await tmpdir({
|
|
180
|
-
init: async (dir) => {
|
|
181
|
-
await Bun.write(
|
|
182
|
-
`${dir}/saeeol.json`,
|
|
183
|
-
JSON.stringify({
|
|
184
|
-
$schema: "https://app.saeeol.ai/config.json",
|
|
185
|
-
mcp: {
|
|
186
|
-
"test-oauth-server-2": {
|
|
187
|
-
type: "remote",
|
|
188
|
-
url: "https://example.com/mcp",
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
}),
|
|
192
|
-
)
|
|
193
|
-
},
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
await Instance.provide({
|
|
197
|
-
directory: tmp.path,
|
|
198
|
-
fn: async () => {
|
|
199
|
-
openShouldFail = false
|
|
200
|
-
|
|
201
|
-
const events: Array<{ mcpName: string; url: string }> = []
|
|
202
|
-
const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => {
|
|
203
|
-
events.push(evt.properties)
|
|
204
|
-
})
|
|
205
|
-
// Run authenticate with a timeout to avoid waiting forever for the callback
|
|
206
|
-
const authPromise = AppRuntime.runPromise(
|
|
207
|
-
Effect.gen(function* () {
|
|
208
|
-
const mcp = yield* service
|
|
209
|
-
return yield* mcp.authenticate("test-oauth-server-2")
|
|
210
|
-
}),
|
|
211
|
-
).catch(() => undefined)
|
|
212
|
-
|
|
213
|
-
await waitFor(() => openCalledWith !== undefined)
|
|
214
|
-
await new Promise((resolve) => setTimeout(resolve, 600))
|
|
215
|
-
|
|
216
|
-
// Stop the callback server and cancel any pending auth
|
|
217
|
-
await McpOAuthCallback.stop()
|
|
218
|
-
|
|
219
|
-
await authPromise
|
|
220
|
-
|
|
221
|
-
unsubscribe()
|
|
222
|
-
|
|
223
|
-
// Verify NO BrowserOpenFailed event was published
|
|
224
|
-
expect(events.length).toBe(0)
|
|
225
|
-
// Verify open() was still called
|
|
226
|
-
expect(openCalledWith).toBeDefined()
|
|
227
|
-
},
|
|
228
|
-
})
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
test("open() is called with the authorization URL", async () => {
|
|
232
|
-
await using tmp = await tmpdir({
|
|
233
|
-
init: async (dir) => {
|
|
234
|
-
await Bun.write(
|
|
235
|
-
`${dir}/saeeol.json`,
|
|
236
|
-
JSON.stringify({
|
|
237
|
-
$schema: "https://app.saeeol.ai/config.json",
|
|
238
|
-
mcp: {
|
|
239
|
-
"test-oauth-server-3": {
|
|
240
|
-
type: "remote",
|
|
241
|
-
url: "https://example.com/mcp",
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
}),
|
|
245
|
-
)
|
|
246
|
-
},
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
await Instance.provide({
|
|
250
|
-
directory: tmp.path,
|
|
251
|
-
fn: async () => {
|
|
252
|
-
openShouldFail = false
|
|
253
|
-
openCalledWith = undefined
|
|
254
|
-
// Run authenticate with a timeout to avoid waiting forever for the callback
|
|
255
|
-
const authPromise = AppRuntime.runPromise(
|
|
256
|
-
Effect.gen(function* () {
|
|
257
|
-
const mcp = yield* service
|
|
258
|
-
return yield* mcp.authenticate("test-oauth-server-3")
|
|
259
|
-
}),
|
|
260
|
-
).catch(() => undefined)
|
|
261
|
-
|
|
262
|
-
await waitFor(() => openCalledWith !== undefined)
|
|
263
|
-
await new Promise((resolve) => setTimeout(resolve, 600))
|
|
264
|
-
|
|
265
|
-
// Stop the callback server and cancel any pending auth
|
|
266
|
-
await McpOAuthCallback.stop()
|
|
267
|
-
|
|
268
|
-
await authPromise
|
|
269
|
-
|
|
270
|
-
// Verify open was called with a URL
|
|
271
|
-
expect(openCalledWith).toBeDefined()
|
|
272
|
-
expect(typeof openCalledWith).toBe("string")
|
|
273
|
-
expect(openCalledWith!).toContain("https://")
|
|
274
|
-
},
|
|
275
|
-
})
|
|
276
|
-
})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, afterEach } from "bun:test"
|
|
2
|
-
import { McpOAuthCallback } from "../../src/mcp/oauth-callback"
|
|
3
|
-
import { parseRedirectUri } from "../../src/mcp/oauth-provider"
|
|
4
|
-
|
|
5
|
-
describe("parseRedirectUri", () => {
|
|
6
|
-
test("returns defaults when no URI provided", () => {
|
|
7
|
-
const result = parseRedirectUri()
|
|
8
|
-
expect(result.port).toBe(19876)
|
|
9
|
-
expect(result.path).toBe("/mcp/oauth/callback")
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
test("parses port and path from URI", () => {
|
|
13
|
-
const result = parseRedirectUri("http://127.0.0.1:8080/oauth/callback")
|
|
14
|
-
expect(result.port).toBe(8080)
|
|
15
|
-
expect(result.path).toBe("/oauth/callback")
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test("returns defaults for invalid URI", () => {
|
|
19
|
-
const result = parseRedirectUri("not-a-valid-url")
|
|
20
|
-
expect(result.port).toBe(19876)
|
|
21
|
-
expect(result.path).toBe("/mcp/oauth/callback")
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe("McpOAuthCallback.ensureRunning", () => {
|
|
26
|
-
afterEach(async () => {
|
|
27
|
-
await McpOAuthCallback.stop()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test("starts server with custom redirectUri port and path", async () => {
|
|
31
|
-
await McpOAuthCallback.ensureRunning("http://127.0.0.1:18000/custom/callback")
|
|
32
|
-
expect(McpOAuthCallback.isRunning()).toBe(true)
|
|
33
|
-
})
|
|
34
|
-
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { abortAfterAny } from "../../src/util/abort"
|
|
2
|
-
|
|
3
|
-
const MB = 1024 * 1024
|
|
4
|
-
const ITERATIONS = 50
|
|
5
|
-
|
|
6
|
-
const heap = () => {
|
|
7
|
-
Bun.gc(true)
|
|
8
|
-
return process.memoryUsage().heapUsed / MB
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const server = Bun.serve({
|
|
12
|
-
port: 0,
|
|
13
|
-
fetch() {
|
|
14
|
-
return new Response("hello from local", {
|
|
15
|
-
headers: {
|
|
16
|
-
"content-type": "text/plain",
|
|
17
|
-
},
|
|
18
|
-
})
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
const url = `http://127.0.0.1:${server.port}`
|
|
23
|
-
|
|
24
|
-
async function run() {
|
|
25
|
-
const { signal, clearTimeout } = abortAfterAny(30000, new AbortController().signal)
|
|
26
|
-
try {
|
|
27
|
-
const response = await fetch(url, { signal })
|
|
28
|
-
await response.text()
|
|
29
|
-
} finally {
|
|
30
|
-
clearTimeout()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
await run()
|
|
36
|
-
Bun.sleepSync(100)
|
|
37
|
-
const baseline = heap()
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
40
|
-
await run()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Bun.sleepSync(100)
|
|
44
|
-
const after = heap()
|
|
45
|
-
process.stdout.write(JSON.stringify({ baseline, after, growth: after - baseline }))
|
|
46
|
-
} finally {
|
|
47
|
-
void server.stop(true)
|
|
48
|
-
process.exit(0)
|
|
49
|
-
}
|