@vibesdotdev/control 0.0.0-stage → 1.0.0-rc.3
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/LICENSE +18 -0
- package/README.md +90 -2
- package/dist/browser/browser.cloud.plugin.d.ts +21 -0
- package/dist/browser/browser.cloud.plugin.d.ts.map +1 -0
- package/dist/browser/browser.cloud.plugin.js +37 -0
- package/dist/browser/browser.cloud.plugin.js.map +1 -0
- package/dist/browser/browser.consumer.plugin.d.ts +4 -0
- package/dist/browser/browser.consumer.plugin.d.ts.map +1 -0
- package/dist/browser/browser.consumer.plugin.js +77 -0
- package/dist/browser/browser.consumer.plugin.js.map +1 -0
- package/dist/browser/browser.plugin.d.ts +3 -0
- package/dist/browser/browser.plugin.d.ts.map +1 -0
- package/dist/browser/browser.plugin.js +3 -0
- package/dist/browser/browser.plugin.js.map +1 -0
- package/dist/browser/bun/webview.backend.d.ts +23 -0
- package/dist/browser/bun/webview.backend.d.ts.map +1 -0
- package/dist/browser/bun/webview.backend.js +262 -0
- package/dist/browser/bun/webview.backend.js.map +1 -0
- package/dist/browser/bun/webview.descriptor.d.ts +21 -0
- package/dist/browser/bun/webview.descriptor.d.ts.map +1 -0
- package/dist/browser/bun/webview.descriptor.js +21 -0
- package/dist/browser/bun/webview.descriptor.js.map +1 -0
- package/dist/browser/bun/webview.plugin.d.ts +3 -0
- package/dist/browser/bun/webview.plugin.d.ts.map +1 -0
- package/dist/browser/bun/webview.plugin.js +24 -0
- package/dist/browser/bun/webview.plugin.js.map +1 -0
- package/dist/browser/client/browser-client.d.ts +92 -0
- package/dist/browser/client/browser-client.d.ts.map +1 -0
- package/dist/browser/client/browser-client.js +138 -0
- package/dist/browser/client/browser-client.js.map +1 -0
- package/dist/browser/client/workspace-client.d.ts +29 -0
- package/dist/browser/client/workspace-client.d.ts.map +1 -0
- package/dist/browser/client/workspace-client.js +38 -0
- package/dist/browser/client/workspace-client.js.map +1 -0
- package/dist/browser/contracts.d.ts +246 -0
- package/dist/browser/contracts.d.ts.map +1 -0
- package/dist/browser/contracts.js +38 -0
- package/dist/browser/contracts.js.map +1 -0
- package/dist/browser/index.d.ts +6 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +7 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/plugin.d.ts +3 -0
- package/dist/browser/plugin.d.ts.map +1 -0
- package/dist/browser/plugin.js +3 -0
- package/dist/browser/plugin.js.map +1 -0
- package/dist/browser/runtime/backend/kind.d.ts +23 -0
- package/dist/browser/runtime/backend/kind.d.ts.map +1 -0
- package/dist/browser/runtime/backend/kind.js +12 -0
- package/dist/browser/runtime/backend/kind.js.map +1 -0
- package/dist/browser/runtime/backend/playwright.descriptor.d.ts +20 -0
- package/dist/browser/runtime/backend/playwright.descriptor.d.ts.map +1 -0
- package/dist/browser/runtime/backend/playwright.descriptor.js +20 -0
- package/dist/browser/runtime/backend/playwright.descriptor.js.map +1 -0
- package/dist/browser/runtime/backend/resolve.d.ts +8 -0
- package/dist/browser/runtime/backend/resolve.d.ts.map +1 -0
- package/dist/browser/runtime/backend/resolve.js +43 -0
- package/dist/browser/runtime/backend/resolve.js.map +1 -0
- package/dist/browser/runtime/backend/types.d.ts +12 -0
- package/dist/browser/runtime/backend/types.d.ts.map +1 -0
- package/dist/browser/runtime/backend/types.js +12 -0
- package/dist/browser/runtime/backend/types.js.map +1 -0
- package/dist/browser/runtime/backends/playwright.backend.d.ts +71 -0
- package/dist/browser/runtime/backends/playwright.backend.d.ts.map +1 -0
- package/dist/browser/runtime/backends/playwright.backend.js +177 -0
- package/dist/browser/runtime/backends/playwright.backend.js.map +1 -0
- package/dist/browser/runtime/index.d.ts +3 -0
- package/dist/browser/runtime/index.d.ts.map +1 -0
- package/dist/browser/runtime/index.js +2 -0
- package/dist/browser/runtime/index.js.map +1 -0
- package/dist/browser/runtime/launcher.d.ts +18 -0
- package/dist/browser/runtime/launcher.d.ts.map +1 -0
- package/dist/browser/runtime/launcher.js +123 -0
- package/dist/browser/runtime/launcher.js.map +1 -0
- package/dist/browser/runtime/manager-host.d.ts +22 -0
- package/dist/browser/runtime/manager-host.d.ts.map +1 -0
- package/dist/browser/runtime/manager-host.js +102 -0
- package/dist/browser/runtime/manager-host.js.map +1 -0
- package/dist/browser/runtime/manager.context.d.ts +19 -0
- package/dist/browser/runtime/manager.context.d.ts.map +1 -0
- package/dist/browser/runtime/manager.context.descriptor.d.ts +30 -0
- package/dist/browser/runtime/manager.context.descriptor.d.ts.map +1 -0
- package/dist/browser/runtime/manager.context.descriptor.js +7 -0
- package/dist/browser/runtime/manager.context.descriptor.js.map +1 -0
- package/dist/browser/runtime/manager.context.impl.consumer.d.ts +6 -0
- package/dist/browser/runtime/manager.context.impl.consumer.d.ts.map +1 -0
- package/dist/browser/runtime/manager.context.impl.consumer.js +7 -0
- package/dist/browser/runtime/manager.context.impl.consumer.js.map +1 -0
- package/dist/browser/runtime/manager.context.js +19 -0
- package/dist/browser/runtime/manager.context.js.map +1 -0
- package/dist/browser/runtime/manager.d.ts +97 -0
- package/dist/browser/runtime/manager.d.ts.map +1 -0
- package/dist/browser/runtime/manager.js +731 -0
- package/dist/browser/runtime/manager.js.map +1 -0
- package/dist/browser/runtime/safe-url.d.ts +27 -0
- package/dist/browser/runtime/safe-url.d.ts.map +1 -0
- package/dist/browser/runtime/safe-url.js +77 -0
- package/dist/browser/runtime/safe-url.js.map +1 -0
- package/dist/browser/runtime/session-lease.d.ts +24 -0
- package/dist/browser/runtime/session-lease.d.ts.map +1 -0
- package/dist/browser/runtime/session-lease.js +65 -0
- package/dist/browser/runtime/session-lease.js.map +1 -0
- package/dist/browser/runtime/step-executor.d.ts +14 -0
- package/dist/browser/runtime/step-executor.d.ts.map +1 -0
- package/dist/browser/runtime/step-executor.js +97 -0
- package/dist/browser/runtime/step-executor.js.map +1 -0
- package/dist/browser/runtime/types.d.ts +54 -0
- package/dist/browser/runtime/types.d.ts.map +1 -0
- package/dist/browser/runtime/types.js +5 -0
- package/dist/browser/runtime/types.js.map +1 -0
- package/dist/browser/schemas/backend.d.ts +79 -0
- package/dist/browser/schemas/backend.d.ts.map +1 -0
- package/dist/browser/schemas/backend.js +33 -0
- package/dist/browser/schemas/backend.js.map +1 -0
- package/dist/browser/schemas/cli.d.ts +55 -0
- package/dist/browser/schemas/cli.d.ts.map +1 -0
- package/dist/browser/schemas/cli.js +37 -0
- package/dist/browser/schemas/cli.js.map +1 -0
- package/dist/browser/schemas/client.d.ts +28 -0
- package/dist/browser/schemas/client.d.ts.map +1 -0
- package/dist/browser/schemas/client.js +7 -0
- package/dist/browser/schemas/client.js.map +1 -0
- package/dist/browser/schemas/page.d.ts +71 -0
- package/dist/browser/schemas/page.d.ts.map +1 -0
- package/dist/browser/schemas/page.js +32 -0
- package/dist/browser/schemas/page.js.map +1 -0
- package/dist/browser/schemas/profile.d.ts +14 -0
- package/dist/browser/schemas/profile.d.ts.map +1 -0
- package/dist/browser/schemas/profile.js +14 -0
- package/dist/browser/schemas/profile.js.map +1 -0
- package/dist/browser/schemas/recipe.d.ts +218 -0
- package/dist/browser/schemas/recipe.d.ts.map +1 -0
- package/dist/browser/schemas/recipe.js +91 -0
- package/dist/browser/schemas/recipe.js.map +1 -0
- package/dist/browser/schemas/session.d.ts +42 -0
- package/dist/browser/schemas/session.d.ts.map +1 -0
- package/dist/browser/schemas/session.js +22 -0
- package/dist/browser/schemas/session.js.map +1 -0
- package/dist/browser/schemas/tab.d.ts +38 -0
- package/dist/browser/schemas/tab.d.ts.map +1 -0
- package/dist/browser/schemas/tab.js +20 -0
- package/dist/browser/schemas/tab.js.map +1 -0
- package/dist/browser/schemas/tool-card.d.ts +68 -0
- package/dist/browser/schemas/tool-card.d.ts.map +1 -0
- package/dist/browser/schemas/tool-card.js +29 -0
- package/dist/browser/schemas/tool-card.js.map +1 -0
- package/dist/browser/schemas/webview.d.ts +40 -0
- package/dist/browser/schemas/webview.d.ts.map +1 -0
- package/dist/browser/schemas/webview.js +5 -0
- package/dist/browser/schemas/webview.js.map +1 -0
- package/dist/browser/schemas/workspace.d.ts +8 -0
- package/dist/browser/schemas/workspace.d.ts.map +1 -0
- package/dist/browser/schemas/workspace.js +7 -0
- package/dist/browser/schemas/workspace.js.map +1 -0
- package/dist/browser/shared/ai-client.d.ts +26 -0
- package/dist/browser/shared/ai-client.d.ts.map +1 -0
- package/dist/browser/shared/ai-client.js +27 -0
- package/dist/browser/shared/ai-client.js.map +1 -0
- package/dist/browser/shared/ai-request.d.ts +26 -0
- package/dist/browser/shared/ai-request.d.ts.map +1 -0
- package/dist/browser/shared/ai-request.js +32 -0
- package/dist/browser/shared/ai-request.js.map +1 -0
- package/dist/browser/shared/cleanup.d.ts +5 -0
- package/dist/browser/shared/cleanup.d.ts.map +1 -0
- package/dist/browser/shared/cleanup.js +15 -0
- package/dist/browser/shared/cleanup.js.map +1 -0
- package/dist/browser/shared/constants.d.ts +19 -0
- package/dist/browser/shared/constants.d.ts.map +1 -0
- package/dist/browser/shared/constants.js +13 -0
- package/dist/browser/shared/constants.js.map +1 -0
- package/dist/browser/shared/json.d.ts +5 -0
- package/dist/browser/shared/json.d.ts.map +1 -0
- package/dist/browser/shared/json.js +3 -0
- package/dist/browser/shared/json.js.map +1 -0
- package/dist/browser/shared/paths.d.ts +33 -0
- package/dist/browser/shared/paths.d.ts.map +1 -0
- package/dist/browser/shared/paths.js +55 -0
- package/dist/browser/shared/paths.js.map +1 -0
- package/dist/browser/shared/steps.d.ts +82 -0
- package/dist/browser/shared/steps.d.ts.map +1 -0
- package/dist/browser/shared/steps.js +88 -0
- package/dist/browser/shared/steps.js.map +1 -0
- package/dist/browser/shared/storage-state.d.ts +23 -0
- package/dist/browser/shared/storage-state.d.ts.map +1 -0
- package/dist/browser/shared/storage-state.js +5 -0
- package/dist/browser/shared/storage-state.js.map +1 -0
- package/dist/browser/shared/tool-summary.d.ts +48 -0
- package/dist/browser/shared/tool-summary.d.ts.map +1 -0
- package/dist/browser/shared/tool-summary.js +183 -0
- package/dist/browser/shared/tool-summary.js.map +1 -0
- package/dist/browser/storage/browser-profile.record.descriptor.d.ts +28 -0
- package/dist/browser/storage/browser-profile.record.descriptor.d.ts.map +1 -0
- package/dist/browser/storage/browser-profile.record.descriptor.js +129 -0
- package/dist/browser/storage/browser-profile.record.descriptor.js.map +1 -0
- package/dist/browser/storage/browser-recipe.record.descriptor.d.ts +29 -0
- package/dist/browser/storage/browser-recipe.record.descriptor.d.ts.map +1 -0
- package/dist/browser/storage/browser-recipe.record.descriptor.js +103 -0
- package/dist/browser/storage/browser-recipe.record.descriptor.js.map +1 -0
- package/dist/browser/storage/browser-session.record.descriptor.d.ts +28 -0
- package/dist/browser/storage/browser-session.record.descriptor.d.ts.map +1 -0
- package/dist/browser/storage/browser-session.record.descriptor.js +147 -0
- package/dist/browser/storage/browser-session.record.descriptor.js.map +1 -0
- package/dist/browser/storage/browser-tab.record.descriptor.d.ts +28 -0
- package/dist/browser/storage/browser-tab.record.descriptor.d.ts.map +1 -0
- package/dist/browser/storage/browser-tab.record.descriptor.js +153 -0
- package/dist/browser/storage/browser-tab.record.descriptor.js.map +1 -0
- package/dist/browser/storage/database.d.ts +9 -0
- package/dist/browser/storage/database.d.ts.map +1 -0
- package/dist/browser/storage/database.js +16 -0
- package/dist/browser/storage/database.js.map +1 -0
- package/dist/browser/storage/index.d.ts +6 -0
- package/dist/browser/storage/index.d.ts.map +1 -0
- package/dist/browser/storage/index.js +5 -0
- package/dist/browser/storage/index.js.map +1 -0
- package/dist/browser/storage/types.d.ts +78 -0
- package/dist/browser/storage/types.d.ts.map +1 -0
- package/dist/browser/storage/types.js +5 -0
- package/dist/browser/storage/types.js.map +1 -0
- package/dist/browser/tools/cdp/implementation.d.ts +6 -0
- package/dist/browser/tools/cdp/implementation.d.ts.map +1 -0
- package/dist/browser/tools/cdp/implementation.js +20 -0
- package/dist/browser/tools/cdp/implementation.js.map +1 -0
- package/dist/browser/tools/control/implementation.d.ts +7 -0
- package/dist/browser/tools/control/implementation.d.ts.map +1 -0
- package/dist/browser/tools/control/implementation.js +185 -0
- package/dist/browser/tools/control/implementation.js.map +1 -0
- package/dist/browser/tools/control/prompts/index.d.ts +113 -0
- package/dist/browser/tools/control/prompts/index.d.ts.map +1 -0
- package/dist/browser/tools/control/prompts/index.js +5 -0
- package/dist/browser/tools/control/prompts/index.js.map +1 -0
- package/dist/browser/tools/control/prompts/plan-request.prompt.descriptor.d.ts +114 -0
- package/dist/browser/tools/control/prompts/plan-request.prompt.descriptor.d.ts.map +1 -0
- package/dist/browser/tools/control/prompts/plan-request.prompt.descriptor.js +57 -0
- package/dist/browser/tools/control/prompts/plan-request.prompt.descriptor.js.map +1 -0
- package/dist/browser/tools/control/prompts/planner.prompt.descriptor.d.ts +113 -0
- package/dist/browser/tools/control/prompts/planner.prompt.descriptor.d.ts.map +1 -0
- package/dist/browser/tools/control/prompts/planner.prompt.descriptor.js +15 -0
- package/dist/browser/tools/control/prompts/planner.prompt.descriptor.js.map +1 -0
- package/dist/browser/tools/control/schemas.d.ts +220 -0
- package/dist/browser/tools/control/schemas.d.ts.map +1 -0
- package/dist/browser/tools/control/schemas.js +49 -0
- package/dist/browser/tools/control/schemas.js.map +1 -0
- package/dist/browser/tools/direct/implementation.d.ts +7 -0
- package/dist/browser/tools/direct/implementation.d.ts.map +1 -0
- package/dist/browser/tools/direct/implementation.js +63 -0
- package/dist/browser/tools/direct/implementation.js.map +1 -0
- package/dist/browser/tools/direct/schemas.d.ts +209 -0
- package/dist/browser/tools/direct/schemas.d.ts.map +1 -0
- package/dist/browser/tools/direct/schemas.js +68 -0
- package/dist/browser/tools/direct/schemas.js.map +1 -0
- package/dist/browser/tools/index.d.ts +9 -0
- package/dist/browser/tools/index.d.ts.map +1 -0
- package/dist/browser/tools/index.js +9 -0
- package/dist/browser/tools/index.js.map +1 -0
- package/dist/browser/tools/observe/implementation.d.ts +7 -0
- package/dist/browser/tools/observe/implementation.d.ts.map +1 -0
- package/dist/browser/tools/observe/implementation.js +59 -0
- package/dist/browser/tools/observe/implementation.js.map +1 -0
- package/dist/browser/tools/observe/schemas.d.ts +63 -0
- package/dist/browser/tools/observe/schemas.d.ts.map +1 -0
- package/dist/browser/tools/observe/schemas.js +38 -0
- package/dist/browser/tools/observe/schemas.js.map +1 -0
- package/dist/browser/tools/observe/vision.d.ts +21 -0
- package/dist/browser/tools/observe/vision.d.ts.map +1 -0
- package/dist/browser/tools/observe/vision.js +73 -0
- package/dist/browser/tools/observe/vision.js.map +1 -0
- package/dist/browser/tools/recipe/implementation.d.ts +4 -0
- package/dist/browser/tools/recipe/implementation.d.ts.map +1 -0
- package/dist/browser/tools/recipe/implementation.js +259 -0
- package/dist/browser/tools/recipe/implementation.js.map +1 -0
- package/dist/browser/tools/recipe/schemas.d.ts +646 -0
- package/dist/browser/tools/recipe/schemas.d.ts.map +1 -0
- package/dist/browser/tools/recipe/schemas.js +126 -0
- package/dist/browser/tools/recipe/schemas.js.map +1 -0
- package/dist/browser/tools/session/implementation.d.ts +7 -0
- package/dist/browser/tools/session/implementation.d.ts.map +1 -0
- package/dist/browser/tools/session/implementation.js +111 -0
- package/dist/browser/tools/session/implementation.js.map +1 -0
- package/dist/browser/tools/session/index.d.ts +5 -0
- package/dist/browser/tools/session/index.d.ts.map +1 -0
- package/dist/browser/tools/session/index.js +6 -0
- package/dist/browser/tools/session/index.js.map +1 -0
- package/dist/browser/tools/session/schemas.d.ts +78 -0
- package/dist/browser/tools/session/schemas.d.ts.map +1 -0
- package/dist/browser/tools/session/schemas.js +60 -0
- package/dist/browser/tools/session/schemas.js.map +1 -0
- package/dist/browser/tools/unified/browser-recipe.prompt.descriptor.d.ts +114 -0
- package/dist/browser/tools/unified/browser-recipe.prompt.descriptor.d.ts.map +1 -0
- package/dist/browser/tools/unified/browser-recipe.prompt.descriptor.js +22 -0
- package/dist/browser/tools/unified/browser-recipe.prompt.descriptor.js.map +1 -0
- package/dist/browser/tools/unified/browser.impl.cloud.d.ts +8 -0
- package/dist/browser/tools/unified/browser.impl.cloud.d.ts.map +1 -0
- package/dist/browser/tools/unified/browser.impl.cloud.js +381 -0
- package/dist/browser/tools/unified/browser.impl.cloud.js.map +1 -0
- package/dist/browser/tools/unified/browser.impl.consumer.d.ts +14 -0
- package/dist/browser/tools/unified/browser.impl.consumer.d.ts.map +1 -0
- package/dist/browser/tools/unified/browser.impl.consumer.js +87 -0
- package/dist/browser/tools/unified/browser.impl.consumer.js.map +1 -0
- package/dist/browser/tools/unified/browser.unified.descriptor.d.ts +587 -0
- package/dist/browser/tools/unified/browser.unified.descriptor.d.ts.map +1 -0
- package/dist/browser/tools/unified/browser.unified.descriptor.js +54 -0
- package/dist/browser/tools/unified/browser.unified.descriptor.js.map +1 -0
- package/dist/browser/tools/unified/execution-binding.d.ts +5 -0
- package/dist/browser/tools/unified/execution-binding.d.ts.map +1 -0
- package/dist/browser/tools/unified/execution-binding.js +51 -0
- package/dist/browser/tools/unified/execution-binding.js.map +1 -0
- package/dist/browser/tools/unified/index.d.ts +5 -0
- package/dist/browser/tools/unified/index.d.ts.map +1 -0
- package/dist/browser/tools/unified/index.js +6 -0
- package/dist/browser/tools/unified/index.js.map +1 -0
- package/dist/browser/tools/unified/scan-cache-wiring.d.ts +16 -0
- package/dist/browser/tools/unified/scan-cache-wiring.d.ts.map +1 -0
- package/dist/browser/tools/unified/scan-cache-wiring.js +87 -0
- package/dist/browser/tools/unified/scan-cache-wiring.js.map +1 -0
- package/dist/browser/tools/unified/schemas.d.ts +786 -0
- package/dist/browser/tools/unified/schemas.d.ts.map +1 -0
- package/dist/browser/tools/unified/schemas.js +354 -0
- package/dist/browser/tools/unified/schemas.js.map +1 -0
- package/dist/browser/tools/webmcp/detection-script.d.ts +19 -0
- package/dist/browser/tools/webmcp/detection-script.d.ts.map +1 -0
- package/dist/browser/tools/webmcp/detection-script.js +104 -0
- package/dist/browser/tools/webmcp/detection-script.js.map +1 -0
- package/dist/browser/tools/webmcp/implementation.d.ts +18 -0
- package/dist/browser/tools/webmcp/implementation.d.ts.map +1 -0
- package/dist/browser/tools/webmcp/implementation.js +61 -0
- package/dist/browser/tools/webmcp/implementation.js.map +1 -0
- package/dist/cli/browser/browser.cli-group.descriptor.d.ts +17 -0
- package/dist/cli/browser/browser.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/browser.cli-group.descriptor.js +17 -0
- package/dist/cli/browser/browser.cli-group.descriptor.js.map +1 -0
- package/dist/cli/browser/browser.cli.plugin.d.ts +12 -0
- package/dist/cli/browser/browser.cli.plugin.d.ts.map +1 -0
- package/dist/cli/browser/browser.cli.plugin.js +179 -0
- package/dist/cli/browser/browser.cli.plugin.js.map +1 -0
- package/dist/cli/browser/browser.recipe.cli-group.descriptor.d.ts +18 -0
- package/dist/cli/browser/browser.recipe.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/browser.recipe.cli-group.descriptor.js +18 -0
- package/dist/cli/browser/browser.recipe.cli-group.descriptor.js.map +1 -0
- package/dist/cli/browser/browser.session.cli-group.descriptor.d.ts +18 -0
- package/dist/cli/browser/browser.session.cli-group.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/browser.session.cli-group.descriptor.js +18 -0
- package/dist/cli/browser/browser.session.cli-group.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/control/browser.control.cli-command.descriptor.d.ts +36 -0
- package/dist/cli/browser/commands/control/browser.control.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/control/browser.control.cli-command.descriptor.js +29 -0
- package/dist/cli/browser/commands/control/browser.control.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/control/browser.control.impl.d.ts +7 -0
- package/dist/cli/browser/commands/control/browser.control.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/control/browser.control.impl.js +67 -0
- package/dist/cli/browser/commands/control/browser.control.impl.js.map +1 -0
- package/dist/cli/browser/commands/direct/browser.direct.cli-command.descriptor.d.ts +38 -0
- package/dist/cli/browser/commands/direct/browser.direct.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/direct/browser.direct.cli-command.descriptor.js +28 -0
- package/dist/cli/browser/commands/direct/browser.direct.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/direct/browser.direct.impl.d.ts +7 -0
- package/dist/cli/browser/commands/direct/browser.direct.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/direct/browser.direct.impl.js +85 -0
- package/dist/cli/browser/commands/direct/browser.direct.impl.js.map +1 -0
- package/dist/cli/browser/commands/observe/browser.observe.cli-command.descriptor.d.ts +30 -0
- package/dist/cli/browser/commands/observe/browser.observe.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/observe/browser.observe.cli-command.descriptor.js +22 -0
- package/dist/cli/browser/commands/observe/browser.observe.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/observe/browser.observe.impl.d.ts +7 -0
- package/dist/cli/browser/commands/observe/browser.observe.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/observe/browser.observe.impl.js +59 -0
- package/dist/cli/browser/commands/observe/browser.observe.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.cli-command.descriptor.d.ts +38 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.cli-command.descriptor.js +26 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.impl.js +60 -0
- package/dist/cli/browser/commands/recipe/create/browser.recipe.create.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.cli-command.descriptor.d.ts +25 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.cli-command.descriptor.js +22 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.impl.js +35 -0
- package/dist/cli/browser/commands/recipe/delete/browser.recipe.delete.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.cli-command.descriptor.d.ts +25 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.cli-command.descriptor.js +22 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.impl.js +88 -0
- package/dist/cli/browser/commands/recipe/get/browser.recipe.get.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.cli-command.descriptor.d.ts +20 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.cli-command.descriptor.js +19 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.impl.js +34 -0
- package/dist/cli/browser/commands/recipe/list/browser.recipe.list.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.cli-command.descriptor.d.ts +37 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.cli-command.descriptor.js +26 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.impl.js +88 -0
- package/dist/cli/browser/commands/recipe/run/browser.recipe.run.impl.js.map +1 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.cli-command.descriptor.d.ts +37 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.cli-command.descriptor.js +26 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.impl.d.ts +7 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.impl.js +63 -0
- package/dist/cli/browser/commands/recipe/update/browser.recipe.update.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.cli-command.descriptor.d.ts +28 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.cli-command.descriptor.js +23 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.impl.d.ts +35 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.impl.js +75 -0
- package/dist/cli/browser/commands/session/close/browser.session.close.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.cli-command.descriptor.d.ts +16 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.cli-command.descriptor.js +16 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.impl.d.ts +7 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.impl.js +14 -0
- package/dist/cli/browser/commands/session/dismiss/browser.session.dismiss.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.cli-command.descriptor.d.ts +25 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.cli-command.descriptor.js +22 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.impl.d.ts +18 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.impl.js +61 -0
- package/dist/cli/browser/commands/session/get/browser.session.get.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.cli-command.descriptor.d.ts +27 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.cli-command.descriptor.js +24 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.impl.d.ts +22 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.impl.js +73 -0
- package/dist/cli/browser/commands/session/list/browser.session.list.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.cli-command.descriptor.d.ts +25 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.cli-command.descriptor.js +22 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.impl.d.ts +7 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.impl.js +29 -0
- package/dist/cli/browser/commands/session/present/browser.session.present.impl.js.map +1 -0
- package/dist/cli/browser/commands/session/resolve-session.d.ts +19 -0
- package/dist/cli/browser/commands/session/resolve-session.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/resolve-session.js +11 -0
- package/dist/cli/browser/commands/session/resolve-session.js.map +1 -0
- package/dist/cli/browser/commands/session/session-age.d.ts +6 -0
- package/dist/cli/browser/commands/session/session-age.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/session-age.js +34 -0
- package/dist/cli/browser/commands/session/session-age.js.map +1 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.cli-command.descriptor.d.ts +26 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.cli-command.descriptor.js +24 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.cli-command.descriptor.js.map +1 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.impl.d.ts +32 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.impl.d.ts.map +1 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.impl.js +112 -0
- package/dist/cli/browser/commands/session/sweep/browser.session.sweep.impl.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/svelte/browser/BrowserWorkspace.svelte +450 -0
- package/dist/svelte/browser/BrowserWorkspace.svelte.d.ts +20 -0
- package/dist/svelte/browser/BrowserWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/browser/browser-automation-svelte.plugin.d.ts +3 -0
- package/dist/svelte/browser/browser-automation-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/browser/browser-automation-svelte.plugin.js +25 -0
- package/dist/svelte/browser/docs/browser-automation-svelte.docs.descriptor.d.ts +4 -0
- package/dist/svelte/browser/docs/browser-automation-svelte.docs.descriptor.d.ts.map +1 -0
- package/dist/svelte/browser/docs/browser-automation-svelte.docs.descriptor.js +55 -0
- package/dist/svelte/browser/index.d.ts +6 -0
- package/dist/svelte/browser/index.d.ts.map +1 -0
- package/dist/svelte/browser/index.js +4 -0
- package/dist/svelte/browser/state.svelte.d.ts +28 -0
- package/dist/svelte/browser/state.svelte.d.ts.map +1 -0
- package/dist/svelte/browser/state.svelte.js +87 -0
- package/dist/svelte/browser/styles.css +10 -0
- package/dist/svelte/browser/tool-widgets/BrowserToolCard.svelte +268 -0
- package/dist/svelte/browser/tool-widgets/BrowserToolCard.svelte.d.ts +10 -0
- package/dist/svelte/browser/tool-widgets/BrowserToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/browser/tool-widgets/index.d.ts +2 -0
- package/dist/svelte/browser/tool-widgets/index.d.ts.map +1 -0
- package/dist/svelte/browser/tool-widgets/index.js +1 -0
- package/dist/svelte/browser/tool-widgets/summary.d.ts +2 -0
- package/dist/svelte/browser/tool-widgets/summary.d.ts.map +1 -0
- package/dist/svelte/browser/tool-widgets/summary.js +1 -0
- package/dist/svelte/browser/types.d.ts +16 -0
- package/dist/svelte/browser/types.d.ts.map +1 -0
- package/dist/svelte/browser/types.js +1 -0
- package/dist/tui/browser/browser-sessions.app.descriptor.d.ts +33 -0
- package/dist/tui/browser/browser-sessions.app.descriptor.d.ts.map +1 -0
- package/dist/tui/browser/browser-sessions.app.descriptor.js +33 -0
- package/dist/tui/browser/browser-sessions.app.descriptor.js.map +1 -0
- package/dist/tui/browser/browser-sessions.app.impl.d.ts +4 -0
- package/dist/tui/browser/browser-sessions.app.impl.d.ts.map +1 -0
- package/dist/tui/browser/browser-sessions.app.impl.js +231 -0
- package/dist/tui/browser/browser-sessions.app.impl.js.map +1 -0
- package/dist/tui/browser/browser.tui.plugin.d.ts +13 -0
- package/dist/tui/browser/browser.tui.plugin.d.ts.map +1 -0
- package/dist/tui/browser/browser.tui.plugin.js +52 -0
- package/dist/tui/browser/browser.tui.plugin.js.map +1 -0
- package/dist/tui/browser/tool-widgets/BrowserToolWidget.impl.consumer.d.ts +4 -0
- package/dist/tui/browser/tool-widgets/BrowserToolWidget.impl.consumer.d.ts.map +1 -0
- package/dist/tui/browser/tool-widgets/BrowserToolWidget.impl.consumer.js +31 -0
- package/dist/tui/browser/tool-widgets/BrowserToolWidget.impl.consumer.js.map +1 -0
- package/dist/tui/browser/tool-widgets/summary.d.ts +40 -0
- package/dist/tui/browser/tool-widgets/summary.d.ts.map +1 -0
- package/dist/tui/browser/tool-widgets/summary.js +16 -0
- package/dist/tui/browser/tool-widgets/summary.js.map +1 -0
- package/package.json +212 -4
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Runtime Manager
|
|
3
|
+
*/
|
|
4
|
+
import { resolveFileSystemAdapter, statChecked } from '@vibesdotdev/filesystem';
|
|
5
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
6
|
+
import { CapabilityError, settle, composeChildEnv, CaughtFailureSchema } from '@vibesdotdev/kernel';
|
|
7
|
+
import { runCapture } from '@vibesdotdev/kernel/execution';
|
|
8
|
+
import { resolveProfilesDir, resolveStorageDir } from "../shared/paths.js";
|
|
9
|
+
import { DEFAULT_MAX_ACTIVE_SESSIONS, DEFAULT_SESSION_IDLE_TIMEOUT_MS, DEFAULT_CLEANUP_INTERVAL_MS } from "../shared/constants.js";
|
|
10
|
+
import { launchBrowserContext } from "./launcher.js";
|
|
11
|
+
import { assertSafeBrowserUrl, launchTrustedBrowserOrigins, normalizeTrustedBrowserOrigins } from "./safe-url.js";
|
|
12
|
+
import { closeBrowserSessionLease, withBrowserSessionLease } from "./session-lease.js";
|
|
13
|
+
/** Map the legacy launch-options shape onto the backend launch input. */
|
|
14
|
+
function toBackendLaunchInput(runtime, opts, extra) {
|
|
15
|
+
return {
|
|
16
|
+
runtime,
|
|
17
|
+
profileDir: extra.profileDir,
|
|
18
|
+
extensions: extra.extensions,
|
|
19
|
+
storageStatePath: extra.storageStatePath,
|
|
20
|
+
headless: opts?.headless,
|
|
21
|
+
viewport: opts?.viewport,
|
|
22
|
+
recordVideo: opts?.recordVideo?.enabled
|
|
23
|
+
? { dir: opts.recordVideo.dir, size: opts.recordVideo.size }
|
|
24
|
+
: undefined,
|
|
25
|
+
trustedOrigins: extra.trustedOrigins
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** The exact origin a session's base URL declares; none when it has no base URL. */
|
|
29
|
+
function sessionBaseOrigins(baseUrl) {
|
|
30
|
+
if (!baseUrl)
|
|
31
|
+
return [];
|
|
32
|
+
if (!URL.canParse(baseUrl))
|
|
33
|
+
throw new Error(`Browser session base URL is invalid: ${baseUrl}`);
|
|
34
|
+
return [...normalizeTrustedBrowserOrigins([new URL(baseUrl).origin])];
|
|
35
|
+
}
|
|
36
|
+
/** A session's navigation trust: its base origin, else the runtime's declared local proxy origins. */
|
|
37
|
+
function sessionTrustedOrigins(runtime, baseUrl) {
|
|
38
|
+
return launchTrustedBrowserOrigins(runtime, sessionBaseOrigins(baseUrl));
|
|
39
|
+
}
|
|
40
|
+
function sessionTrustedOriginList(baseUrl) {
|
|
41
|
+
const origins = sessionBaseOrigins(baseUrl);
|
|
42
|
+
return origins.length > 0 ? origins : undefined;
|
|
43
|
+
}
|
|
44
|
+
function cleanupErrorDetail(error) {
|
|
45
|
+
if (error instanceof AggregateError) {
|
|
46
|
+
const nested = error.errors.map((cause) => cleanupErrorDetail(CaughtFailureSchema.parse(cause))).join('; ');
|
|
47
|
+
return nested ? `${error.message}: ${nested}` : error.message;
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof Error) {
|
|
50
|
+
return error.cause === undefined
|
|
51
|
+
? error.message
|
|
52
|
+
: `${error.message}: ${cleanupErrorDetail(CaughtFailureSchema.parse(error.cause))}`;
|
|
53
|
+
}
|
|
54
|
+
return error.description;
|
|
55
|
+
}
|
|
56
|
+
function cleanupFailure(resource, error) {
|
|
57
|
+
const detail = cleanupErrorDetail(error);
|
|
58
|
+
return new Error(`Failed to close browser resource '${resource}': ${detail}`, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
function throwCleanupFailures(failures, operation) {
|
|
61
|
+
if (failures.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
throw new AggregateError(failures, `${operation} failed for ${failures.length} resource${failures.length === 1 ? '' : 's'}`);
|
|
64
|
+
}
|
|
65
|
+
export async function activatePresentationWindow(runtime) {
|
|
66
|
+
const platform = runtime.localEnvironment.getPlatform?.();
|
|
67
|
+
if (!platform) {
|
|
68
|
+
throw new CapabilityError('Browser presentation requires a declared host platform.', 'UNAVAILABLE', {
|
|
69
|
+
reason: 'host-platform-undeclared'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (platform !== 'darwin')
|
|
73
|
+
return;
|
|
74
|
+
const script = [
|
|
75
|
+
'tell application "System Events"',
|
|
76
|
+
' try',
|
|
77
|
+
' set p to first process whose name contains "Chromium"',
|
|
78
|
+
' set frontmost of p to true',
|
|
79
|
+
' end try',
|
|
80
|
+
'end tell'
|
|
81
|
+
].join('\n');
|
|
82
|
+
const result = await runCapture(runtime, 'osascript', ['-e', script], {
|
|
83
|
+
envMode: 'replace',
|
|
84
|
+
env: composeChildEnv(runtime, {}),
|
|
85
|
+
signal: runtime.lifecycle.signal
|
|
86
|
+
});
|
|
87
|
+
if (result.exitCode !== 0) {
|
|
88
|
+
throw new CapabilityError('Browser presentation activation failed.', 'UNAVAILABLE', {
|
|
89
|
+
reason: 'presentation-activation-failed',
|
|
90
|
+
details: { exitCode: result.exitCode }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class BrowserRuntimeManager {
|
|
95
|
+
runtime;
|
|
96
|
+
workspacePath;
|
|
97
|
+
backend;
|
|
98
|
+
maxActive;
|
|
99
|
+
presentationLauncher;
|
|
100
|
+
presentationActivator;
|
|
101
|
+
sessions = new Map();
|
|
102
|
+
sessionAliases = new Map();
|
|
103
|
+
ephemeralSessions = new Set();
|
|
104
|
+
sessionActivity = new Map();
|
|
105
|
+
tabs = new Map();
|
|
106
|
+
closedTabs = new Set();
|
|
107
|
+
closedContexts = new Set();
|
|
108
|
+
stopCleanupTimer = null;
|
|
109
|
+
closeAllPromise = null;
|
|
110
|
+
instanceId = crypto.randomUUID();
|
|
111
|
+
idleTimeout = DEFAULT_SESSION_IDLE_TIMEOUT_MS;
|
|
112
|
+
presentationBrowser = null;
|
|
113
|
+
// Storage descriptors
|
|
114
|
+
sessionsStorage;
|
|
115
|
+
tabsStorage;
|
|
116
|
+
profilesStorage;
|
|
117
|
+
storageInitialized = false;
|
|
118
|
+
constructor(runtime, workspacePath,
|
|
119
|
+
/** Runtime-selected browser provider. Construction never invents a fallback. */
|
|
120
|
+
backend, maxActive = DEFAULT_MAX_ACTIVE_SESSIONS, presentationLauncher = launchBrowserContext, presentationActivator = () => activatePresentationWindow(runtime)) {
|
|
121
|
+
this.runtime = runtime;
|
|
122
|
+
this.workspacePath = workspacePath;
|
|
123
|
+
this.backend = backend;
|
|
124
|
+
this.maxActive = maxActive;
|
|
125
|
+
this.presentationLauncher = presentationLauncher;
|
|
126
|
+
this.presentationActivator = presentationActivator;
|
|
127
|
+
this.startCleanupInterval();
|
|
128
|
+
}
|
|
129
|
+
getBackendId() {
|
|
130
|
+
return this.backend.id;
|
|
131
|
+
}
|
|
132
|
+
async resolveStorageRecord(id) {
|
|
133
|
+
return this.runtime
|
|
134
|
+
.query('storage/record')
|
|
135
|
+
.withId(id)
|
|
136
|
+
.enabled()
|
|
137
|
+
.resolve();
|
|
138
|
+
}
|
|
139
|
+
async ensureStorage() {
|
|
140
|
+
if (this.storageInitialized)
|
|
141
|
+
return;
|
|
142
|
+
this.sessionsStorage = await this.resolveStorageRecord('browser.sessions');
|
|
143
|
+
this.tabsStorage = await this.resolveStorageRecord('browser.tabs');
|
|
144
|
+
this.profilesStorage = await this.resolveStorageRecord('browser.profiles');
|
|
145
|
+
this.storageInitialized = true;
|
|
146
|
+
}
|
|
147
|
+
setMaxActiveSessions(maxActive) {
|
|
148
|
+
this.maxActive = maxActive;
|
|
149
|
+
}
|
|
150
|
+
setIdleTimeout(timeoutMs) {
|
|
151
|
+
this.idleTimeout = timeoutMs;
|
|
152
|
+
}
|
|
153
|
+
/** Start periodic cleanup of idle sessions */
|
|
154
|
+
startCleanupInterval() {
|
|
155
|
+
if (this.stopCleanupTimer)
|
|
156
|
+
return;
|
|
157
|
+
// A finite host (the CLI) starts no autonomous background work; its sessions are
|
|
158
|
+
// closed when the runtime host disposes (BrowserRuntimeManagerHost.dispose).
|
|
159
|
+
if (this.runtime.scope.lifecycle === 'finite')
|
|
160
|
+
return;
|
|
161
|
+
const logger = getLogger('control:browser-manager', this.runtime);
|
|
162
|
+
this.stopCleanupTimer = this.runtime.lifecycle.interval(async () => {
|
|
163
|
+
const cleaned = await settle(async () => {
|
|
164
|
+
await this.heartbeatSessions();
|
|
165
|
+
await this.cleanupIdleSessions();
|
|
166
|
+
});
|
|
167
|
+
if (!cleaned.ok)
|
|
168
|
+
logger.warn('browser session cleanup tick failed', { error: cleaned.cause });
|
|
169
|
+
}, DEFAULT_CLEANUP_INTERVAL_MS);
|
|
170
|
+
}
|
|
171
|
+
async heartbeatSessions() {
|
|
172
|
+
if (this.sessions.size === 0)
|
|
173
|
+
return;
|
|
174
|
+
await this.ensureStorage();
|
|
175
|
+
await Promise.all([...this.sessions.values()].map(async (runtimeSession) => this.sessionsStorage.update(runtimeSession.session.id, {
|
|
176
|
+
metadata: await withBrowserSessionLease(this.runtime, runtimeSession.session.metadata, {
|
|
177
|
+
instanceId: this.instanceId,
|
|
178
|
+
persistence: this.ephemeralSessions.has(runtimeSession.session.id)
|
|
179
|
+
? 'ephemeral'
|
|
180
|
+
: 'persistent'
|
|
181
|
+
})
|
|
182
|
+
})));
|
|
183
|
+
}
|
|
184
|
+
/** Stop the cleanup interval */
|
|
185
|
+
stopCleanupInterval() {
|
|
186
|
+
this.stopCleanupTimer?.();
|
|
187
|
+
this.stopCleanupTimer = null;
|
|
188
|
+
}
|
|
189
|
+
/** Update activity timestamp for a session */
|
|
190
|
+
touchSession(sessionId) {
|
|
191
|
+
this.sessionActivity.set(sessionId, Date.now());
|
|
192
|
+
}
|
|
193
|
+
/** Get oldest idle session ID */
|
|
194
|
+
getOldestIdleSession() {
|
|
195
|
+
let oldest = null;
|
|
196
|
+
for (const [id, time] of this.sessionActivity.entries()) {
|
|
197
|
+
if (!oldest || time < oldest.time) {
|
|
198
|
+
oldest = { id, time };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return oldest?.id ?? null;
|
|
202
|
+
}
|
|
203
|
+
/** Close sessions that have been idle too long */
|
|
204
|
+
async cleanupIdleSessions() {
|
|
205
|
+
const now = Date.now();
|
|
206
|
+
const toClose = [];
|
|
207
|
+
for (const [id, lastActivity] of this.sessionActivity.entries()) {
|
|
208
|
+
if (now - lastActivity > this.idleTimeout) {
|
|
209
|
+
toClose.push(id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const results = await Promise.allSettled(toClose.map((id) => this.closeSession(id)));
|
|
213
|
+
const failures = [];
|
|
214
|
+
for (const [index, result] of results.entries()) {
|
|
215
|
+
if (result.status === 'rejected')
|
|
216
|
+
failures.push(cleanupFailure(`idle-session:${toClose[index]}`, CaughtFailureSchema.parse(result.reason)));
|
|
217
|
+
}
|
|
218
|
+
throwCleanupFailures(failures, 'Idle browser session cleanup');
|
|
219
|
+
return toClose.length;
|
|
220
|
+
}
|
|
221
|
+
/** Ensure capacity, closing oldest idle session if needed */
|
|
222
|
+
async ensureCapacity() {
|
|
223
|
+
if (this.sessions.size < this.maxActive)
|
|
224
|
+
return;
|
|
225
|
+
// Try to close oldest idle session first
|
|
226
|
+
const oldest = this.getOldestIdleSession();
|
|
227
|
+
if (oldest) {
|
|
228
|
+
await this.closeSession(oldest);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
throw new Error(`Maximum active browser sessions (${this.maxActive}) exceeded`);
|
|
232
|
+
}
|
|
233
|
+
async getOrCreateSession(sessionOptions, launchOptions) {
|
|
234
|
+
// Validate host-declared trust before creating records, profiles, or browsers.
|
|
235
|
+
sessionBaseOrigins(sessionOptions?.baseUrl);
|
|
236
|
+
await this.ensureStorage();
|
|
237
|
+
const mode = sessionOptions?.mode ?? 'ephemeral';
|
|
238
|
+
const shouldPersist = sessionOptions?.persist === true || mode !== 'ephemeral';
|
|
239
|
+
if (shouldPersist && !this.backend.capabilities.storageStateFile) {
|
|
240
|
+
throw new CapabilityError(`Browser backend '${this.backend.id}' does not support persistent session storage`, 'UNAVAILABLE', { reason: 'browser-backend-capability-unsupported', details: { backendId: this.backend.id, feature: 'storage-state' } });
|
|
241
|
+
}
|
|
242
|
+
if (sessionOptions?.profile && !this.backend.capabilities.profiles) {
|
|
243
|
+
throw new CapabilityError(`Browser backend '${this.backend.id}' does not support profiles`, 'UNAVAILABLE', { reason: 'browser-backend-capability-unsupported', details: { backendId: this.backend.id } });
|
|
244
|
+
}
|
|
245
|
+
if (launchOptions?.recordVideo?.enabled && !this.backend.capabilities.recording) {
|
|
246
|
+
throw new CapabilityError(`Browser backend '${this.backend.id}' does not support video recording`, 'UNAVAILABLE', { reason: 'browser-backend-capability-unsupported', details: { backendId: this.backend.id } });
|
|
247
|
+
}
|
|
248
|
+
const requestedIdOrName = sessionOptions?.id ?? sessionOptions?.name ?? '';
|
|
249
|
+
if (requestedIdOrName) {
|
|
250
|
+
const existing = this.sessions.get(requestedIdOrName);
|
|
251
|
+
if (existing)
|
|
252
|
+
return existing;
|
|
253
|
+
const aliasId = this.sessionAliases.get(requestedIdOrName);
|
|
254
|
+
const aliased = aliasId ? this.sessions.get(aliasId) : undefined;
|
|
255
|
+
if (aliased)
|
|
256
|
+
return aliased;
|
|
257
|
+
}
|
|
258
|
+
let sessionRecord = null;
|
|
259
|
+
if (requestedIdOrName && shouldPersist) {
|
|
260
|
+
sessionRecord = await this.sessionsStorage.get(requestedIdOrName);
|
|
261
|
+
// Fall back to name-based lookup when requestedIdOrName is not a UUID
|
|
262
|
+
if (!sessionRecord) {
|
|
263
|
+
const byName = await this.sessionsStorage.list({
|
|
264
|
+
where: { name: requestedIdOrName },
|
|
265
|
+
limit: 1
|
|
266
|
+
});
|
|
267
|
+
sessionRecord = byName.items[0] ?? null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!sessionRecord && mode === 'load' && shouldPersist) {
|
|
271
|
+
throw new Error(`Browser session not found: ${requestedIdOrName}`);
|
|
272
|
+
}
|
|
273
|
+
if (!sessionRecord) {
|
|
274
|
+
await this.ensureCapacity();
|
|
275
|
+
const sessionId = sessionOptions?.id ?? crypto.randomUUID();
|
|
276
|
+
const name = sessionOptions?.name ?? `browser-session-${sessionId.slice(0, 8)}`;
|
|
277
|
+
const storageDir = resolveStorageDir(this.workspacePath);
|
|
278
|
+
const storageStatePath = shouldPersist ? `${storageDir}/${sessionId}.json` : undefined;
|
|
279
|
+
let profileId;
|
|
280
|
+
let profileDir;
|
|
281
|
+
if (sessionOptions?.profile?.name || sessionOptions?.profile?.id) {
|
|
282
|
+
const profileKey = sessionOptions.profile.id ?? sessionOptions.profile.name ?? '';
|
|
283
|
+
const profileMode = sessionOptions.profile.mode ?? 'upsert';
|
|
284
|
+
const existingProfile = profileKey ? await this.profilesStorage.get(profileKey) : null;
|
|
285
|
+
if (!existingProfile && profileMode === 'load') {
|
|
286
|
+
throw new Error(`Browser profile not found: ${profileKey}`);
|
|
287
|
+
}
|
|
288
|
+
if (existingProfile) {
|
|
289
|
+
profileId = existingProfile.id;
|
|
290
|
+
profileDir = existingProfile.dirPath;
|
|
291
|
+
await this.profilesStorage.update(existingProfile.id, {
|
|
292
|
+
lastUsedAt: new Date().toISOString()
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const newProfileId = sessionOptions.profile.id ?? crypto.randomUUID();
|
|
297
|
+
profileDir = `${resolveProfilesDir(this.workspacePath)}/${newProfileId}`;
|
|
298
|
+
const newProfile = await this.profilesStorage.create({
|
|
299
|
+
id: newProfileId,
|
|
300
|
+
name: sessionOptions.profile.name ?? `profile-${newProfileId.slice(0, 8)}`,
|
|
301
|
+
dirPath: profileDir,
|
|
302
|
+
createdAt: new Date().toISOString(),
|
|
303
|
+
lastUsedAt: new Date().toISOString()
|
|
304
|
+
});
|
|
305
|
+
profileId = newProfile.id;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Always persist session to DB to satisfy FK constraint for tabs.
|
|
309
|
+
// Ephemeral sessions can be cleaned up when the session closes.
|
|
310
|
+
sessionRecord = await this.sessionsStorage.create({
|
|
311
|
+
id: sessionId,
|
|
312
|
+
name,
|
|
313
|
+
baseUrl: sessionOptions?.baseUrl,
|
|
314
|
+
profileId,
|
|
315
|
+
storageStatePath,
|
|
316
|
+
createdAt: new Date().toISOString(),
|
|
317
|
+
lastUsedAt: new Date().toISOString(),
|
|
318
|
+
status: 'active',
|
|
319
|
+
metadata: await withBrowserSessionLease(this.runtime, undefined, {
|
|
320
|
+
instanceId: this.instanceId,
|
|
321
|
+
persistence: shouldPersist ? 'persistent' : 'ephemeral'
|
|
322
|
+
})
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
const storageStatePath = sessionRecord.storageStatePath;
|
|
326
|
+
const hasStorageState = storageStatePath
|
|
327
|
+
? (await statChecked(await resolveFileSystemAdapter(this.runtime), storageStatePath)).found
|
|
328
|
+
: false;
|
|
329
|
+
const profileDir = sessionRecord.profileId
|
|
330
|
+
? (await this.profilesStorage.get(sessionRecord.profileId))?.dirPath
|
|
331
|
+
: undefined;
|
|
332
|
+
const launched = await this.backend.launch(toBackendLaunchInput(this.runtime, launchOptions, {
|
|
333
|
+
profileDir,
|
|
334
|
+
extensions: sessionOptions?.extensions,
|
|
335
|
+
storageStatePath: hasStorageState ? storageStatePath : undefined,
|
|
336
|
+
trustedOrigins: sessionTrustedOriginList(sessionRecord.baseUrl)
|
|
337
|
+
}));
|
|
338
|
+
const runtimeSession = {
|
|
339
|
+
session: sessionRecord,
|
|
340
|
+
context: launched.context,
|
|
341
|
+
persistent: launched.persistent
|
|
342
|
+
};
|
|
343
|
+
this.sessions.set(sessionRecord.id, runtimeSession);
|
|
344
|
+
this.sessionAliases.set(sessionRecord.name, sessionRecord.id);
|
|
345
|
+
this.touchSession(sessionRecord.id);
|
|
346
|
+
if (!shouldPersist) {
|
|
347
|
+
this.ephemeralSessions.add(sessionRecord.id);
|
|
348
|
+
}
|
|
349
|
+
await this.sessionsStorage.update(sessionRecord.id, {
|
|
350
|
+
lastUsedAt: new Date().toISOString(),
|
|
351
|
+
status: 'active',
|
|
352
|
+
metadata: await withBrowserSessionLease(this.runtime, sessionRecord.metadata, {
|
|
353
|
+
instanceId: this.instanceId,
|
|
354
|
+
persistence: shouldPersist ? 'persistent' : 'ephemeral'
|
|
355
|
+
})
|
|
356
|
+
});
|
|
357
|
+
return runtimeSession;
|
|
358
|
+
}
|
|
359
|
+
async getOrCreateTab(runtimeSession, url, tabOptions) {
|
|
360
|
+
await this.ensureStorage();
|
|
361
|
+
const trustedOrigins = sessionTrustedOrigins(this.runtime, runtimeSession.session.baseUrl);
|
|
362
|
+
const tabId = tabOptions?.id;
|
|
363
|
+
const forceNew = tabOptions?.newTab === true;
|
|
364
|
+
// Return specific tab by ID if it exists
|
|
365
|
+
const existingTab = tabId ? this.tabs.get(tabId) : undefined;
|
|
366
|
+
if (existingTab && !forceNew)
|
|
367
|
+
return existingTab;
|
|
368
|
+
// Reuse the most recent open tab for this session when no tab is specified
|
|
369
|
+
if (!tabId && !forceNew) {
|
|
370
|
+
const sessionTab = this.getActiveTabForSession(runtimeSession.session.id);
|
|
371
|
+
if (sessionTab) {
|
|
372
|
+
// Navigate if URL provided and different from current
|
|
373
|
+
if (url && sessionTab.page.url() !== url) {
|
|
374
|
+
await sessionTab.page.goto(assertSafeBrowserUrl(url, trustedOrigins), {
|
|
375
|
+
waitUntil: 'domcontentloaded',
|
|
376
|
+
timeout: 30000
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
this.touchSession(runtimeSession.session.id);
|
|
380
|
+
return sessionTab;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const page = await runtimeSession.context.newPage();
|
|
384
|
+
if (url) {
|
|
385
|
+
await page.goto(assertSafeBrowserUrl(url, trustedOrigins), {
|
|
386
|
+
waitUntil: 'domcontentloaded',
|
|
387
|
+
timeout: 30000
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
const newTabId = forceNew ? crypto.randomUUID() : (tabId ?? crypto.randomUUID());
|
|
391
|
+
const tabRecord = await this.tabsStorage.create({
|
|
392
|
+
id: newTabId,
|
|
393
|
+
sessionId: runtimeSession.session.id,
|
|
394
|
+
url: url ?? undefined,
|
|
395
|
+
createdAt: new Date().toISOString(),
|
|
396
|
+
lastActiveAt: new Date().toISOString(),
|
|
397
|
+
status: 'open'
|
|
398
|
+
});
|
|
399
|
+
const runtimeTab = { tab: tabRecord, page };
|
|
400
|
+
this.tabs.set(newTabId, runtimeTab);
|
|
401
|
+
this.touchSession(runtimeSession.session.id);
|
|
402
|
+
return runtimeTab;
|
|
403
|
+
}
|
|
404
|
+
/** Find the most recent active tab for a session */
|
|
405
|
+
getActiveTabForSession(sessionId) {
|
|
406
|
+
let latestTab;
|
|
407
|
+
let latestTime = 0;
|
|
408
|
+
for (const tab of this.tabs.values()) {
|
|
409
|
+
if (tab.tab.sessionId === sessionId && tab.tab.status === 'open') {
|
|
410
|
+
const time = new Date(tab.tab.lastActiveAt).getTime();
|
|
411
|
+
if (!latestTab || time > latestTime) {
|
|
412
|
+
latestTab = tab;
|
|
413
|
+
latestTime = time;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return latestTab;
|
|
418
|
+
}
|
|
419
|
+
async updateTabUrl(tabId, page) {
|
|
420
|
+
await this.ensureStorage();
|
|
421
|
+
await this.tabsStorage.update(tabId, {
|
|
422
|
+
url: page.url(),
|
|
423
|
+
status: 'open',
|
|
424
|
+
lastActiveAt: new Date().toISOString()
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
async closeTab(tabId) {
|
|
428
|
+
await this.ensureStorage();
|
|
429
|
+
const runtimeTab = this.tabs.get(tabId);
|
|
430
|
+
if (!runtimeTab)
|
|
431
|
+
return;
|
|
432
|
+
if (!this.closedTabs.has(tabId)) {
|
|
433
|
+
await runtimeTab.page.close();
|
|
434
|
+
this.closedTabs.add(tabId);
|
|
435
|
+
}
|
|
436
|
+
await this.tabsStorage.update(tabId, {
|
|
437
|
+
status: 'closed',
|
|
438
|
+
lastActiveAt: new Date().toISOString()
|
|
439
|
+
});
|
|
440
|
+
this.tabs.delete(tabId);
|
|
441
|
+
this.closedTabs.delete(tabId);
|
|
442
|
+
}
|
|
443
|
+
async closeSession(sessionId) {
|
|
444
|
+
const runtimeSession = this.sessions.get(sessionId);
|
|
445
|
+
if (!runtimeSession)
|
|
446
|
+
return;
|
|
447
|
+
const failures = [];
|
|
448
|
+
const wasEphemeral = this.ephemeralSessions.has(sessionId);
|
|
449
|
+
const sessionTabs = [...this.tabs.entries()].filter(([, tab]) => tab.tab.sessionId === sessionId);
|
|
450
|
+
if (!this.closedContexts.has(sessionId)) {
|
|
451
|
+
const results = await Promise.allSettled(sessionTabs.map(async ([tabId, tab]) => {
|
|
452
|
+
if (!this.closedTabs.has(tabId)) {
|
|
453
|
+
await tab.page.close();
|
|
454
|
+
this.closedTabs.add(tabId);
|
|
455
|
+
}
|
|
456
|
+
}));
|
|
457
|
+
for (const [index, result] of results.entries()) {
|
|
458
|
+
if (result.status === 'rejected') {
|
|
459
|
+
failures.push(cleanupFailure(`session:${sessionId}:tab:${sessionTabs[index]?.[0]}`, CaughtFailureSchema.parse(result.reason)));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const closed = await settle(() => runtimeSession.context.close());
|
|
463
|
+
if (closed.ok) {
|
|
464
|
+
this.closedContexts.add(sessionId);
|
|
465
|
+
for (const [tabId] of sessionTabs)
|
|
466
|
+
this.closedTabs.add(tabId);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
failures.push(cleanupFailure(`session:${sessionId}:context`, closed.cause));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
throwCleanupFailures(failures, `Browser session '${sessionId}' cleanup`);
|
|
473
|
+
// Keep the complete session and tab custody until every storage operation
|
|
474
|
+
// succeeds. A retry skips resources already closed, then retries persistence.
|
|
475
|
+
await this.ensureStorage();
|
|
476
|
+
for (const [tabId] of sessionTabs) {
|
|
477
|
+
if (wasEphemeral)
|
|
478
|
+
await this.tabsStorage.delete(tabId);
|
|
479
|
+
else
|
|
480
|
+
await this.tabsStorage.update(tabId, { status: 'closed', lastActiveAt: new Date().toISOString() });
|
|
481
|
+
}
|
|
482
|
+
if (wasEphemeral)
|
|
483
|
+
await this.sessionsStorage.delete(sessionId);
|
|
484
|
+
else
|
|
485
|
+
await this.sessionsStorage.update(sessionId, {
|
|
486
|
+
status: 'closed',
|
|
487
|
+
metadata: closeBrowserSessionLease(runtimeSession.session.metadata, 'closed')
|
|
488
|
+
});
|
|
489
|
+
for (const [tabId] of sessionTabs) {
|
|
490
|
+
this.tabs.delete(tabId);
|
|
491
|
+
this.closedTabs.delete(tabId);
|
|
492
|
+
}
|
|
493
|
+
this.closedContexts.delete(sessionId);
|
|
494
|
+
this.ephemeralSessions.delete(sessionId);
|
|
495
|
+
this.sessions.delete(sessionId);
|
|
496
|
+
this.sessionAliases.delete(runtimeSession.session.name);
|
|
497
|
+
this.sessionActivity.delete(sessionId);
|
|
498
|
+
}
|
|
499
|
+
async persistSessionState(runtimeSession) {
|
|
500
|
+
if (!this.backend.capabilities.storageStateFile) {
|
|
501
|
+
throw new CapabilityError(`Browser backend '${this.backend.id}' does not support persistent session storage`, 'UNAVAILABLE', { reason: 'browser-backend-capability-unsupported', details: { backendId: this.backend.id, feature: 'storage-state' } });
|
|
502
|
+
}
|
|
503
|
+
await this.ensureStorage();
|
|
504
|
+
let path = runtimeSession.session.storageStatePath;
|
|
505
|
+
if (!path) {
|
|
506
|
+
// Upgrade ephemeral session to persistent: assign a storage path and stop treating
|
|
507
|
+
// it as ephemeral so closeSession won't delete the DB record on cleanup.
|
|
508
|
+
const storageDir = resolveStorageDir(this.workspacePath);
|
|
509
|
+
await (await resolveFileSystemAdapter(this.runtime)).mkdir(storageDir, { recursive: true });
|
|
510
|
+
path = `${storageDir}/${runtimeSession.session.id}.json`;
|
|
511
|
+
}
|
|
512
|
+
await runtimeSession.context.storageState({ path });
|
|
513
|
+
await this.sessionsStorage.update(runtimeSession.session.id, {
|
|
514
|
+
storageStatePath: path,
|
|
515
|
+
lastUsedAt: new Date().toISOString()
|
|
516
|
+
});
|
|
517
|
+
runtimeSession.session.storageStatePath = path;
|
|
518
|
+
this.ephemeralSessions.delete(runtimeSession.session.id);
|
|
519
|
+
this.touchSession(runtimeSession.session.id);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Get a runtime session by ID/name from memory, or re-load it from the DB
|
|
523
|
+
* (re-creating its Playwright context) if the MCP server restarted.
|
|
524
|
+
*/
|
|
525
|
+
async getOrLoadSession(idOrName) {
|
|
526
|
+
// Check in-memory first
|
|
527
|
+
const inMemory = this.getRuntimeSession(idOrName);
|
|
528
|
+
if (inMemory)
|
|
529
|
+
return inMemory;
|
|
530
|
+
if (!this.backend.capabilities.storageStateFile) {
|
|
531
|
+
throw new Error(`Browser backend '${this.backend.id}' does not support loading persistent sessions`);
|
|
532
|
+
}
|
|
533
|
+
// Try to load from DB by ID or name
|
|
534
|
+
await this.ensureStorage();
|
|
535
|
+
let sessionRecord = await this.sessionsStorage.get(idOrName);
|
|
536
|
+
if (!sessionRecord) {
|
|
537
|
+
const byName = await this.sessionsStorage.list({ where: { name: idOrName }, limit: 1 });
|
|
538
|
+
sessionRecord = byName.items[0] ?? null;
|
|
539
|
+
}
|
|
540
|
+
if (!sessionRecord)
|
|
541
|
+
return null;
|
|
542
|
+
// Re-create the Playwright context from stored state
|
|
543
|
+
const storageStatePath = sessionRecord.storageStatePath;
|
|
544
|
+
const hasStorageState = storageStatePath
|
|
545
|
+
? (await statChecked(await resolveFileSystemAdapter(this.runtime), storageStatePath)).found
|
|
546
|
+
: false;
|
|
547
|
+
const launched = await this.backend.launch(toBackendLaunchInput(this.runtime, { headless: true }, {
|
|
548
|
+
storageStatePath: hasStorageState ? storageStatePath : undefined,
|
|
549
|
+
trustedOrigins: sessionTrustedOriginList(sessionRecord.baseUrl)
|
|
550
|
+
}));
|
|
551
|
+
const runtimeSession = {
|
|
552
|
+
session: sessionRecord,
|
|
553
|
+
context: launched.context,
|
|
554
|
+
persistent: launched.persistent
|
|
555
|
+
};
|
|
556
|
+
this.sessions.set(sessionRecord.id, runtimeSession);
|
|
557
|
+
this.sessionAliases.set(sessionRecord.name, sessionRecord.id);
|
|
558
|
+
this.touchSession(sessionRecord.id);
|
|
559
|
+
await this.sessionsStorage.update(sessionRecord.id, { lastUsedAt: new Date().toISOString() });
|
|
560
|
+
return runtimeSession;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Present a session in a headed browser window and raise it to the foreground.
|
|
564
|
+
*
|
|
565
|
+
* Persists the session's cookies/storage state and opens a new headed browser
|
|
566
|
+
* pre-loaded with that state. On macOS, activates the window via System Events.
|
|
567
|
+
* Call dismissPresentation() to close the presentation browser.
|
|
568
|
+
*/
|
|
569
|
+
async presentSession(runtimeSessionInput, url) {
|
|
570
|
+
if (!this.backend.capabilities.present) {
|
|
571
|
+
throw new CapabilityError(`Browser backend '${this.backend.id}' does not support headed presentation`, 'UNAVAILABLE', { reason: 'browser-backend-capability-unsupported', details: { backendId: this.backend.id } });
|
|
572
|
+
}
|
|
573
|
+
const sessionId = runtimeSessionInput.session.id;
|
|
574
|
+
const presentedPages = this.presentationBrowser?.sessionId === sessionId
|
|
575
|
+
? this.presentationBrowser.context.pages()
|
|
576
|
+
: [];
|
|
577
|
+
const presentedUrl = presentedPages[presentedPages.length - 1]?.url();
|
|
578
|
+
const activeTabUrl = this.getActiveTabForSession(sessionId)?.page.url();
|
|
579
|
+
const current = this.sessions.get(sessionId);
|
|
580
|
+
const contextPages = current?.context === runtimeSessionInput.context
|
|
581
|
+
? await current.context.pages()
|
|
582
|
+
: [];
|
|
583
|
+
const contextUrl = contextPages[contextPages.length - 1]?.url();
|
|
584
|
+
const targetUrl = url ??
|
|
585
|
+
[presentedUrl, activeTabUrl, contextUrl, runtimeSessionInput.session.baseUrl].find((candidate) => candidate && candidate !== 'about:blank');
|
|
586
|
+
const safeTargetUrl = targetUrl
|
|
587
|
+
? assertSafeBrowserUrl(targetUrl, sessionTrustedOrigins(this.runtime, runtimeSessionInput.session.baseUrl))
|
|
588
|
+
: undefined;
|
|
589
|
+
// Close any existing presentation first (saves state before closing)
|
|
590
|
+
await this.dismissPresentation();
|
|
591
|
+
// Dismissal intentionally evicts the old headless context after copying the
|
|
592
|
+
// headed browser's auth state. Always re-resolve so repeated presentation
|
|
593
|
+
// never persists or navigates a context that dismissal just closed.
|
|
594
|
+
const runtimeSession = await this.getOrLoadSession(sessionId);
|
|
595
|
+
if (!runtimeSession) {
|
|
596
|
+
throw new Error(`Browser session not found: ${sessionId}`);
|
|
597
|
+
}
|
|
598
|
+
// Persist state so the presentation browser loads the same cookies/storage
|
|
599
|
+
await this.persistSessionState(runtimeSession);
|
|
600
|
+
const storageStatePath = runtimeSession.session.storageStatePath;
|
|
601
|
+
// Launch a headed browser with the saved state
|
|
602
|
+
const presRuntime = await this.presentationLauncher(this.runtime, {
|
|
603
|
+
options: { headless: false },
|
|
604
|
+
storageStatePath: storageStatePath ?? undefined
|
|
605
|
+
});
|
|
606
|
+
this.presentationBrowser = {
|
|
607
|
+
context: presRuntime.context,
|
|
608
|
+
browser: presRuntime.browser,
|
|
609
|
+
sessionId: runtimeSession.session.id,
|
|
610
|
+
storageStatePath
|
|
611
|
+
};
|
|
612
|
+
// Navigate to the target URL
|
|
613
|
+
const page = await presRuntime.context.newPage();
|
|
614
|
+
if (safeTargetUrl) {
|
|
615
|
+
await page.goto(safeTargetUrl, {
|
|
616
|
+
waitUntil: 'domcontentloaded',
|
|
617
|
+
timeout: 30000
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
// Bring to front within Playwright's context
|
|
621
|
+
await page.bringToFront();
|
|
622
|
+
// Retain custody before activation so a refused activation can still be disposed.
|
|
623
|
+
await this.presentationActivator();
|
|
624
|
+
return { url: page.url() };
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Dismiss the current presentation browser (if any).
|
|
628
|
+
* Saves the presentation browser's auth state back to the headless session before closing,
|
|
629
|
+
* so that any credentials entered in the headed browser are available to subsequent tool calls.
|
|
630
|
+
*/
|
|
631
|
+
async dismissPresentation(evictSession = true) {
|
|
632
|
+
if (!this.presentationBrowser)
|
|
633
|
+
return;
|
|
634
|
+
const presentation = this.presentationBrowser;
|
|
635
|
+
const { context, browser, sessionId } = presentation;
|
|
636
|
+
const failures = [];
|
|
637
|
+
// Auth state must reach storage before its only context can be discarded.
|
|
638
|
+
const runtimeSession = sessionId ? this.sessions.get(sessionId) : undefined;
|
|
639
|
+
const path = presentation.storageStatePath ?? runtimeSession?.session.storageStatePath;
|
|
640
|
+
if (path && !presentation.contextClosed)
|
|
641
|
+
await context.storageState({ path });
|
|
642
|
+
if (!presentation.contextClosed) {
|
|
643
|
+
const closed = await settle(() => context.close());
|
|
644
|
+
if (closed.ok)
|
|
645
|
+
presentation.contextClosed = true;
|
|
646
|
+
else
|
|
647
|
+
failures.push(cleanupFailure(`presentation:${sessionId ?? 'unbound'}:context`, closed.cause));
|
|
648
|
+
}
|
|
649
|
+
if (browser && !presentation.browserClosed) {
|
|
650
|
+
const closed = await settle(() => browser.close());
|
|
651
|
+
if (closed.ok)
|
|
652
|
+
presentation.browserClosed = true;
|
|
653
|
+
else
|
|
654
|
+
failures.push(cleanupFailure(`presentation:${sessionId ?? 'unbound'}:browser`, closed.cause));
|
|
655
|
+
}
|
|
656
|
+
throwCleanupFailures(failures, 'Browser presentation cleanup');
|
|
657
|
+
// Evict stale headless custody only after both presentation resources close.
|
|
658
|
+
if (runtimeSession && sessionId && evictSession) {
|
|
659
|
+
await runtimeSession.context.close();
|
|
660
|
+
this.sessions.delete(sessionId);
|
|
661
|
+
this.sessionAliases.delete(runtimeSession.session.name);
|
|
662
|
+
for (const [tabId, tab] of this.tabs) {
|
|
663
|
+
if (tab.tab.sessionId === sessionId) {
|
|
664
|
+
this.tabs.delete(tabId);
|
|
665
|
+
this.closedTabs.delete(tabId);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
this.sessionActivity.delete(sessionId);
|
|
669
|
+
}
|
|
670
|
+
this.presentationBrowser = null;
|
|
671
|
+
}
|
|
672
|
+
/** Close all active sessions under an explicit runtime/host lifecycle owner. */
|
|
673
|
+
closeAllSessions() {
|
|
674
|
+
if (this.closeAllPromise)
|
|
675
|
+
return this.closeAllPromise;
|
|
676
|
+
this.stopCleanupInterval();
|
|
677
|
+
const closeOperation = (async () => {
|
|
678
|
+
const failures = [];
|
|
679
|
+
const dismissed = await settle(() => this.dismissPresentation(false));
|
|
680
|
+
if (!dismissed.ok)
|
|
681
|
+
failures.push(cleanupFailure('presentation', dismissed.cause));
|
|
682
|
+
const sessionIds = [...this.sessions.keys()];
|
|
683
|
+
const results = await Promise.allSettled(sessionIds.map((id) => this.closeSession(id)));
|
|
684
|
+
for (let index = 0; index < results.length; index++) {
|
|
685
|
+
const result = results[index];
|
|
686
|
+
if (result?.status !== 'rejected')
|
|
687
|
+
continue;
|
|
688
|
+
const id = sessionIds[index] ?? `session-${index + 1}`;
|
|
689
|
+
failures.push(cleanupFailure(`session:${id}`, CaughtFailureSchema.parse(result.reason)));
|
|
690
|
+
}
|
|
691
|
+
throwCleanupFailures(failures, 'Browser manager cleanup');
|
|
692
|
+
})();
|
|
693
|
+
this.closeAllPromise = closeOperation.finally(() => {
|
|
694
|
+
this.closeAllPromise = null;
|
|
695
|
+
});
|
|
696
|
+
return this.closeAllPromise;
|
|
697
|
+
}
|
|
698
|
+
/** Get active session count */
|
|
699
|
+
getActiveSessionCount() {
|
|
700
|
+
return this.sessions.size;
|
|
701
|
+
}
|
|
702
|
+
/** Get session IDs with their last activity time */
|
|
703
|
+
getSessionActivity() {
|
|
704
|
+
return new Map(this.sessionActivity);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Get an active runtime session by ID or name.
|
|
708
|
+
* If idOrName is omitted, returns the most recently active session.
|
|
709
|
+
*/
|
|
710
|
+
getRuntimeSession(idOrName) {
|
|
711
|
+
if (!idOrName) {
|
|
712
|
+
// Most recently active session
|
|
713
|
+
let latestId = null;
|
|
714
|
+
let latestTime = 0;
|
|
715
|
+
for (const [id, time] of this.sessionActivity.entries()) {
|
|
716
|
+
if (time > latestTime) {
|
|
717
|
+
latestTime = time;
|
|
718
|
+
latestId = id;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return latestId ? this.sessions.get(latestId) : undefined;
|
|
722
|
+
}
|
|
723
|
+
if (this.sessions.has(idOrName))
|
|
724
|
+
return this.sessions.get(idOrName);
|
|
725
|
+
const aliasId = this.sessionAliases.get(idOrName);
|
|
726
|
+
if (aliasId)
|
|
727
|
+
return this.sessions.get(aliasId);
|
|
728
|
+
return undefined;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
//# sourceMappingURL=manager.js.map
|