Showing preview only (6,422K chars total). Download the full file or copy to clipboard to get everything.
Repository: hangwin/mcp-chrome
Branch: master
Commit: f48e71751e00
Files: 652
Total size: 37.5 MB
Directory structure:
gitextract_0pcja33u/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── build-release.yml
├── .gitignore
├── .husky/
│ ├── commit-msg
│ └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│ └── extensions.json
├── LICENSE
├── README.md
├── README_zh.md
├── app/
│ ├── chrome-extension/
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── _locales/
│ │ │ ├── de/
│ │ │ │ └── messages.json
│ │ │ ├── en/
│ │ │ │ └── messages.json
│ │ │ ├── ja/
│ │ │ │ └── messages.json
│ │ │ ├── ko/
│ │ │ │ └── messages.json
│ │ │ ├── zh_CN/
│ │ │ │ └── messages.json
│ │ │ └── zh_TW/
│ │ │ └── messages.json
│ │ ├── common/
│ │ │ ├── agent-models.ts
│ │ │ ├── constants.ts
│ │ │ ├── element-marker-types.ts
│ │ │ ├── message-types.ts
│ │ │ ├── node-types.ts
│ │ │ ├── rr-v3-keepalive-protocol.ts
│ │ │ ├── step-types.ts
│ │ │ ├── tool-handler.ts
│ │ │ └── web-editor-types.ts
│ │ ├── entrypoints/
│ │ │ ├── background/
│ │ │ │ ├── element-marker/
│ │ │ │ │ ├── element-marker-storage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── keepalive-manager.ts
│ │ │ │ ├── native-host.ts
│ │ │ │ ├── quick-panel/
│ │ │ │ │ ├── agent-handler.ts
│ │ │ │ │ ├── commands.ts
│ │ │ │ │ └── tabs-handler.ts
│ │ │ │ ├── record-replay/
│ │ │ │ │ ├── actions/
│ │ │ │ │ │ ├── adapter.ts
│ │ │ │ │ │ ├── handlers/
│ │ │ │ │ │ │ ├── assert.ts
│ │ │ │ │ │ │ ├── click.ts
│ │ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ │ ├── control-flow.ts
│ │ │ │ │ │ │ ├── delay.ts
│ │ │ │ │ │ │ ├── dom.ts
│ │ │ │ │ │ │ ├── drag.ts
│ │ │ │ │ │ │ ├── extract.ts
│ │ │ │ │ │ │ ├── fill.ts
│ │ │ │ │ │ │ ├── http.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── key.ts
│ │ │ │ │ │ │ ├── navigate.ts
│ │ │ │ │ │ │ ├── screenshot.ts
│ │ │ │ │ │ │ ├── script.ts
│ │ │ │ │ │ │ ├── scroll.ts
│ │ │ │ │ │ │ ├── tabs.ts
│ │ │ │ │ │ │ └── wait.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── registry.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── engine/
│ │ │ │ │ │ ├── constants.ts
│ │ │ │ │ │ ├── execution-mode.ts
│ │ │ │ │ │ ├── logging/
│ │ │ │ │ │ │ └── run-logger.ts
│ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ ├── breakpoint.ts
│ │ │ │ │ │ │ ├── manager.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ │ ├── policies/
│ │ │ │ │ │ │ ├── retry.ts
│ │ │ │ │ │ │ └── wait.ts
│ │ │ │ │ │ ├── runners/
│ │ │ │ │ │ │ ├── after-script-queue.ts
│ │ │ │ │ │ │ ├── control-flow-runner.ts
│ │ │ │ │ │ │ ├── step-executor.ts
│ │ │ │ │ │ │ ├── step-runner.ts
│ │ │ │ │ │ │ └── subflow-runner.ts
│ │ │ │ │ │ ├── scheduler.ts
│ │ │ │ │ │ ├── state-manager.ts
│ │ │ │ │ │ └── utils/
│ │ │ │ │ │ └── expression.ts
│ │ │ │ │ ├── flow-runner.ts
│ │ │ │ │ ├── flow-store.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── legacy-types.ts
│ │ │ │ │ ├── nodes/
│ │ │ │ │ │ ├── assert.ts
│ │ │ │ │ │ ├── click.ts
│ │ │ │ │ │ ├── conditional.ts
│ │ │ │ │ │ ├── download-screenshot-attr-event-frame-loop.ts
│ │ │ │ │ │ ├── drag.ts
│ │ │ │ │ │ ├── execute-flow.ts
│ │ │ │ │ │ ├── extract.ts
│ │ │ │ │ │ ├── fill.ts
│ │ │ │ │ │ ├── http.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── key.ts
│ │ │ │ │ │ ├── loops.ts
│ │ │ │ │ │ ├── navigate.ts
│ │ │ │ │ │ ├── script.ts
│ │ │ │ │ │ ├── scroll.ts
│ │ │ │ │ │ ├── tabs.ts
│ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ └── wait.ts
│ │ │ │ │ ├── recording/
│ │ │ │ │ │ ├── browser-event-listener.ts
│ │ │ │ │ │ ├── content-injection.ts
│ │ │ │ │ │ ├── content-message-handler.ts
│ │ │ │ │ │ ├── flow-builder.ts
│ │ │ │ │ │ ├── recorder-manager.ts
│ │ │ │ │ │ └── session-manager.ts
│ │ │ │ │ ├── rr-utils.ts
│ │ │ │ │ ├── selector-engine.ts
│ │ │ │ │ ├── storage/
│ │ │ │ │ │ └── indexeddb-manager.ts
│ │ │ │ │ ├── trigger-store.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── record-replay-v3/
│ │ │ │ │ ├── bootstrap.ts
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── debug.ts
│ │ │ │ │ │ ├── errors.ts
│ │ │ │ │ │ ├── events.ts
│ │ │ │ │ │ ├── flow.ts
│ │ │ │ │ │ ├── ids.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── json.ts
│ │ │ │ │ │ ├── policy.ts
│ │ │ │ │ │ ├── triggers.ts
│ │ │ │ │ │ └── variables.ts
│ │ │ │ │ ├── engine/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── keepalive/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── offscreen-keepalive.ts
│ │ │ │ │ │ ├── kernel/
│ │ │ │ │ │ │ ├── artifacts.ts
│ │ │ │ │ │ │ ├── breakpoints.ts
│ │ │ │ │ │ │ ├── debug-controller.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── kernel.ts
│ │ │ │ │ │ │ ├── recovery-kernel.ts
│ │ │ │ │ │ │ ├── runner.ts
│ │ │ │ │ │ │ └── traversal.ts
│ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── register-v2-replay-nodes.ts
│ │ │ │ │ │ │ ├── registry.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── v2-action-adapter.ts
│ │ │ │ │ │ ├── queue/
│ │ │ │ │ │ │ ├── enqueue-run.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── leasing.ts
│ │ │ │ │ │ │ ├── queue.ts
│ │ │ │ │ │ │ └── scheduler.ts
│ │ │ │ │ │ ├── recovery/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── recovery-coordinator.ts
│ │ │ │ │ │ ├── storage/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── storage-port.ts
│ │ │ │ │ │ ├── transport/
│ │ │ │ │ │ │ ├── events-bus.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── rpc-server.ts
│ │ │ │ │ │ │ └── rpc.ts
│ │ │ │ │ │ └── triggers/
│ │ │ │ │ │ ├── command-trigger.ts
│ │ │ │ │ │ ├── context-menu-trigger.ts
│ │ │ │ │ │ ├── cron-trigger.ts
│ │ │ │ │ │ ├── dom-trigger.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── interval-trigger.ts
│ │ │ │ │ │ ├── manual-trigger.ts
│ │ │ │ │ │ ├── once-trigger.ts
│ │ │ │ │ │ ├── trigger-handler.ts
│ │ │ │ │ │ ├── trigger-manager.ts
│ │ │ │ │ │ └── url-trigger.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── storage/
│ │ │ │ │ ├── db.ts
│ │ │ │ │ ├── events.ts
│ │ │ │ │ ├── flows.ts
│ │ │ │ │ ├── import/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── v2-reader.ts
│ │ │ │ │ │ └── v2-to-v3.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── persistent-vars.ts
│ │ │ │ │ ├── queue.ts
│ │ │ │ │ ├── runs.ts
│ │ │ │ │ └── triggers.ts
│ │ │ │ ├── semantic-similarity.ts
│ │ │ │ ├── storage-manager.ts
│ │ │ │ ├── tools/
│ │ │ │ │ ├── base-browser.ts
│ │ │ │ │ ├── browser/
│ │ │ │ │ │ ├── bookmark.ts
│ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ ├── computer.ts
│ │ │ │ │ │ ├── console-buffer.ts
│ │ │ │ │ │ ├── console.ts
│ │ │ │ │ │ ├── dialog.ts
│ │ │ │ │ │ ├── download.ts
│ │ │ │ │ │ ├── element-picker.ts
│ │ │ │ │ │ ├── file-upload.ts
│ │ │ │ │ │ ├── gif-auto-capture.ts
│ │ │ │ │ │ ├── gif-enhanced-renderer.ts
│ │ │ │ │ │ ├── gif-recorder.ts
│ │ │ │ │ │ ├── history.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── inject-script.ts
│ │ │ │ │ │ ├── interaction.ts
│ │ │ │ │ │ ├── javascript.ts
│ │ │ │ │ │ ├── keyboard.ts
│ │ │ │ │ │ ├── network-capture-debugger.ts
│ │ │ │ │ │ ├── network-capture-web-request.ts
│ │ │ │ │ │ ├── network-capture.ts
│ │ │ │ │ │ ├── network-request.ts
│ │ │ │ │ │ ├── performance.ts
│ │ │ │ │ │ ├── read-page.ts
│ │ │ │ │ │ ├── screenshot.ts
│ │ │ │ │ │ ├── userscript.ts
│ │ │ │ │ │ ├── vector-search.ts
│ │ │ │ │ │ ├── web-fetcher.ts
│ │ │ │ │ │ └── window.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── record-replay.ts
│ │ │ │ ├── utils/
│ │ │ │ │ └── sidepanel.ts
│ │ │ │ └── web-editor/
│ │ │ │ └── index.ts
│ │ │ ├── builder/
│ │ │ │ ├── App.vue
│ │ │ │ ├── index.html
│ │ │ │ └── main.ts
│ │ │ ├── content.ts
│ │ │ ├── element-picker.content.ts
│ │ │ ├── offscreen/
│ │ │ │ ├── gif-encoder.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ └── rr-keepalive.ts
│ │ │ ├── options/
│ │ │ │ ├── App.vue
│ │ │ │ ├── index.html
│ │ │ │ └── main.ts
│ │ │ ├── popup/
│ │ │ │ ├── App.vue
│ │ │ │ ├── components/
│ │ │ │ │ ├── ConfirmDialog.vue
│ │ │ │ │ ├── ElementMarkerManagement.vue
│ │ │ │ │ ├── LocalModelPage.vue
│ │ │ │ │ ├── ModelCacheManagement.vue
│ │ │ │ │ ├── ProgressIndicator.vue
│ │ │ │ │ ├── ScheduleDialog.vue
│ │ │ │ │ ├── builder/
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── Canvas.vue
│ │ │ │ │ │ │ ├── EdgePropertyPanel.vue
│ │ │ │ │ │ │ ├── KeyValueEditor.vue
│ │ │ │ │ │ │ ├── PropertyPanel.vue
│ │ │ │ │ │ │ ├── Sidebar.vue
│ │ │ │ │ │ │ ├── TriggerPanel.vue
│ │ │ │ │ │ │ ├── nodes/
│ │ │ │ │ │ │ │ ├── NodeCard.vue
│ │ │ │ │ │ │ │ ├── NodeIf.vue
│ │ │ │ │ │ │ │ └── node-util.ts
│ │ │ │ │ │ │ └── properties/
│ │ │ │ │ │ │ ├── PropertyAssert.vue
│ │ │ │ │ │ │ ├── PropertyClick.vue
│ │ │ │ │ │ │ ├── PropertyCloseTab.vue
│ │ │ │ │ │ │ ├── PropertyDelay.vue
│ │ │ │ │ │ │ ├── PropertyDrag.vue
│ │ │ │ │ │ │ ├── PropertyExecuteFlow.vue
│ │ │ │ │ │ │ ├── PropertyExtract.vue
│ │ │ │ │ │ │ ├── PropertyFill.vue
│ │ │ │ │ │ │ ├── PropertyForeach.vue
│ │ │ │ │ │ │ ├── PropertyFormRenderer.vue
│ │ │ │ │ │ │ ├── PropertyFromSpec.vue
│ │ │ │ │ │ │ ├── PropertyHandleDownload.vue
│ │ │ │ │ │ │ ├── PropertyHttp.vue
│ │ │ │ │ │ │ ├── PropertyIf.vue
│ │ │ │ │ │ │ ├── PropertyKey.vue
│ │ │ │ │ │ │ ├── PropertyLoopElements.vue
│ │ │ │ │ │ │ ├── PropertyNavigate.vue
│ │ │ │ │ │ │ ├── PropertyOpenTab.vue
│ │ │ │ │ │ │ ├── PropertyScreenshot.vue
│ │ │ │ │ │ │ ├── PropertyScript.vue
│ │ │ │ │ │ │ ├── PropertyScroll.vue
│ │ │ │ │ │ │ ├── PropertySetAttribute.vue
│ │ │ │ │ │ │ ├── PropertySwitchFrame.vue
│ │ │ │ │ │ │ ├── PropertySwitchTab.vue
│ │ │ │ │ │ │ ├── PropertyTrigger.vue
│ │ │ │ │ │ │ ├── PropertyTriggerEvent.vue
│ │ │ │ │ │ │ ├── PropertyWait.vue
│ │ │ │ │ │ │ ├── PropertyWhile.vue
│ │ │ │ │ │ │ └── SelectorEditor.vue
│ │ │ │ │ │ ├── model/
│ │ │ │ │ │ │ ├── form-widget-registry.ts
│ │ │ │ │ │ │ ├── node-spec-registry.ts
│ │ │ │ │ │ │ ├── node-spec.ts
│ │ │ │ │ │ │ ├── node-specs-builtin.ts
│ │ │ │ │ │ │ ├── toast.ts
│ │ │ │ │ │ │ ├── transforms.ts
│ │ │ │ │ │ │ ├── ui-nodes.ts
│ │ │ │ │ │ │ ├── validation.ts
│ │ │ │ │ │ │ └── variables.ts
│ │ │ │ │ │ ├── store/
│ │ │ │ │ │ │ └── useBuilderStore.ts
│ │ │ │ │ │ └── widgets/
│ │ │ │ │ │ ├── FieldCode.vue
│ │ │ │ │ │ ├── FieldDuration.vue
│ │ │ │ │ │ ├── FieldExpression.vue
│ │ │ │ │ │ ├── FieldKeySequence.vue
│ │ │ │ │ │ ├── FieldSelector.vue
│ │ │ │ │ │ ├── FieldTargetLocator.vue
│ │ │ │ │ │ └── VarInput.vue
│ │ │ │ │ └── icons/
│ │ │ │ │ ├── BoltIcon.vue
│ │ │ │ │ ├── CheckIcon.vue
│ │ │ │ │ ├── DatabaseIcon.vue
│ │ │ │ │ ├── DocumentIcon.vue
│ │ │ │ │ ├── EditIcon.vue
│ │ │ │ │ ├── MarkerIcon.vue
│ │ │ │ │ ├── RecordIcon.vue
│ │ │ │ │ ├── RefreshIcon.vue
│ │ │ │ │ ├── StopIcon.vue
│ │ │ │ │ ├── TabIcon.vue
│ │ │ │ │ ├── TrashIcon.vue
│ │ │ │ │ ├── VectorIcon.vue
│ │ │ │ │ ├── WorkflowIcon.vue
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ └── style.css
│ │ │ ├── quick-panel.content.ts
│ │ │ ├── shared/
│ │ │ │ ├── composables/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── useRRV3Rpc.ts
│ │ │ │ └── utils/
│ │ │ │ ├── index.ts
│ │ │ │ └── rr-flow-convert.ts
│ │ │ ├── sidepanel/
│ │ │ │ ├── App.vue
│ │ │ │ ├── components/
│ │ │ │ │ ├── AgentChat.vue
│ │ │ │ │ ├── SidepanelNavigator.vue
│ │ │ │ │ ├── agent/
│ │ │ │ │ │ ├── AttachmentPreview.vue
│ │ │ │ │ │ ├── ChatInput.vue
│ │ │ │ │ │ ├── CliSettings.vue
│ │ │ │ │ │ ├── ConnectionStatus.vue
│ │ │ │ │ │ ├── MessageItem.vue
│ │ │ │ │ │ ├── MessageList.vue
│ │ │ │ │ │ ├── ProjectCreateForm.vue
│ │ │ │ │ │ ├── ProjectSelector.vue
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── agent-chat/
│ │ │ │ │ │ ├── AgentChatShell.vue
│ │ │ │ │ │ ├── AgentComposer.vue
│ │ │ │ │ │ ├── AgentConversation.vue
│ │ │ │ │ │ ├── AgentOpenProjectMenu.vue
│ │ │ │ │ │ ├── AgentProjectMenu.vue
│ │ │ │ │ │ ├── AgentRequestThread.vue
│ │ │ │ │ │ ├── AgentSessionListItem.vue
│ │ │ │ │ │ ├── AgentSessionMenu.vue
│ │ │ │ │ │ ├── AgentSessionSettingsPanel.vue
│ │ │ │ │ │ ├── AgentSessionsView.vue
│ │ │ │ │ │ ├── AgentSettingsMenu.vue
│ │ │ │ │ │ ├── AgentTimeline.vue
│ │ │ │ │ │ ├── AgentTimelineItem.vue
│ │ │ │ │ │ ├── AgentTopBar.vue
│ │ │ │ │ │ ├── ApplyMessageChip.vue
│ │ │ │ │ │ ├── AttachmentCachePanel.vue
│ │ │ │ │ │ ├── ComposerDrawer.vue
│ │ │ │ │ │ ├── ElementChip.vue
│ │ │ │ │ │ ├── FakeCaretOverlay.vue
│ │ │ │ │ │ ├── SelectionChip.vue
│ │ │ │ │ │ ├── WebEditorChanges.vue
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── timeline/
│ │ │ │ │ │ ├── ThinkingNode.vue
│ │ │ │ │ │ ├── TimelineNarrativeStep.vue
│ │ │ │ │ │ ├── TimelineStatusStep.vue
│ │ │ │ │ │ ├── TimelineToolCallStep.vue
│ │ │ │ │ │ ├── TimelineToolResultCardStep.vue
│ │ │ │ │ │ ├── TimelineUserPromptStep.vue
│ │ │ │ │ │ └── markstream-thinking.ts
│ │ │ │ │ ├── rr-v3/
│ │ │ │ │ │ └── DebuggerPanel.vue
│ │ │ │ │ └── workflows/
│ │ │ │ │ ├── WorkflowListItem.vue
│ │ │ │ │ ├── WorkflowsView.vue
│ │ │ │ │ └── index.ts
│ │ │ │ ├── composables/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useAgentChat.ts
│ │ │ │ │ ├── useAgentChatViewRoute.ts
│ │ │ │ │ ├── useAgentInputPreferences.ts
│ │ │ │ │ ├── useAgentProjects.ts
│ │ │ │ │ ├── useAgentServer.ts
│ │ │ │ │ ├── useAgentSessions.ts
│ │ │ │ │ ├── useAgentTheme.ts
│ │ │ │ │ ├── useAgentThreads.ts
│ │ │ │ │ ├── useAttachments.ts
│ │ │ │ │ ├── useFakeCaret.ts
│ │ │ │ │ ├── useFloatingDrag.ts
│ │ │ │ │ ├── useOpenProjectPreference.ts
│ │ │ │ │ ├── useRRV3Debugger.ts
│ │ │ │ │ ├── useRRV3Rpc.ts
│ │ │ │ │ ├── useTextareaAutoResize.ts
│ │ │ │ │ ├── useWebEditorTxState.ts
│ │ │ │ │ └── useWorkflowsV3.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ ├── styles/
│ │ │ │ │ └── agent-chat.css
│ │ │ │ └── utils/
│ │ │ │ └── loading-texts.ts
│ │ │ ├── styles/
│ │ │ │ └── tailwind.css
│ │ │ ├── web-editor-v2/
│ │ │ │ ├── attr-ui-refactor.md
│ │ │ │ ├── constants.ts
│ │ │ │ ├── core/
│ │ │ │ │ ├── css-compare.ts
│ │ │ │ │ ├── cssom-styles-collector.ts
│ │ │ │ │ ├── debug-source.ts
│ │ │ │ │ ├── design-tokens/
│ │ │ │ │ │ ├── design-tokens-service.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── token-detector.ts
│ │ │ │ │ │ ├── token-resolver.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── editor.ts
│ │ │ │ │ ├── element-key.ts
│ │ │ │ │ ├── event-controller.ts
│ │ │ │ │ ├── execution-tracker.ts
│ │ │ │ │ ├── hmr-consistency.ts
│ │ │ │ │ ├── locator.ts
│ │ │ │ │ ├── message-listener.ts
│ │ │ │ │ ├── payload-builder.ts
│ │ │ │ │ ├── perf-monitor.ts
│ │ │ │ │ ├── position-tracker.ts
│ │ │ │ │ ├── props-bridge.ts
│ │ │ │ │ ├── snap-engine.ts
│ │ │ │ │ ├── transaction-aggregator.ts
│ │ │ │ │ └── transaction-manager.ts
│ │ │ │ ├── drag/
│ │ │ │ │ └── drag-reorder-controller.ts
│ │ │ │ ├── overlay/
│ │ │ │ │ ├── canvas-overlay.ts
│ │ │ │ │ └── handles-controller.ts
│ │ │ │ ├── selection/
│ │ │ │ │ └── selection-engine.ts
│ │ │ │ ├── ui/
│ │ │ │ │ ├── breadcrumbs.ts
│ │ │ │ │ ├── floating-drag.ts
│ │ │ │ │ ├── icons.ts
│ │ │ │ │ ├── property-panel/
│ │ │ │ │ │ ├── class-editor.ts
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── alignment-grid.ts
│ │ │ │ │ │ │ ├── icon-button-group.ts
│ │ │ │ │ │ │ ├── input-container.ts
│ │ │ │ │ │ │ ├── slider-input.ts
│ │ │ │ │ │ │ └── token-pill.ts
│ │ │ │ │ │ ├── components-tree.ts
│ │ │ │ │ │ ├── controls/
│ │ │ │ │ │ │ ├── appearance-control.ts
│ │ │ │ │ │ │ ├── background-control.ts
│ │ │ │ │ │ │ ├── border-control.ts
│ │ │ │ │ │ │ ├── color-field.ts
│ │ │ │ │ │ │ ├── css-helpers.ts
│ │ │ │ │ │ │ ├── effects-control.ts
│ │ │ │ │ │ │ ├── gradient-control.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── layout-control.ts
│ │ │ │ │ │ │ ├── number-stepping.ts
│ │ │ │ │ │ │ ├── position-control.ts
│ │ │ │ │ │ │ ├── size-control.ts
│ │ │ │ │ │ │ ├── spacing-control.ts
│ │ │ │ │ │ │ ├── token-picker.ts
│ │ │ │ │ │ │ └── typography-control.ts
│ │ │ │ │ │ ├── css-defaults.ts
│ │ │ │ │ │ ├── css-panel.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── property-panel.ts
│ │ │ │ │ │ ├── props-panel.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── shadow-host.ts
│ │ │ │ │ └── toolbar.ts
│ │ │ │ └── utils/
│ │ │ │ └── disposables.ts
│ │ │ ├── web-editor-v2.ts
│ │ │ └── welcome/
│ │ │ ├── App.vue
│ │ │ ├── index.html
│ │ │ └── main.ts
│ │ ├── env.d.ts
│ │ ├── eslint.config.js
│ │ ├── inject-scripts/
│ │ │ ├── accessibility-tree-helper.js
│ │ │ ├── click-helper.js
│ │ │ ├── dom-observer.js
│ │ │ ├── element-marker.js
│ │ │ ├── element-picker.js
│ │ │ ├── fill-helper.js
│ │ │ ├── inject-bridge.js
│ │ │ ├── interactive-elements-helper.js
│ │ │ ├── keyboard-helper.js
│ │ │ ├── network-helper.js
│ │ │ ├── props-agent.js
│ │ │ ├── recorder.js
│ │ │ ├── screenshot-helper.js
│ │ │ ├── wait-helper.js
│ │ │ ├── web-editor.js
│ │ │ └── web-fetcher-helper.js
│ │ ├── package.json
│ │ ├── shared/
│ │ │ ├── element-picker/
│ │ │ │ ├── controller.ts
│ │ │ │ └── index.ts
│ │ │ ├── quick-panel/
│ │ │ │ ├── core/
│ │ │ │ │ ├── agent-bridge.ts
│ │ │ │ │ ├── search-engine.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── providers/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── tabs-provider.ts
│ │ │ │ └── ui/
│ │ │ │ ├── ai-chat-panel.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── markdown-renderer.ts
│ │ │ │ ├── message-renderer.ts
│ │ │ │ ├── panel-shell.ts
│ │ │ │ ├── quick-entries.ts
│ │ │ │ ├── search-input.ts
│ │ │ │ ├── shadow-host.ts
│ │ │ │ └── styles.ts
│ │ │ └── selector/
│ │ │ ├── dom-path.ts
│ │ │ ├── fingerprint.ts
│ │ │ ├── generator.ts
│ │ │ ├── index.ts
│ │ │ ├── locator.ts
│ │ │ ├── shadow-dom.ts
│ │ │ ├── stability.ts
│ │ │ ├── strategies/
│ │ │ │ ├── anchor-relpath.ts
│ │ │ │ ├── aria.ts
│ │ │ │ ├── css-path.ts
│ │ │ │ ├── css-unique.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── testid.ts
│ │ │ │ └── text.ts
│ │ │ └── types.ts
│ │ ├── tailwind.config.ts
│ │ ├── tests/
│ │ │ ├── __mocks__/
│ │ │ │ └── hnswlib-wasm-static.ts
│ │ │ ├── record-replay/
│ │ │ │ ├── _test-helpers.ts
│ │ │ │ ├── adapter-policy.contract.test.ts
│ │ │ │ ├── flow-store-strip-steps.contract.test.ts
│ │ │ │ ├── high-risk-actions.integration.test.ts
│ │ │ │ ├── hybrid-actions.integration.test.ts
│ │ │ │ ├── script-control-flow.integration.test.ts
│ │ │ │ ├── session-dag-sync.contract.test.ts
│ │ │ │ ├── step-executor.contract.test.ts
│ │ │ │ └── tab-cursor.integration.test.ts
│ │ │ ├── record-replay-v3/
│ │ │ │ ├── command-trigger.test.ts
│ │ │ │ ├── context-menu-trigger.test.ts
│ │ │ │ ├── cron-trigger.test.ts
│ │ │ │ ├── debugger.contract.test.ts
│ │ │ │ ├── dom-trigger.test.ts
│ │ │ │ ├── e2e.integration.test.ts
│ │ │ │ ├── events.contract.test.ts
│ │ │ │ ├── interval-trigger.test.ts
│ │ │ │ ├── manual-trigger.test.ts
│ │ │ │ ├── once-trigger.test.ts
│ │ │ │ ├── queue.contract.test.ts
│ │ │ │ ├── recovery.test.ts
│ │ │ │ ├── rpc-api.test.ts
│ │ │ │ ├── runner.onError.contract.test.ts
│ │ │ │ ├── scheduler-integration.test.ts
│ │ │ │ ├── scheduler.test.ts
│ │ │ │ ├── spec-smoke.test.ts
│ │ │ │ ├── trigger-manager.test.ts
│ │ │ │ ├── triggers.test.ts
│ │ │ │ ├── url-trigger.test.ts
│ │ │ │ ├── v2-action-adapter.test.ts
│ │ │ │ ├── v2-adapter-integration.test.ts
│ │ │ │ ├── v2-to-v3-conversion.test.ts
│ │ │ │ └── v3-e2e-harness.ts
│ │ │ ├── vitest.setup.ts
│ │ │ └── web-editor-v2/
│ │ │ ├── design-tokens.test.ts
│ │ │ ├── drag-reorder-controller.test.ts
│ │ │ ├── event-controller.test.ts
│ │ │ ├── locator.test.ts
│ │ │ ├── property-panel-live-sync.test.ts
│ │ │ ├── selection-engine.test.ts
│ │ │ ├── snap-engine.test.ts
│ │ │ └── test-utils/
│ │ │ └── dom.ts
│ │ ├── tsconfig.json
│ │ ├── types/
│ │ │ ├── gifenc.d.ts
│ │ │ └── icons.d.ts
│ │ ├── utils/
│ │ │ ├── cdp-session-manager.ts
│ │ │ ├── content-indexer.ts
│ │ │ ├── i18n.ts
│ │ │ ├── image-utils.ts
│ │ │ ├── indexeddb-client.ts
│ │ │ ├── lru-cache.ts
│ │ │ ├── model-cache-manager.ts
│ │ │ ├── offscreen-manager.ts
│ │ │ ├── output-sanitizer.ts
│ │ │ ├── screenshot-context.ts
│ │ │ ├── semantic-similarity-engine.ts
│ │ │ ├── simd-math-engine.ts
│ │ │ ├── text-chunker.ts
│ │ │ └── vector-database.ts
│ │ ├── vitest.config.ts
│ │ ├── workers/
│ │ │ ├── ort-wasm-simd-threaded.jsep.mjs
│ │ │ ├── ort-wasm-simd-threaded.jsep.wasm
│ │ │ ├── ort-wasm-simd-threaded.mjs
│ │ │ ├── ort-wasm-simd-threaded.wasm
│ │ │ ├── simd_math.js
│ │ │ ├── simd_math_bg.wasm
│ │ │ └── similarity.worker.js
│ │ └── wxt.config.ts
│ └── native-server/
│ ├── .npmignore
│ ├── README.md
│ ├── debug.sh
│ ├── install.md
│ ├── jest.config.js
│ ├── package.json
│ ├── src/
│ │ ├── agent/
│ │ │ ├── attachment-service.ts
│ │ │ ├── ccr-detector.ts
│ │ │ ├── chat-service.ts
│ │ │ ├── db/
│ │ │ │ ├── client.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── schema.ts
│ │ │ ├── directory-picker.ts
│ │ │ ├── engines/
│ │ │ │ ├── claude.ts
│ │ │ │ ├── codex.ts
│ │ │ │ └── types.ts
│ │ │ ├── message-service.ts
│ │ │ ├── open-project.ts
│ │ │ ├── project-service.ts
│ │ │ ├── project-types.ts
│ │ │ ├── session-service.ts
│ │ │ ├── storage.ts
│ │ │ ├── stream-manager.ts
│ │ │ ├── tool-bridge.ts
│ │ │ └── types.ts
│ │ ├── cli.ts
│ │ ├── constant/
│ │ │ └── index.ts
│ │ ├── file-handler.ts
│ │ ├── index.ts
│ │ ├── mcp/
│ │ │ ├── mcp-server-stdio.ts
│ │ │ ├── mcp-server.ts
│ │ │ ├── register-tools.ts
│ │ │ └── stdio-config.json
│ │ ├── native-messaging-host.ts
│ │ ├── scripts/
│ │ │ ├── browser-config.ts
│ │ │ ├── build.ts
│ │ │ ├── constant.ts
│ │ │ ├── doctor.ts
│ │ │ ├── postinstall.ts
│ │ │ ├── register-dev.ts
│ │ │ ├── register.ts
│ │ │ ├── report.ts
│ │ │ ├── run_host.bat
│ │ │ ├── run_host.sh
│ │ │ └── utils.ts
│ │ ├── server/
│ │ │ ├── index.ts
│ │ │ ├── routes/
│ │ │ │ ├── agent.ts
│ │ │ │ └── index.ts
│ │ │ └── server.test.ts
│ │ ├── shims/
│ │ │ └── devtools.d.ts
│ │ ├── trace-analyzer.ts
│ │ ├── types/
│ │ │ └── devtools-frontend.d.ts
│ │ └── util/
│ │ └── logger.ts
│ └── tsconfig.json
├── commitlint.config.cjs
├── docs/
│ ├── ARCHITECTURE.md
│ ├── ARCHITECTURE_zh.md
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── CONTRIBUTING_zh.md
│ ├── ISSUE.md
│ ├── TOOLS.md
│ ├── TOOLS_zh.md
│ ├── TROUBLESHOOTING.md
│ ├── TROUBLESHOOTING_zh.md
│ ├── VisualEditor.md
│ ├── VisualEditor_zh.md
│ ├── WINDOWS_INSTALL_zh.md
│ └── mcp-cli-config.md
├── eslint.config.js
├── package.json
├── packages/
│ ├── shared/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── agent-types.ts
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ ├── labels.ts
│ │ │ ├── node-spec-registry.ts
│ │ │ ├── node-spec.ts
│ │ │ ├── node-specs-builtin.ts
│ │ │ ├── rr-graph.ts
│ │ │ ├── step-types.ts
│ │ │ ├── tools.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ └── wasm-simd/
│ ├── .gitignore
│ ├── BUILD.md
│ ├── Cargo.toml
│ ├── README.md
│ ├── package.json
│ └── src/
│ └── lib.rs
├── pnpm-workspace.yaml
├── prompt/
│ ├── content-analize.md
│ ├── excalidraw-prompt.md
│ └── modify-web.md
└── releases/
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.onnx filter=lfs diff=lfs merge=lfs -text
================================================
FILE: .github/workflows/build-release.yml
================================================
# name: Build and Release Chrome Extension
# on:
# push:
# branches: [ master, develop ]
# paths:
# - 'app/chrome-extension/**'
# pull_request:
# branches: [ master ]
# paths:
# - 'app/chrome-extension/**'
# workflow_dispatch:
# jobs:
# build-extension:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '18'
# cache: 'npm'
# cache-dependency-path: 'app/chrome-extension/package-lock.json'
# - name: Install dependencies
# run: |
# cd app/chrome-extension
# npm ci
# - name: Build extension
# run: |
# cd app/chrome-extension
# npm run build
# - name: Create zip package
# run: |
# cd app/chrome-extension
# npm run zip
# - name: Prepare release directory
# run: |
# mkdir -p releases/chrome-extension/latest
# mkdir -p releases/chrome-extension/$(date +%Y%m%d-%H%M%S)
# - name: Copy release files
# run: |
# # Copy to latest
# cp app/chrome-extension/.output/chrome-mv3-prod.zip releases/chrome-extension/latest/chrome-mcp-server-latest.zip
# # Copy to timestamped version
# TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# cp app/chrome-extension/.output/chrome-mv3-prod.zip releases/chrome-extension/$TIMESTAMP/chrome-mcp-server-$TIMESTAMP.zip
# - name: Upload build artifacts
# uses: actions/upload-artifact@v4
# with:
# name: chrome-extension-build
# path: releases/chrome-extension/
# retention-days: 30
# - name: Commit and push releases (if on main branch)
# if: github.ref == 'refs/heads/main'
# run: |
# git config --local user.email "action@github.com"
# git config --local user.name "GitHub Action"
# git add releases/
# git diff --staged --quiet || git commit -m "Auto-build: Update Chrome extension release [skip ci]"
# git push
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.output
stats.html
stats-*.json
.wxt
web-ext.config.ts
dist
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.onnx
# Environment variables
.env
.env.local
.env.*.local
# Prevent npm metadata pollution
false/
metadata-v1.3/
registry.npmmirror.com/
registry.npmjs.com/
other/
tools_optimize.md
Agents.md
CLAUDE.md
**/*/coverage/*
.docs/
.claude/
================================================
FILE: .husky/commit-msg
================================================
npx --no -- commitlint --edit "$1"
================================================
FILE: .husky/pre-commit
================================================
npx lint-staged
================================================
FILE: .prettierignore
================================================
# 构建输出目录
dist
.output
.wxt
# 依赖
node_modules
# 日志
logs
*.log
# 缓存
.cache
.temp
# 编辑器配置
.vscode
!.vscode/extensions.json
.idea
# 系统文件
.DS_Store
Thumbs.db
# 打包文件
*.zip
*.tar.gz
# 统计文件
stats.html
stats-*.json
# 锁文件
pnpm-lock.yaml
================================================
FILE: .prettierrc.json
================================================
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"printWidth": 100,
"endOfLine": "auto",
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "strict"
}
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": ["Vue.volar"]
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 hangye
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Chrome MCP Server 🚀
[](https://img.shields.io/github/stars/hangwin/mcp-chrome)
[](https://opensource.org/licenses/MIT)
[](https://www.typescriptlang.org/)
[](https://developer.chrome.com/docs/extensions/)
[](https://img.shields.io/github/v/release/hangwin/mcp-chrome.svg)
> 🌟 **Turn your Chrome browser into your intelligent assistant** - Let AI take control of your browser, transforming it into a powerful AI-controlled automation tool.
**📖 Documentation**: [English](README.md) | [中文](README_zh.md)
> The project is still in its early stages and is under intensive development. More features, stability improvements, and other enhancements will follow.
---
## 🎯 What is Chrome MCP Server?
Chrome MCP Server is a Chrome extension-based **Model Context Protocol (MCP) server** that exposes your Chrome browser functionality to AI assistants like Claude, enabling complex browser automation, content analysis, and semantic search. Unlike traditional browser automation tools (like Playwright), **Chrome MCP Server** directly uses your daily Chrome browser, leveraging existing user habits, configurations, and login states, allowing various large models or chatbots to take control of your browser and truly become your everyday assistant.
## ✨ New Features(2025/12/30)
- **A New Visual Editor for Claude Code & Codex**, for more detail here: [VisualEditor](docs/VisualEditor.md)
## ✨ Core Features
- 😁 **Chatbot/Model Agnostic**: Let any LLM or chatbot client or agent you prefer automate your browser
- ⭐️ **Use Your Original Browser**: Seamlessly integrate with your existing browser environment (your configurations, login states, etc.)
- 💻 **Fully Local**: Pure local MCP server ensuring user privacy
- 🚄 **Streamable HTTP**: Streamable HTTP connection method
- 🏎 **Cross-Tab**: Cross-tab context
- 🧠 **Semantic Search**: Built-in vector database for intelligent browser tab content discovery
- 🔍 **Smart Content Analysis**: AI-powered text extraction and similarity matching
- 🌐 **20+ Tools**: Support for screenshots, network monitoring, interactive operations, bookmark management, browsing history, and 20+ other tools
- 🚀 **SIMD-Accelerated AI**: Custom WebAssembly SIMD optimization for 4-8x faster vector operations
## 🆚 Comparison with Similar Projects
| Comparison Dimension | Playwright-based MCP Server | Chrome Extension-based MCP Server |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Resource Usage** | ❌ Requires launching independent browser process, installing Playwright dependencies, downloading browser binaries, etc. | ✅ No need to launch independent browser process, directly utilizes user's already open Chrome browser |
| **User Session Reuse** | ❌ Requires re-login | ✅ Automatically uses existing login state |
| **Browser Environment** | ❌ Clean environment lacks user settings | ✅ Fully preserves user environment |
| **API Access** | ⚠️ Limited to Playwright API | ✅ Full access to Chrome native APIs |
| **Startup Speed** | ❌ Requires launching browser process | ✅ Only needs to activate extension |
| **Response Speed** | 50-200ms inter-process communication | ✅ Faster |
## 🚀 Quick Start
### Prerequisites
- Node.js >= 20.0.0 and pnpm/npm
- Chrome/Chromium browser
### Installation Steps
1. **Download the latest Chrome extension from GitHub**
Download link: https://github.com/hangwin/mcp-chrome/releases
2. **Install mcp-chrome-bridge globally**
npm
```bash
npm install -g mcp-chrome-bridge
```
pnpm
```bash
# Method 1: Enable scripts globally (recommended)
pnpm config set enable-pre-post-scripts true
pnpm install -g mcp-chrome-bridge
# Method 2: Manual registration (if postinstall doesn't run)
pnpm install -g mcp-chrome-bridge
mcp-chrome-bridge register
```
> Note: pnpm v7+ disables postinstall scripts by default for security. The `enable-pre-post-scripts` setting controls whether pre/post install scripts run. If automatic registration fails, use the manual registration command above.
3. **Load Chrome Extension**
- Open Chrome and go to `chrome://extensions/`
- Enable "Developer mode"
- Click "Load unpacked" and select `your/dowloaded/extension/folder`
- Click the extension icon to open the plugin, then click connect to see the MCP configuration
<img width="475" alt="Screenshot 2025-06-09 15 52 06" src="https://github.com/user-attachments/assets/241e57b8-c55f-41a4-9188-0367293dc5bc" />
### Usage with MCP Protocol Clients
#### Using Streamable HTTP Connection (👍🏻 Recommended)
Add the following configuration to your MCP client configuration (using CherryStudio as an example):
> Streamable HTTP connection method is recommended
```json
{
"mcpServers": {
"chrome-mcp-server": {
"type": "streamableHttp",
"url": "http://127.0.0.1:12306/mcp"
}
}
}
```
#### Using STDIO Connection (Alternative)
If your client only supports stdio connection method, please use the following approach:
1. First, check the installation location of the npm package you just installed
```sh
# npm check method
npm list -g mcp-chrome-bridge
# pnpm check method
pnpm list -g mcp-chrome-bridge
```
Assuming the command above outputs the path: /Users/xxx/Library/pnpm/global/5
Then your final path would be: /Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js
2. Replace the configuration below with the final path you just obtained
```json
{
"mcpServers": {
"chrome-mcp-stdio": {
"command": "npx",
"args": [
"node",
"/Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js"
]
}
}
}
```
eg:config in augment:
<img width="494" alt="截屏2025-06-22 22 11 25" src="https://github.com/user-attachments/assets/48eefc0c-a257-4d3b-8bbe-d7ff716de2bf" />
## 🛠️ Available Tools
Complete tool list: [Complete Tool List](docs/TOOLS.md)
<details>
<summary><strong>📊 Browser Management (6 tools)</strong></summary>
- `get_windows_and_tabs` - List all browser windows and tabs
- `chrome_navigate` - Navigate to URLs and control viewport
- `chrome_switch_tab` - Switch the current active tab
- `chrome_close_tabs` - Close specific tabs or windows
- `chrome_go_back_or_forward` - Browser navigation control
- `chrome_inject_script` - Inject content scripts into web pages
- `chrome_send_command_to_inject_script` - Send commands to injected content scripts
</details>
<details>
<summary><strong>📸 Screenshots & Visual (1 tool)</strong></summary>
- `chrome_screenshot` - Advanced screenshot capture with element targeting, full-page support, and custom dimensions
</details>
<details>
<summary><strong>🌐 Network Monitoring (4 tools)</strong></summary>
- `chrome_network_capture_start/stop` - webRequest API network capture
- `chrome_network_debugger_start/stop` - Debugger API with response bodies
- `chrome_network_request` - Send custom HTTP requests
</details>
<details>
<summary><strong>🔍 Content Analysis (4 tools)</strong></summary>
- `search_tabs_content` - AI-powered semantic search across browser tabs
- `chrome_get_web_content` - Extract HTML/text content from pages
- `chrome_get_interactive_elements` - Find clickable elements
- `chrome_console` - Capture and retrieve console output from browser tabs
</details>
<details>
<summary><strong>🎯 Interaction (3 tools)</strong></summary>
- `chrome_click_element` - Click elements using CSS selectors
- `chrome_fill_or_select` - Fill forms and select options
- `chrome_keyboard` - Simulate keyboard input and shortcuts
</details>
<details>
<summary><strong>📚 Data Management (5 tools)</strong></summary>
- `chrome_history` - Search browser history with time filters
- `chrome_bookmark_search` - Find bookmarks by keywords
- `chrome_bookmark_add` - Add new bookmarks with folder support
- `chrome_bookmark_delete` - Delete bookmarks
</details>
## 🧪 Usage Examples
### AI helps you summarize webpage content and automatically control Excalidraw for drawing
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)
Instruction: Help me summarize the current page content, then draw a diagram to aid my understanding.
https://www.youtube.com/watch?v=3fBPdUBWVz0
https://github.com/user-attachments/assets/fd17209b-303d-48db-9e5e-3717141df183
### After analyzing the content of the image, the LLM automatically controls Excalidraw to replicate the image
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)|[content-analize](prompt/content-analize.md)
Instruction: First, analyze the content of the image, and then replicate the image by combining the analysis with the content of the image.
https://www.youtube.com/watch?v=tEPdHZBzbZk
https://github.com/user-attachments/assets/60d12b1a-9b74-40f4-994c-95e8fa1fc8d3
### AI automatically injects scripts and modifies webpage styles
prompt: [modify-web-prompt](prompt/modify-web.md)
Instruction: Help me modify the current page's style and remove advertisements.
https://youtu.be/twI6apRKHsk
https://github.com/user-attachments/assets/69cb561c-2e1e-4665-9411-4a3185f9643e
### AI automatically captures network requests for you
query: I want to know what the search API for Xiaohongshu is and what the response structure looks like
https://youtu.be/1hHKr7XKqnQ
https://github.com/user-attachments/assets/dc7e5cab-b9af-4b9a-97ce-18e4837318d9
### AI helps analyze your browsing history
query: Analyze my browsing history from the past month
https://youtu.be/jf2UZfrR2Vk
https://github.com/user-attachments/assets/31b2e064-88c6-4adb-96d7-50748b826eae
### Web page conversation
query: Translate and summarize the current web page
https://youtu.be/FlJKS9UQyC8
https://github.com/user-attachments/assets/aa8ef2a1-2310-47e6-897a-769d85489396
### AI automatically takes screenshots for you (web page screenshots)
query: Take a screenshot of Hugging Face's homepage
https://youtu.be/7ycK6iksWi4
https://github.com/user-attachments/assets/65c6eee2-6366-493d-a3bd-2b27529ff5b3
### AI automatically takes screenshots for you (element screenshots)
query: Capture the icon from Hugging Face's homepage
https://youtu.be/ev8VivANIrk
https://github.com/user-attachments/assets/d0cf9785-c2fe-4729-a3c5-7f2b8b96fe0c
### AI helps manage bookmarks
query: Add the current page to bookmarks and put it in an appropriate folder
https://youtu.be/R_83arKmFTo
https://github.com/user-attachments/assets/15a7d04c-0196-4b40-84c2-bafb5c26dfe0
### Automatically close web pages
query: Close all shadcn-related web pages
https://youtu.be/2wzUT6eNVg4
https://github.com/user-attachments/assets/83de4008-bb7e-494d-9b0f-98325cfea592
## 🤝 Contributing
We welcome contributions! Please see [CONTRIBUTING.md](docs/CONTRIBUTING.md) for detailed guidelines.
## 🚧 Future Roadmap
We have exciting plans for the future development of Chrome MCP Server:
- [ ] Authentication
- [ ] Recording and Playback
- [ ] Workflow Automation
- [ ] Enhanced Browser Support (Firefox Extension)
---
**Want to contribute to any of these features?** Check out our [Contributing Guide](docs/CONTRIBUTING.md) and join our development community!
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 📚 More Documentation
- [Architecture Design](docs/ARCHITECTURE.md) - Detailed technical architecture documentation
- [TOOLS API](docs/TOOLS.md) - Complete tool API documentation
- [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issue solutions
================================================
FILE: README_zh.md
================================================
# Chrome MCP Server 🚀
[](https://opensource.org/licenses/MIT)
[](https://www.typescriptlang.org/)
[](https://developer.chrome.com/docs/extensions/)
> 🌟 **让chrome浏览器变成你的智能助手** - 让AI接管你的浏览器,将您的浏览器转变为强大的 AI 控制自动化工具。
**📖 文档**: [English](README.md) | [中文](README_zh.md)
> 项目仍处于早期阶段,正在紧锣密鼓开发中,后续将有更多新功能,以及稳定性等的提升,如遇bug,请轻喷
---
## 🎯 什么是 Chrome MCP Server?
Chrome MCP Server 是一个基于chrome插件的 **模型上下文协议 (MCP) 服务器**,它将您的 Chrome 浏览器功能暴露给 Claude 等 AI 助手,实现复杂的浏览器自动化、内容分析和语义搜索等。与传统的浏览器自动化工具(如playwright)不同,**Chrome MCP server**直接使用您日常使用的chrome浏览器,基于现有的用户习惯和配置、登录态,让各种大模型或者各种chatbot都可以接管你的浏览器,真正成为你的日常助手
## ✨ 船新的功能(2025/12/30)
- **让Claude Code/Codex也能使用的可视化编辑器**, 更多详情请看: [VisualEditor](docs/VisualEditor_zh.md)
## ✨ 核心特性
- 😁 **chatbot/模型无关**:让任意你喜欢的llm或chatbot客户端或agent来自动化操作你的浏览器
- ⭐️ **使用你原本的浏览器**:无缝集成用户本身的浏览器环境(你的配置、登录态等)
- 💻 **完全本地运行**:纯本地运行的mcp server,保证用户隐私
- 🚄 **Streamable http**:Streamable http的连接方式
- 🏎 **跨标签页** 跨标签页的上下文
- 🧠 **语义搜索**:内置向量数据库和本地小模型,智能发现浏览器标签页内容
- 🔍 **智能内容分析**:AI 驱动的文本提取和相似度匹配
- 🌐 **20+ 工具**:支持截图、网络监控、交互操作、书签管理、浏览历史等20多种工具
- 🚀 **SIMD 加速 AI**:自定义 WebAssembly SIMD 优化,向量运算速度提升 4-8 倍
## 🆚 与同类项目对比
| 对比维度 | 基于Playwright的MCP Server | 基于Chrome插件的MCP Server |
| ------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------- |
| **资源占用** | ❌ 需启动独立浏览器进程,需要安装Playwright依赖,下载浏览器二进制等 | ✅ 无需启动独立的浏览器进程,直接利用用户已打开的Chrome浏览器 |
| **用户会话复用** | ❌ 需重新登录 | ✅ 自动使用已登录状态 |
| **浏览器环境保持** | ❌ 干净环境缺少用户设置 | ✅ 完整保留用户环境 |
| **API访问权限** | ⚠️ 受限于Playwright API | ✅ Chrome原生API全访问 |
| **启动速度** | ❌ 需启动浏览器进程 | ✅ 只需激活插件 |
| **响应速度** | 50-200ms进程间通信 | ✅ 更快 |
## 🚀 快速开始
### 环境要求
- Node.js >= 20.0.0 和 (npm 或 pnpm)
- Chrome/Chromium 浏览器
### 安装步骤
1. **从github上下载最新的chrome扩展**
下载地址:https://github.com/hangwin/mcp-chrome/releases
2. **全局安装mcp-chrome-bridge**
npm
```bash
npm install -g mcp-chrome-bridge
```
pnpm
```bash
# 方法1:全局启用脚本(推荐)
pnpm config set enable-pre-post-scripts true
pnpm install -g mcp-chrome-bridge
# 方法2:如果 postinstall 没有运行,手动注册
pnpm install -g mcp-chrome-bridge
mcp-chrome-bridge register
```
> 注意:pnpm v7+ 默认禁用 postinstall 脚本以提高安全性。`enable-pre-post-scripts` 设置控制是否运行 pre/post 安装脚本。如果自动注册失败,请使用上述手动注册命令。
3. **加载 Chrome 扩展**
- 打开 Chrome 并访问 `chrome://extensions/`
- 启用"开发者模式"
- 点击"加载已解压的扩展程序",选择 `your/dowloaded/extension/folder`
- 点击插件图标打开插件,点击连接即可看到mcp的配置
<img width="475" alt="截屏2025-06-09 15 52 06" src="https://github.com/user-attachments/assets/241e57b8-c55f-41a4-9188-0367293dc5bc" />
### 在支持MCP协议的客户端中使用
#### 使用streamable http的方式连接(👍🏻推荐)
将以下配置添加到客户端的 MCP 配置中以cherryStudio为例:
> 推荐用streamable http的连接方式
```json
{
"mcpServers": {
"chrome-mcp-server": {
"type": "streamableHttp",
"url": "http://127.0.0.1:12306/mcp"
}
}
}
```
#### 使用stdio的方式连接(备选)
假设你的客户端仅支持stdio的连接方式,那么请使用下面的方法:
1. 先查看你刚刚安装的npm包的安装位置
```sh
# npm 查看方式
npm list -g mcp-chrome-bridge
# pnpm 查看方式
pnpm list -g mcp-chrome-bridge
```
假设上面的命令输出的路径是:/Users/xxx/Library/pnpm/global/5
那么你的最终路径就是:/Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js
2. 把下面的配置替换成你刚刚得到的最终路径
```json
{
"mcpServers": {
"chrome-mcp-stdio": {
"command": "npx",
"args": [
"node",
"/Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js"
]
}
}
}
```
比如:在augment中的配置如下:
<img width="494" alt="截屏2025-06-22 22 11 25" src="https://github.com/user-attachments/assets/07c0b090-622b-433d-be70-44e8cb8980a5" />
## 🛠️ 可用工具
完整工具列表:[完整工具列表](docs/TOOLS_zh.md)
<details>
<summary><strong>📊 浏览器管理 (6个工具)</strong></summary>
- `get_windows_and_tabs` - 列出所有浏览器窗口和标签页
- `chrome_navigate` - 导航到 URL 并控制视口
- `chrome_switch_tab` - 切换当前显示的标签页
- `chrome_close_tabs` - 关闭特定标签页或窗口
- `chrome_go_back_or_forward` - 浏览器导航控制
- `chrome_inject_script` - 向网页注入内容脚本
- `chrome_send_command_to_inject_script` - 向已注入的内容脚本发送指令
</details>
<details>
<summary><strong>📸 截图和视觉 (1个工具)</strong></summary>
- `chrome_screenshot` - 高级截图捕获,支持元素定位、全页面和自定义尺寸
</details>
<details>
<summary><strong>🌐 网络监控 (4个工具)</strong></summary>
- `chrome_network_capture_start/stop` - webRequest API 网络捕获
- `chrome_network_debugger_start/stop` - Debugger API 包含响应体
- `chrome_network_request` - 发送自定义 HTTP 请求
</details>
<details>
<summary><strong>🔍 内容分析 (4个工具)</strong></summary>
- `search_tabs_content` - AI 驱动的浏览器标签页语义搜索
- `chrome_get_web_content` - 从页面提取 HTML/文本内容
- `chrome_get_interactive_elements` - 查找可点击元素
- `chrome_console` - 捕获和获取浏览器标签页的控制台输出
</details>
<details>
<summary><strong>🎯 交互操作 (3个工具)</strong></summary>
- `chrome_click_element` - 使用 CSS 选择器点击元素
- `chrome_fill_or_select` - 填充表单和选择选项
- `chrome_keyboard` - 模拟键盘输入和快捷键
</details>
<details>
<summary><strong>📚 数据管理 (5个工具)</strong></summary>
- `chrome_history` - 搜索浏览器历史记录,支持时间过滤
- `chrome_bookmark_search` - 按关键词查找书签
- `chrome_bookmark_add` - 添加新书签,支持文件夹
- `chrome_bookmark_delete` - 删除书签
</details>
## 🧪 使用示例
### ai帮你总结网页内容然后自动控制excalidraw画图
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)
指令:帮我总结当前页面内容,然后画个图帮我理解
https://www.youtube.com/watch?v=3fBPdUBWVz0
https://github.com/user-attachments/assets/f14f79a6-9390-4821-8296-06d020bcfc07
### ai先分析图片的内容元素,然后再自动控制excalidraw把图片模仿出来
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)|[content-analize](prompt/content-analize.md)
指令:先看下图片是否能用excalidraw画出来,如果则列出所需的步骤和元素,然后画出来
https://www.youtube.com/watch?v=tEPdHZBzbZk
https://github.com/user-attachments/assets/4f0600c1-bb1e-4b57-85ab-36c8bdf71c68
### ai自动帮你注入脚本并修改网页的样式
prompt: [modify-web-prompt](prompt/modify-web.md)
指令:帮我修改当前页面的样式,去掉广告
https://youtu.be/twI6apRKHsk
https://github.com/user-attachments/assets/aedbe98d-e90c-4a58-a4a5-d888f7293d8e
### ai自动帮你捕获网络请求
指令:我想知道小红书的搜索接口是哪个,响应体结构是什么样的
https://youtu.be/1hHKr7XKqnQ
https://github.com/user-attachments/assets/dc7e5cab-b9af-4b9a-97ce-18e4837318d9
### ai帮你分析你的浏览记录
指令:分析一下我近一个月的浏览记录
https://youtu.be/jf2UZfrR2Vk
https://github.com/user-attachments/assets/31b2e064-88c6-4adb-96d7-50748b826eae
### 网页对话
指令:翻译并总结当前网页
https://youtu.be/FlJKS9UQyC8
https://github.com/user-attachments/assets/aa8ef2a1-2310-47e6-897a-769d85489396
### ai帮你自动截图(网页截图)
指令:把huggingface的首页截个图
https://youtu.be/7ycK6iksWi4
https://github.com/user-attachments/assets/65c6eee2-6366-493d-a3bd-2b27529ff5b3
### ai帮你自动截图(元素截图)
指令:把huggingface首页的图标截取下来
https://youtu.be/ev8VivANIrk
https://github.com/user-attachments/assets/d0cf9785-c2fe-4729-a3c5-7f2b8b96fe0c
### ai帮你管理书签
指令:将当前页面添加到书签中,放到合适的文件夹
https://youtu.be/R_83arKmFTo
https://github.com/user-attachments/assets/15a7d04c-0196-4b40-84c2-bafb5c26dfe0
### 自动关闭网页
指令:关闭所有shadcn相关的网页
https://youtu.be/2wzUT6eNVg4
https://github.com/user-attachments/assets/83de4008-bb7e-494d-9b0f-98325cfea592
## 🤝 贡献指南
我们欢迎贡献!请查看 [CONTRIBUTING_zh.md](docs/CONTRIBUTING_zh.md) 了解详细指南。
## 🚧 未来发展路线图
我们对 Chrome MCP Server 的未来发展有着激动人心的计划:
- [ ] 身份认证
- [ ] 录制与回放
- [ ] 工作流自动化
- [ ] 增强浏览器支持(Firefox 扩展)
---
**想要为这些功能中的任何一个做贡献?** 查看我们的[贡献指南](docs/CONTRIBUTING_zh.md)并加入我们的开发社区!
## 📄 许可证
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
## 📚 更多文档
- [架构设计](docs/ARCHITECTURE_zh.md) - 详细的技术架构说明
- [工具列表](docs/TOOLS_zh.md) - 完整的工具 API 文档
- [故障排除](docs/TROUBLESHOOTING_zh.md) - 常见问题解决方案
## 微信交流群
拉群的目的是让踩过坑的大佬们互相帮忙解答问题,因本人平时要忙着搬砖,不一定能及时解答

================================================
FILE: app/chrome-extension/LICENSE
================================================
MIT License
Copyright (c) 2025 hangwin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: app/chrome-extension/README.md
================================================
# WXT + Vue 3
This template should help get you started developing with Vue 3 in WXT.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
================================================
FILE: app/chrome-extension/_locales/de/messages.json
================================================
{
"extensionName": {
"message": "chrome-mcp-server",
"description": "Erweiterungsname"
},
"extensionDescription": {
"message": "Stellt Browser-Funktionen mit Ihrem eigenen Chrome zur Verfügung",
"description": "Erweiterungsbeschreibung"
},
"nativeServerConfigLabel": {
"message": "Native Server-Konfiguration",
"description": "Hauptabschnittstitel für Native Server-Einstellungen"
},
"semanticEngineLabel": {
"message": "Semantische Engine",
"description": "Hauptabschnittstitel für semantische Engine"
},
"embeddingModelLabel": {
"message": "Embedding-Modell",
"description": "Hauptabschnittstitel für Modellauswahl"
},
"indexDataManagementLabel": {
"message": "Index-Datenverwaltung",
"description": "Hauptabschnittstitel für Datenverwaltung"
},
"modelCacheManagementLabel": {
"message": "Modell-Cache-Verwaltung",
"description": "Hauptabschnittstitel für Cache-Verwaltung"
},
"statusLabel": {
"message": "Status",
"description": "Allgemeines Statuslabel"
},
"runningStatusLabel": {
"message": "Betriebsstatus",
"description": "Server-Betriebsstatuslabel"
},
"connectionStatusLabel": {
"message": "Verbindungsstatus",
"description": "Verbindungsstatuslabel"
},
"lastUpdatedLabel": {
"message": "Zuletzt aktualisiert:",
"description": "Zeitstempel der letzten Aktualisierung"
},
"connectButton": {
"message": "Verbinden",
"description": "Verbinden-Schaltflächentext"
},
"disconnectButton": {
"message": "Trennen",
"description": "Trennen-Schaltflächentext"
},
"connectingStatus": {
"message": "Verbindung wird hergestellt...",
"description": "Verbindungsstatusmeldung"
},
"connectedStatus": {
"message": "Verbunden",
"description": "Verbunden-Statusmeldung"
},
"disconnectedStatus": {
"message": "Getrennt",
"description": "Getrennt-Statusmeldung"
},
"detectingStatus": {
"message": "Erkennung läuft...",
"description": "Erkennungsstatusmeldung"
},
"serviceRunningStatus": {
"message": "Service läuft (Port: $PORT$)",
"description": "Service läuft mit Portnummer",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "Service nicht verbunden",
"description": "Service nicht verbunden Status"
},
"connectedServiceNotStartedStatus": {
"message": "Verbunden, Service nicht gestartet",
"description": "Verbunden aber Service nicht gestartet Status"
},
"mcpServerConfigLabel": {
"message": "MCP Server-Konfiguration",
"description": "MCP Server-Konfigurationsabschnittslabel"
},
"connectionPortLabel": {
"message": "Verbindungsport",
"description": "Verbindungsport-Eingabelabel"
},
"refreshStatusButton": {
"message": "Status aktualisieren",
"description": "Status aktualisieren Schaltflächen-Tooltip"
},
"copyConfigButton": {
"message": "Konfiguration kopieren",
"description": "Konfiguration kopieren Schaltflächentext"
},
"retryButton": {
"message": "Wiederholen",
"description": "Wiederholen-Schaltflächentext"
},
"cancelButton": {
"message": "Abbrechen",
"description": "Abbrechen-Schaltflächentext"
},
"confirmButton": {
"message": "Bestätigen",
"description": "Bestätigen-Schaltflächentext"
},
"saveButton": {
"message": "Speichern",
"description": "Speichern-Schaltflächentext"
},
"closeButton": {
"message": "Schließen",
"description": "Schließen-Schaltflächentext"
},
"resetButton": {
"message": "Zurücksetzen",
"description": "Zurücksetzen-Schaltflächentext"
},
"initializingStatus": {
"message": "Initialisierung...",
"description": "Initialisierung-Fortschrittsmeldung"
},
"processingStatus": {
"message": "Verarbeitung...",
"description": "Verarbeitung-Fortschrittsmeldung"
},
"loadingStatus": {
"message": "Wird geladen...",
"description": "Ladefortschrittsmeldung"
},
"clearingStatus": {
"message": "Wird geleert...",
"description": "Leerungsfortschrittsmeldung"
},
"cleaningStatus": {
"message": "Wird bereinigt...",
"description": "Bereinigungsfortschrittsmeldung"
},
"downloadingStatus": {
"message": "Wird heruntergeladen...",
"description": "Download-Fortschrittsmeldung"
},
"semanticEngineReadyStatus": {
"message": "Semantische Engine bereit",
"description": "Semantische Engine bereit Status"
},
"semanticEngineInitializingStatus": {
"message": "Semantische Engine wird initialisiert...",
"description": "Semantische Engine Initialisierungsstatus"
},
"semanticEngineInitFailedStatus": {
"message": "Initialisierung der semantischen Engine fehlgeschlagen",
"description": "Semantische Engine Initialisierung fehlgeschlagen Status"
},
"semanticEngineNotInitStatus": {
"message": "Semantische Engine nicht initialisiert",
"description": "Semantische Engine nicht initialisiert Status"
},
"initSemanticEngineButton": {
"message": "Semantische Engine initialisieren",
"description": "Semantische Engine initialisieren Schaltflächentext"
},
"reinitializeButton": {
"message": "Neu initialisieren",
"description": "Neu initialisieren Schaltflächentext"
},
"downloadingModelStatus": {
"message": "Modell wird heruntergeladen... $PROGRESS$%",
"description": "Modell-Download-Fortschritt mit Prozentsatz",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "Modell wird gewechselt...",
"description": "Modellwechsel-Fortschrittsmeldung"
},
"modelLoadedStatus": {
"message": "Modell geladen",
"description": "Modell erfolgreich geladen Status"
},
"modelFailedStatus": {
"message": "Modell konnte nicht geladen werden",
"description": "Modell-Ladefehler Status"
},
"lightweightModelDescription": {
"message": "Leichtgewichtiges mehrsprachiges Modell",
"description": "Beschreibung für leichtgewichtige Modelloption"
},
"betterThanSmallDescription": {
"message": "Etwas größer als e5-small, aber bessere Leistung",
"description": "Beschreibung für mittlere Modelloption"
},
"multilingualModelDescription": {
"message": "Mehrsprachiges semantisches Modell",
"description": "Beschreibung für mehrsprachige Modelloption"
},
"fastPerformance": {
"message": "Schnell",
"description": "Schnelle Leistungsanzeige"
},
"balancedPerformance": {
"message": "Ausgewogen",
"description": "Ausgewogene Leistungsanzeige"
},
"accuratePerformance": {
"message": "Genau",
"description": "Genaue Leistungsanzeige"
},
"networkErrorMessage": {
"message": "Netzwerkverbindungsfehler, bitte Netzwerk prüfen und erneut versuchen",
"description": "Netzwerkverbindungsfehlermeldung"
},
"modelCorruptedErrorMessage": {
"message": "Modelldatei beschädigt oder unvollständig, bitte Download wiederholen",
"description": "Modell-Beschädigungsfehlermeldung"
},
"unknownErrorMessage": {
"message": "Unbekannter Fehler, bitte prüfen Sie, ob Ihr Netzwerk auf HuggingFace zugreifen kann",
"description": "Unbekannte Fehler-Rückfallmeldung"
},
"permissionDeniedErrorMessage": {
"message": "Zugriff verweigert",
"description": "Zugriff verweigert Fehlermeldung"
},
"timeoutErrorMessage": {
"message": "Zeitüberschreitung",
"description": "Zeitüberschreitungsfehlermeldung"
},
"indexedPagesLabel": {
"message": "Indizierte Seiten",
"description": "Anzahl indizierter Seiten Label"
},
"indexSizeLabel": {
"message": "Indexgröße",
"description": "Indexgröße Label"
},
"activeTabsLabel": {
"message": "Aktive Tabs",
"description": "Anzahl aktiver Tabs Label"
},
"vectorDocumentsLabel": {
"message": "Vektordokumente",
"description": "Anzahl Vektordokumente Label"
},
"cacheSizeLabel": {
"message": "Cache-Größe",
"description": "Cache-Größe Label"
},
"cacheEntriesLabel": {
"message": "Cache-Einträge",
"description": "Anzahl Cache-Einträge Label"
},
"clearAllDataButton": {
"message": "Alle Daten löschen",
"description": "Alle Daten löschen Schaltflächentext"
},
"clearAllCacheButton": {
"message": "Gesamten Cache löschen",
"description": "Gesamten Cache löschen Schaltflächentext"
},
"cleanExpiredCacheButton": {
"message": "Abgelaufenen Cache bereinigen",
"description": "Abgelaufenen Cache bereinigen Schaltflächentext"
},
"exportDataButton": {
"message": "Daten exportieren",
"description": "Daten exportieren Schaltflächentext"
},
"importDataButton": {
"message": "Daten importieren",
"description": "Daten importieren Schaltflächentext"
},
"confirmClearDataTitle": {
"message": "Datenlöschung bestätigen",
"description": "Datenlöschung bestätigen Dialogtitel"
},
"settingsTitle": {
"message": "Einstellungen",
"description": "Einstellungen Dialogtitel"
},
"aboutTitle": {
"message": "Über",
"description": "Über Dialogtitel"
},
"helpTitle": {
"message": "Hilfe",
"description": "Hilfe Dialogtitel"
},
"clearDataWarningMessage": {
"message": "Diese Aktion löscht alle indizierten Webseiteninhalte und Vektordaten, einschließlich:",
"description": "Datenlöschung Warnmeldung"
},
"clearDataList1": {
"message": "Alle Webseitentextinhaltsindizes",
"description": "Erster Punkt in Datenlöschungsliste"
},
"clearDataList2": {
"message": "Vektor-Embedding-Daten",
"description": "Zweiter Punkt in Datenlöschungsliste"
},
"clearDataList3": {
"message": "Suchverlauf und Cache",
"description": "Dritter Punkt in Datenlöschungsliste"
},
"clearDataIrreversibleWarning": {
"message": "Diese Aktion ist unwiderruflich! Nach dem Löschen müssen Sie Webseiten erneut durchsuchen, um den Index neu aufzubauen.",
"description": "Unwiderrufliche Aktion Warnung"
},
"confirmClearButton": {
"message": "Löschung bestätigen",
"description": "Löschung bestätigen Aktionsschaltfläche"
},
"cacheDetailsLabel": {
"message": "Cache-Details",
"description": "Cache-Details Abschnittslabel"
},
"noCacheDataMessage": {
"message": "Keine Cache-Daten vorhanden",
"description": "Keine Cache-Daten verfügbar Meldung"
},
"loadingCacheInfoStatus": {
"message": "Cache-Informationen werden geladen...",
"description": "Cache-Informationen laden Status"
},
"processingCacheStatus": {
"message": "Cache wird verarbeitet...",
"description": "Cache verarbeiten Status"
},
"expiredLabel": {
"message": "Abgelaufen",
"description": "Abgelaufenes Element Label"
},
"bookmarksBarLabel": {
"message": "Lesezeichenleiste",
"description": "Lesezeichenleiste Ordnername"
},
"newTabLabel": {
"message": "Neuer Tab",
"description": "Neuer Tab Label"
},
"currentPageLabel": {
"message": "Aktuelle Seite",
"description": "Aktuelle Seite Label"
},
"menuLabel": {
"message": "Menü",
"description": "Menü Barrierefreiheitslabel"
},
"navigationLabel": {
"message": "Navigation",
"description": "Navigation Barrierefreiheitslabel"
},
"mainContentLabel": {
"message": "Hauptinhalt",
"description": "Hauptinhalt Barrierefreiheitslabel"
},
"languageSelectorLabel": {
"message": "Sprache",
"description": "Sprachauswahl Label"
},
"themeLabel": {
"message": "Design",
"description": "Design-Auswahl Label"
},
"lightTheme": {
"message": "Hell",
"description": "Helles Design Option"
},
"darkTheme": {
"message": "Dunkel",
"description": "Dunkles Design Option"
},
"autoTheme": {
"message": "Automatisch",
"description": "Automatisches Design Option"
},
"advancedSettingsLabel": {
"message": "Erweiterte Einstellungen",
"description": "Erweiterte Einstellungen Abschnittslabel"
},
"debugModeLabel": {
"message": "Debug-Modus",
"description": "Debug-Modus Umschalter Label"
},
"verboseLoggingLabel": {
"message": "Ausführliche Protokollierung",
"description": "Ausführliche Protokollierung Umschalter Label"
},
"successNotification": {
"message": "Vorgang erfolgreich abgeschlossen",
"description": "Allgemeine Erfolgsmeldung"
},
"warningNotification": {
"message": "Warnung: Bitte prüfen Sie vor dem Fortfahren",
"description": "Allgemeine Warnmeldung"
},
"infoNotification": {
"message": "Information",
"description": "Allgemeine Informationsmeldung"
},
"configCopiedNotification": {
"message": "Konfiguration in Zwischenablage kopiert",
"description": "Konfiguration kopiert Erfolgsmeldung"
},
"dataClearedNotification": {
"message": "Daten erfolgreich gelöscht",
"description": "Daten gelöscht Erfolgsmeldung"
},
"bytesUnit": {
"message": "Bytes",
"description": "Bytes Einheit"
},
"kilobytesUnit": {
"message": "KB",
"description": "Kilobytes Einheit"
},
"megabytesUnit": {
"message": "MB",
"description": "Megabytes Einheit"
},
"gigabytesUnit": {
"message": "GB",
"description": "Gigabytes Einheit"
},
"itemsUnit": {
"message": "Elemente",
"description": "Elemente Zähleinheit"
},
"pagesUnit": {
"message": "Seiten",
"description": "Seiten Zähleinheit"
}
}
================================================
FILE: app/chrome-extension/_locales/en/messages.json
================================================
{
"extensionName": {
"message": "chrome-mcp-server",
"description": "Extension name"
},
"extensionDescription": {
"message": "Exposes browser capabilities with your own chrome",
"description": "Extension description"
},
"nativeServerConfigLabel": {
"message": "Native Server Configuration",
"description": "Main section header for native server settings"
},
"semanticEngineLabel": {
"message": "Semantic Engine",
"description": "Main section header for semantic engine"
},
"embeddingModelLabel": {
"message": "Embedding Model",
"description": "Main section header for model selection"
},
"indexDataManagementLabel": {
"message": "Index Data Management",
"description": "Main section header for data management"
},
"modelCacheManagementLabel": {
"message": "Model Cache Management",
"description": "Main section header for cache management"
},
"statusLabel": {
"message": "Status",
"description": "Generic status label"
},
"runningStatusLabel": {
"message": "Running Status",
"description": "Server running status label"
},
"connectionStatusLabel": {
"message": "Connection Status",
"description": "Connection status label"
},
"lastUpdatedLabel": {
"message": "Last Updated:",
"description": "Last updated timestamp label"
},
"connectButton": {
"message": "Connect",
"description": "Connect button text"
},
"disconnectButton": {
"message": "Disconnect",
"description": "Disconnect button text"
},
"connectingStatus": {
"message": "Connecting...",
"description": "Connecting status message"
},
"connectedStatus": {
"message": "Connected",
"description": "Connected status message"
},
"disconnectedStatus": {
"message": "Disconnected",
"description": "Disconnected status message"
},
"detectingStatus": {
"message": "Detecting...",
"description": "Detecting status message"
},
"serviceRunningStatus": {
"message": "Service Running (Port: $PORT$)",
"description": "Service running with port number",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "Service Not Connected",
"description": "Service not connected status"
},
"connectedServiceNotStartedStatus": {
"message": "Connected, Service Not Started",
"description": "Connected but service not started status"
},
"mcpServerConfigLabel": {
"message": "MCP Server Configuration",
"description": "MCP server configuration section label"
},
"connectionPortLabel": {
"message": "Connection Port",
"description": "Connection port input label"
},
"refreshStatusButton": {
"message": "Refresh Status",
"description": "Refresh status button tooltip"
},
"copyConfigButton": {
"message": "Copy Configuration",
"description": "Copy configuration button text"
},
"retryButton": {
"message": "Retry",
"description": "Retry button text"
},
"cancelButton": {
"message": "Cancel",
"description": "Cancel button text"
},
"confirmButton": {
"message": "Confirm",
"description": "Confirm button text"
},
"saveButton": {
"message": "Save",
"description": "Save button text"
},
"closeButton": {
"message": "Close",
"description": "Close button text"
},
"resetButton": {
"message": "Reset",
"description": "Reset button text"
},
"initializingStatus": {
"message": "Initializing...",
"description": "Initializing progress message"
},
"processingStatus": {
"message": "Processing...",
"description": "Processing progress message"
},
"loadingStatus": {
"message": "Loading...",
"description": "Loading progress message"
},
"clearingStatus": {
"message": "Clearing...",
"description": "Clearing progress message"
},
"cleaningStatus": {
"message": "Cleaning...",
"description": "Cleaning progress message"
},
"downloadingStatus": {
"message": "Downloading...",
"description": "Downloading progress message"
},
"semanticEngineReadyStatus": {
"message": "Semantic Engine Ready",
"description": "Semantic engine ready status"
},
"semanticEngineInitializingStatus": {
"message": "Semantic Engine Initializing...",
"description": "Semantic engine initializing status"
},
"semanticEngineInitFailedStatus": {
"message": "Semantic Engine Initialization Failed",
"description": "Semantic engine initialization failed status"
},
"semanticEngineNotInitStatus": {
"message": "Semantic Engine Not Initialized",
"description": "Semantic engine not initialized status"
},
"initSemanticEngineButton": {
"message": "Initialize Semantic Engine",
"description": "Initialize semantic engine button text"
},
"reinitializeButton": {
"message": "Reinitialize",
"description": "Reinitialize button text"
},
"downloadingModelStatus": {
"message": "Downloading Model... $PROGRESS$%",
"description": "Model download progress with percentage",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "Switching Model...",
"description": "Model switching progress message"
},
"modelLoadedStatus": {
"message": "Model Loaded",
"description": "Model successfully loaded status"
},
"modelFailedStatus": {
"message": "Model Failed to Load",
"description": "Model failed to load status"
},
"lightweightModelDescription": {
"message": "Lightweight Multilingual Model",
"description": "Description for lightweight model option"
},
"betterThanSmallDescription": {
"message": "Slightly larger than e5-small, but better performance",
"description": "Description for medium model option"
},
"multilingualModelDescription": {
"message": "Multilingual Semantic Model",
"description": "Description for multilingual model option"
},
"fastPerformance": {
"message": "Fast",
"description": "Fast performance indicator"
},
"balancedPerformance": {
"message": "Balanced",
"description": "Balanced performance indicator"
},
"accuratePerformance": {
"message": "Accurate",
"description": "Accurate performance indicator"
},
"networkErrorMessage": {
"message": "Network connection error, please check network and retry",
"description": "Network connection error message"
},
"modelCorruptedErrorMessage": {
"message": "Model file corrupted or incomplete, please retry download",
"description": "Model corruption error message"
},
"unknownErrorMessage": {
"message": "Unknown error, please check if your network can access HuggingFace",
"description": "Unknown error fallback message"
},
"permissionDeniedErrorMessage": {
"message": "Permission denied",
"description": "Permission denied error message"
},
"timeoutErrorMessage": {
"message": "Operation timed out",
"description": "Timeout error message"
},
"indexedPagesLabel": {
"message": "Indexed Pages",
"description": "Number of indexed pages label"
},
"indexSizeLabel": {
"message": "Index Size",
"description": "Index size label"
},
"activeTabsLabel": {
"message": "Active Tabs",
"description": "Number of active tabs label"
},
"vectorDocumentsLabel": {
"message": "Vector Documents",
"description": "Number of vector documents label"
},
"cacheSizeLabel": {
"message": "Cache Size",
"description": "Cache size label"
},
"cacheEntriesLabel": {
"message": "Cache Entries",
"description": "Number of cache entries label"
},
"clearAllDataButton": {
"message": "Clear All Data",
"description": "Clear all data button text"
},
"clearAllCacheButton": {
"message": "Clear All Cache",
"description": "Clear all cache button text"
},
"cleanExpiredCacheButton": {
"message": "Clean Expired Cache",
"description": "Clean expired cache button text"
},
"exportDataButton": {
"message": "Export Data",
"description": "Export data button text"
},
"importDataButton": {
"message": "Import Data",
"description": "Import data button text"
},
"confirmClearDataTitle": {
"message": "Confirm Clear Data",
"description": "Clear data confirmation dialog title"
},
"settingsTitle": {
"message": "Settings",
"description": "Settings dialog title"
},
"aboutTitle": {
"message": "About",
"description": "About dialog title"
},
"helpTitle": {
"message": "Help",
"description": "Help dialog title"
},
"clearDataWarningMessage": {
"message": "This operation will clear all indexed webpage content and vector data, including:",
"description": "Clear data warning message"
},
"clearDataList1": {
"message": "All webpage text content index",
"description": "First item in clear data list"
},
"clearDataList2": {
"message": "Vector embedding data",
"description": "Second item in clear data list"
},
"clearDataList3": {
"message": "Search history and cache",
"description": "Third item in clear data list"
},
"clearDataIrreversibleWarning": {
"message": "This operation is irreversible! After clearing, you need to browse webpages again to rebuild the index.",
"description": "Irreversible operation warning"
},
"confirmClearButton": {
"message": "Confirm Clear",
"description": "Confirm clear action button"
},
"cacheDetailsLabel": {
"message": "Cache Details",
"description": "Cache details section label"
},
"noCacheDataMessage": {
"message": "No cache data",
"description": "No cache data available message"
},
"loadingCacheInfoStatus": {
"message": "Loading cache information...",
"description": "Loading cache information status"
},
"processingCacheStatus": {
"message": "Processing cache...",
"description": "Processing cache status"
},
"expiredLabel": {
"message": "Expired",
"description": "Expired item label"
},
"bookmarksBarLabel": {
"message": "Bookmarks Bar",
"description": "Bookmarks bar folder name"
},
"newTabLabel": {
"message": "New Tab",
"description": "New tab label"
},
"currentPageLabel": {
"message": "Current Page",
"description": "Current page label"
},
"menuLabel": {
"message": "Menu",
"description": "Menu accessibility label"
},
"navigationLabel": {
"message": "Navigation",
"description": "Navigation accessibility label"
},
"mainContentLabel": {
"message": "Main Content",
"description": "Main content accessibility label"
},
"languageSelectorLabel": {
"message": "Language",
"description": "Language selector label"
},
"themeLabel": {
"message": "Theme",
"description": "Theme selector label"
},
"lightTheme": {
"message": "Light",
"description": "Light theme option"
},
"darkTheme": {
"message": "Dark",
"description": "Dark theme option"
},
"autoTheme": {
"message": "Auto",
"description": "Auto theme option"
},
"advancedSettingsLabel": {
"message": "Advanced Settings",
"description": "Advanced settings section label"
},
"debugModeLabel": {
"message": "Debug Mode",
"description": "Debug mode toggle label"
},
"verboseLoggingLabel": {
"message": "Verbose Logging",
"description": "Verbose logging toggle label"
},
"successNotification": {
"message": "Operation completed successfully",
"description": "Generic success notification"
},
"warningNotification": {
"message": "Warning: Please review before proceeding",
"description": "Generic warning notification"
},
"infoNotification": {
"message": "Information",
"description": "Generic info notification"
},
"configCopiedNotification": {
"message": "Configuration copied to clipboard",
"description": "Configuration copied success message"
},
"dataClearedNotification": {
"message": "Data cleared successfully",
"description": "Data cleared success message"
},
"bytesUnit": {
"message": "bytes",
"description": "Bytes unit"
},
"kilobytesUnit": {
"message": "KB",
"description": "Kilobytes unit"
},
"megabytesUnit": {
"message": "MB",
"description": "Megabytes unit"
},
"gigabytesUnit": {
"message": "GB",
"description": "Gigabytes unit"
},
"itemsUnit": {
"message": "items",
"description": "Items count unit"
},
"pagesUnit": {
"message": "pages",
"description": "Pages count unit"
},
"userscriptsManagerTitle": {
"message": "Userscripts Manager",
"description": "Options page title"
},
"emergencySwitchLabel": { "message": "Emergency Switch", "description": "Global disable switch" },
"createRunSectionTitle": {
"message": "Create / Run",
"description": "Create & run section title"
},
"nameLabel": { "message": "Name", "description": "Name input label" },
"runAtLabel": { "message": "Run At", "description": "runAt select label" },
"runAtAuto": { "message": "auto", "description": "runAt auto" },
"runAtDocumentStart": { "message": "document_start", "description": "runAt document_start" },
"runAtDocumentEnd": { "message": "document_end", "description": "runAt document_end" },
"runAtDocumentIdle": { "message": "document_idle", "description": "runAt document_idle" },
"worldLabel": { "message": "World", "description": "world select label" },
"worldAuto": { "message": "auto", "description": "world auto" },
"worldIsolated": { "message": "ISOLATED", "description": "ISOLATED world" },
"worldMain": { "message": "MAIN", "description": "MAIN world" },
"modeLabel": { "message": "Mode", "description": "mode select label" },
"modeAuto": { "message": "auto", "description": "mode auto" },
"modePersistent": { "message": "persistent", "description": "mode persistent" },
"modeCss": { "message": "css", "description": "mode css" },
"modeOnce": { "message": "once", "description": "mode once" },
"allFramesLabel": { "message": "All Frames", "description": "allFrames checkbox" },
"persistLabel": { "message": "Persist", "description": "persist checkbox" },
"dnrFallbackLabel": { "message": "DNR Fallback", "description": "dnr fallback checkbox" },
"matchesInputLabel": { "message": "Matches (comma-separated)", "description": "matches input" },
"excludesInputLabel": {
"message": "Excludes (comma-separated)",
"description": "excludes input"
},
"tagsInputLabel": { "message": "Tags (comma-separated)", "description": "tags input" },
"scriptLabel": { "message": "Script", "description": "script textarea label" },
"applyButton": { "message": "Apply", "description": "apply button" },
"runOnceButton": { "message": "Run Once (CDP)", "description": "run once button" },
"listSectionTitle": { "message": "List", "description": "list section title" },
"queryLabel": { "message": "Query", "description": "query input label" },
"statusAll": { "message": "all", "description": "status all" },
"statusEnabled": { "message": "enabled", "description": "status enabled" },
"statusDisabled": { "message": "disabled", "description": "status disabled" },
"domainLabel": { "message": "Domain", "description": "domain filter label" },
"exportAllButton": { "message": "Export All", "description": "export button" },
"tableHeaderName": { "message": "Name", "description": "table header name" },
"tableHeaderWorld": { "message": "World", "description": "table header world" },
"tableHeaderRunAt": { "message": "Run At", "description": "table header runAt" },
"tableHeaderUpdated": { "message": "Updated", "description": "table header updated" },
"deleteButton": { "message": "Delete", "description": "delete button" },
"placeholderOptional": { "message": "optional", "description": "generic optional placeholder" },
"placeholderMatchesExample": {
"message": "e.g. https://*.example.com/*",
"description": "matches example placeholder"
},
"placeholderScriptHint": {
"message": "Paste JS/CSS/TM here",
"description": "script textarea placeholder"
},
"placeholderDomainHint": { "message": "example.com", "description": "domain filter placeholder" }
}
================================================
FILE: app/chrome-extension/_locales/ja/messages.json
================================================
{
"extensionName": {
"message": "Chrome MCPサーバー"
},
"extensionDescription": {
"message": "自身のChromeブラウザの機能を外部に公開します"
},
"nativeServerConfigLabel": {
"message": "ネイティブサーバー設定"
},
"semanticEngineLabel": {
"message": "セマンティックエンジン"
},
"embeddingModelLabel": {
"message": "埋め込みモデル"
},
"indexDataManagementLabel": {
"message": "インデックスデータ管理"
},
"modelCacheManagementLabel": {
"message": "モデルキャッシュ管理"
},
"statusLabel": {
"message": "ステータス"
},
"runningStatusLabel": {
"message": "実行ステータス"
},
"connectionStatusLabel": {
"message": "接続ステータス"
},
"lastUpdatedLabel": {
"message": "最終更新:"
},
"connectButton": {
"message": "接続"
},
"disconnectButton": {
"message": "切断"
},
"connectingStatus": {
"message": "接続中..."
},
"connectedStatus": {
"message": "接続済み"
},
"disconnectedStatus": {
"message": "未接続"
},
"detectingStatus": {
"message": "検出中..."
},
"serviceRunningStatus": {
"message": "サービス実行中 (ポート: $1)",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "サービス未接続"
},
"connectedServiceNotStartedStatus": {
"message": "接続済み、サービス未起動"
},
"mcpServerConfigLabel": {
"message": "MCPサーバー設定"
},
"connectionPortLabel": {
"message": "接続ポート"
},
"refreshStatusButton": {
"message": "ステータス更新"
},
"copyConfigButton": {
"message": "設定をコピー"
},
"retryButton": {
"message": "再試行"
},
"cancelButton": {
"message": "キャンセル"
},
"confirmButton": {
"message": "確認"
},
"saveButton": {
"message": "保存"
},
"closeButton": {
"message": "閉じる"
},
"resetButton": {
"message": "リセット"
},
"initializingStatus": {
"message": "初期化中..."
},
"processingStatus": {
"message": "処理中..."
},
"loadingStatus": {
"message": "読み込み中..."
},
"clearingStatus": {
"message": "クリア中..."
},
"cleaningStatus": {
"message": "クリーンアップ中..."
},
"downloadingStatus": {
"message": "ダウンロード中..."
},
"semanticEngineReadyStatus": {
"message": "セマンティックエンジン準備完了"
},
"semanticEngineInitializingStatus": {
"message": "セマンティックエンジン初期化中..."
},
"semanticEngineInitFailedStatus": {
"message": "セマンティックエンジンの初期化に失敗しました"
},
"semanticEngineNotInitStatus": {
"message": "セマンティックエンジン未初期化"
},
"initSemanticEngineButton": {
"message": "セマンティックエンジンを初期化"
},
"reinitializeButton": {
"message": "再初期化"
},
"downloadingModelStatus": {
"message": "モデルをダウンロード中... $1%",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "モデルを切り替え中..."
},
"modelLoadedStatus": {
"message": "モデル読み込み完了"
},
"modelFailedStatus": {
"message": "モデルの読み込みに失敗しました"
},
"lightweightModelDescription": {
"message": "軽量多言語モデル"
},
"betterThanSmallDescription": {
"message": "e5-smallよりわずかに大きいが、性能は向上"
},
"multilingualModelDescription": {
"message": "多言語対応セマンティックモデル"
},
"fastPerformance": {
"message": "高速"
},
"balancedPerformance": {
"message": "バランス"
},
"accuratePerformance": {
"message": "高精度"
},
"networkErrorMessage": {
"message": "ネットワーク接続エラーです。ネットワークを確認して再試行してください"
},
"modelCorruptedErrorMessage": {
"message": "モデルファイルが破損しているか不完全です。再ダウンロードしてください"
},
"unknownErrorMessage": {
"message": "不明なエラーです。ネットワークがHuggingFaceにアクセスできるか確認してください"
},
"permissionDeniedErrorMessage": {
"message": "権限が拒否されました"
},
"timeoutErrorMessage": {
"message": "操作がタイムアウトしました"
},
"indexedPagesLabel": {
"message": "インデックス化されたページ"
},
"indexSizeLabel": {
"message": "インデックスサイズ"
},
"activeTabsLabel": {
"message": "アクティブなタブ"
},
"vectorDocumentsLabel": {
"message": "ベクトルドキュメント"
},
"cacheSizeLabel": {
"message": "キャッシュサイズ"
},
"cacheEntriesLabel": {
"message": "キャッシュエントリ"
},
"clearAllDataButton": {
"message": "全データをクリア"
},
"clearAllCacheButton": {
"message": "全キャッシュをクリア"
},
"cleanExpiredCacheButton": {
"message": "期限切れキャッシュをクリーンアップ"
},
"exportDataButton": {
"message": "データのエクスポート"
},
"importDataButton": {
"message": "データのインポート"
},
"confirmClearDataTitle": {
"message": "データクリアの確認"
},
"settingsTitle": {
"message": "設定"
},
"aboutTitle": {
"message": "情報"
},
"helpTitle": {
"message": "ヘルプ"
},
"clearDataWarningMessage": {
"message": "この操作は、インデックス化されたすべてのウェブページコンテンツとベクトルデータをクリアします。これには以下が含まれます:"
},
"clearDataList1": {
"message": "すべてのウェブページテキストコンテンツインデックス"
},
"clearDataList2": {
"message": "ベクトル埋め込みデータ"
},
"clearDataList3": {
"message": "検索履歴とキャッシュ"
},
"clearDataIrreversibleWarning": {
"message": "この操作は元に戻せません!クリア後、再度ウェブページを閲覧してインデックスを再構築する必要があります。"
},
"confirmClearButton": {
"message": "クリアを確認"
},
"cacheDetailsLabel": {
"message": "キャッシュ詳細"
},
"noCacheDataMessage": {
"message": "キャッシュデータがありません"
},
"loadingCacheInfoStatus": {
"message": "キャッシュ情報を読み込み中..."
},
"processingCacheStatus": {
"message": "キャッシュを処理中..."
},
"expiredLabel": {
"message": "期限切れ"
},
"bookmarksBarLabel": {
"message": "ブックマークバー"
},
"newTabLabel": {
"message": "新しいタブ"
},
"currentPageLabel": {
"message": "現在のページ"
},
"menuLabel": {
"message": "メニュー"
},
"navigationLabel": {
"message": "ナビゲーション"
},
"mainContentLabel": {
"message": "メインコンテンツ"
},
"languageSelectorLabel": {
"message": "言語"
},
"themeLabel": {
"message": "テーマ"
},
"lightTheme": {
"message": "ライト"
},
"darkTheme": {
"message": "ダーク"
},
"autoTheme": {
"message": "自動"
},
"advancedSettingsLabel": {
"message": "詳細設定"
},
"debugModeLabel": {
"message": "デバッグモード"
},
"verboseLoggingLabel": {
"message": "詳細ロギング"
},
"successNotification": {
"message": "操作が正常に完了しました"
},
"warningNotification": {
"message": "警告:続行する前に確認してください"
},
"infoNotification": {
"message": "情報"
},
"configCopiedNotification": {
"message": "設定がクリップボードにコピーされました"
},
"dataClearedNotification": {
"message": "データが正常にクリアされました"
},
"bytesUnit": {
"message": "バイト"
},
"kilobytesUnit": {
"message": "KB"
},
"megabytesUnit": {
"message": "MB"
},
"gigabytesUnit": {
"message": "GB"
},
"itemsUnit": {
"message": "項目"
},
"pagesUnit": {
"message": "ページ"
}
}
================================================
FILE: app/chrome-extension/_locales/ko/messages.json
================================================
{
"extensionName": {
"message": "chrome-mcp-server",
"description": "확장 프로그램 이름"
},
"extensionDescription": {
"message": "크롬 브라우저와 연동하여 브라우저 기능을 제어하는 MCP 서버입니다.",
"description": "확장 프로그램 설명"
},
"nativeServerConfigLabel": {
"message": "네이티브 서버 설정",
"description": "네이티브 서버 설정의 주 섹션 제목"
},
"semanticEngineLabel": {
"message": "시맨틱 엔진",
"description": "시맨틱 엔진의 주 섹션 제목"
},
"embeddingModelLabel": {
"message": "임베딩 모델",
"description": "모델 선택의 주 섹션 제목"
},
"indexDataManagementLabel": {
"message": "인덱스 데이터 관리",
"description": "데이터 관리의 주 섹션 제목"
},
"modelCacheManagementLabel": {
"message": "모델 캐시 관리",
"description": "캐시 관리의 주 섹션 제목"
},
"statusLabel": {
"message": "상태",
"description": "일반 상태 레이블"
},
"runningStatusLabel": {
"message": "실행 상태",
"description": "서버 실행 상태 레이블"
},
"connectionStatusLabel": {
"message": "연결 상태",
"description": "연결 상태 레이블"
},
"lastUpdatedLabel": {
"message": "마지막 업데이트:",
"description": "마지막 업데이트 타임스탬프 레이블"
},
"connectButton": {
"message": "연결",
"description": "연결 버튼 텍스트"
},
"disconnectButton": {
"message": "연결 끊기",
"description": "연결 끊기 버튼 텍스트"
},
"connectingStatus": {
"message": "연결 중...",
"description": "연결 상태 메시지"
},
"connectedStatus": {
"message": "연결됨",
"description": "연결된 상태 메시지"
},
"disconnectedStatus": {
"message": "연결 끊김",
"description": "연결이 끊긴 상태 메시지"
},
"detectingStatus": {
"message": "감지 중...",
"description": "감지 상태 메시지"
},
"serviceRunningStatus": {
"message": "서비스 실행 중 (포트: $PORT$)",
"description": "포트 번호와 함께 서비스 실행 중 상태",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "서비스에 연결되지 않음",
"description": "서비스가 연결되지 않은 상태"
},
"connectedServiceNotStartedStatus": {
"message": "연결됨, 서비스 시작되지 않음",
"description": "연결되었지만 서비스가 시작되지 않은 상태"
},
"mcpServerConfigLabel": {
"message": "MCP 서버 설정",
"description": "MCP 서버 설정 섹션 레이블"
},
"connectionPortLabel": {
"message": "연결 포트",
"description": "연결 포트 입력 레이블"
},
"refreshStatusButton": {
"message": "상태 새로고침",
"description": "상태 새로고침 버튼 툴팁"
},
"copyConfigButton": {
"message": "설정 복사",
"description": "설정 복사 버튼 텍스트"
},
"retryButton": {
"message": "재시도",
"description": "재시도 버튼 텍스트"
},
"cancelButton": {
"message": "취소",
"description": "취소 버튼 텍스트"
},
"confirmButton": {
"message": "확인",
"description": "확인 버튼 텍스트"
},
"saveButton": {
"message": "저장",
"description": "저장 버튼 텍스트"
},
"closeButton": {
"message": "닫기",
"description": "닫기 버튼 텍스트"
},
"resetButton": {
"message": "초기화",
"description": "초기화 버튼 텍스트"
},
"initializingStatus": {
"message": "초기화 중...",
"description": "초기화 진행 메시지"
},
"processingStatus": {
"message": "처리 중...",
"description": "처리 진행 메시지"
},
"loadingStatus": {
"message": "로드 중...",
"description": "로드 진행 메시지"
},
"clearingStatus": {
"message": "삭제 중...",
"description": "삭제 진행 메시지"
},
"cleaningStatus": {
"message": "정리 중...",
"description": "정리 진행 메시지"
},
"downloadingStatus": {
"message": "다운로드 중...",
"description": "다운로드 진행 메시지"
},
"semanticEngineReadyStatus": {
"message": "시맨틱 엔진 준비 완료",
"description": "시맨틱 엔진 준비 완료 상태"
},
"semanticEngineInitializingStatus": {
"message": "시맨틱 엔진 초기화 중...",
"description": "시맨틱 엔진 초기화 상태"
},
"semanticEngineInitFailedStatus": {
"message": "시맨틱 엔진 초기화 실패",
"description": "시맨틱 엔진 초기화 실패 상태"
},
"semanticEngineNotInitStatus": {
"message": "시맨틱 엔진이 초기화되지 않음",
"description": "시맨틱 엔진이 초기화되지 않은 상태"
},
"initSemanticEngineButton": {
"message": "시맨틱 엔진 초기화",
"description": "시맨틱 엔진 초기화 버튼 텍스트"
},
"reinitializeButton": {
"message": "재초기화",
"description": "재초기화 버튼 텍스트"
},
"downloadingModelStatus": {
"message": "모델 다운로드 중... $PROGRESS$%",
"description": "백분율이 포함된 모델 다운로드 진행 상태",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "모델 전환 중...",
"description": "모델 전환 진행 메시지"
},
"modelLoadedStatus": {
"message": "모델 로드 완료",
"description": "모델 로드 성공 상태"
},
"modelFailedStatus": {
"message": "모델 로드 실패",
"description": "모델 로드 실패 상태"
},
"lightweightModelDescription": {
"message": "경량 다국어 모델",
"description": "경량 모델 옵션 설명"
},
"betterThanSmallDescription": {
"message": "e5-small보다 약간 크지만 성능이 더 좋습니다",
"description": "중간 모델 옵션 설명"
},
"multilingualModelDescription": {
"message": "다국어 시맨틱 모델",
"description": "다국어 모델 옵션 설명"
},
"fastPerformance": {
"message": "빠름",
"description": "빠른 성능 표시"
},
"balancedPerformance": {
"message": "균형",
"description": "균형 잡힌 성능 표시"
},
"accuratePerformance": {
"message": "정확",
"description": "정확한 성능 표시"
},
"networkErrorMessage": {
"message": "네트워크 연결 오류, 네트워크를 확인하고 다시 시도하세요",
"description": "네트워크 연결 오류 메시지"
},
"modelCorruptedErrorMessage": {
"message": "모델 파일이 손상되었거나 불완전합니다. 다운로드를 다시 시도하세요",
"description": "모델 손상 오류 메시지"
},
"unknownErrorMessage": {
"message": "알 수 없는 오류, 네트워크에서 HuggingFace에 접속할 수 있는지 확인하세요",
"description": "알 수 없는 오류 대체 메시지"
},
"permissionDeniedErrorMessage": {
"message": "권한이 거부되었습니다",
"description": "권한 거부 오류 메시지"
},
"timeoutErrorMessage": {
"message": "작업 시간 초과",
"description": "시간 초과 오류 메시지"
},
"indexedPagesLabel": {
"message": "인덱싱된 페이지",
"description": "인덱싱된 페이지 수 레이블"
},
"indexSizeLabel": {
"message": "인덱스 크기",
"description": "인덱스 크기 레이블"
},
"activeTabsLabel": {
"message": "활성 탭",
"description": "활성 탭 수 레이블"
},
"vectorDocumentsLabel": {
"message": "벡터 문서",
"description": "벡터 문서 수 레이블"
},
"cacheSizeLabel": {
"message": "캐시 크기",
"description": "캐시 크기 레이블"
},
"cacheEntriesLabel": {
"message": "캐시 항목",
"description": "캐시 항목 수 레이블"
},
"clearAllDataButton": {
"message": "모든 데이터 지우기",
"description": "모든 데이터 지우기 버튼 텍스트"
},
"clearAllCacheButton": {
"message": "모든 캐시 지우기",
"description": "모든 캐시 지우기 버튼 텍스트"
},
"cleanExpiredCacheButton": {
"message": "만료된 캐시 정리",
"description": "만료된 캐시 정리 버튼 텍스트"
},
"exportDataButton": {
"message": "데이터 내보내기",
"description": "데이터 내보내기 버튼 텍스트"
},
"importDataButton": {
"message": "데이터 가져오기",
"description": "데이터 가져오기 버튼 텍스트"
},
"confirmClearDataTitle": {
"message": "데이터 지우기 확인",
"description": "데이터 지우기 확인 대화상자 제목"
},
"settingsTitle": {
"message": "설정",
"description": "설정 대화상자 제목"
},
"aboutTitle": {
"message": "정보",
"description": "정보 대화상자 제목"
},
"helpTitle": {
"message": "도움말",
"description": "도움말 대화상자 제목"
},
"clearDataWarningMessage": {
"message": "이 작업은 다음을 포함한 모든 인덱싱된 웹페이지 콘텐츠와 벡터 데이터를 지웁니다:",
"description": "데이터 지우기 경고 메시지"
},
"clearDataList1": {
"message": "모든 웹페이지 텍스트 콘텐츠 인덱스",
"description": "데이터 지우기 목록 첫 번째 항목"
},
"clearDataList2": {
"message": "벡터 임베딩 데이터",
"description": "데이터 지우기 목록 두 번째 항목"
},
"clearDataList3": {
"message": "검색 기록 및 캐시",
"description": "데이터 지우기 목록 세 번째 항목"
},
"clearDataIrreversibleWarning": {
"message": "이 작업은 되돌릴 수 없습니다! 삭제 후에는 인덱스를 다시 생성하기 위해 웹페이지를 다시 방문해야 합니다.",
"description": "되돌릴 수 없는 작업 경고"
},
"confirmClearButton": {
"message": "삭제 확인",
"description": "삭제 작업 확인 버튼"
},
"cacheDetailsLabel": {
"message": "캐시 정보",
"description": "캐시 정보 섹션 레이블"
},
"noCacheDataMessage": {
"message": "캐시 데이터 없음",
"description": "사용 가능한 캐시 데이터 없음 메시지"
},
"loadingCacheInfoStatus": {
"message": "캐시 정보를 불러오는 중...",
"description": "캐시 정보 로드 상태"
},
"processingCacheStatus": {
"message": "캐시 처리 중...",
"description": "캐시 처리 상태"
},
"expiredLabel": {
"message": "만료됨",
"description": "만료된 항목 레이블"
},
"bookmarksBarLabel": {
"message": "북마크바",
"description": "북마크바 폴더 이름"
},
"newTabLabel": {
"message": "새 탭",
"description": "새 탭 레이블"
},
"currentPageLabel": {
"message": "현재 페이지",
"description": "현재 페이지 레이블"
},
"menuLabel": {
"message": "메뉴",
"description": "메뉴 접근성 레이블"
},
"navigationLabel": {
"message": "탐색",
"description": "탐색 접근성 레이블"
},
"mainContentLabel": {
"message": "주요 콘텐츠",
"description": "주요 콘텐츠 접근성 레이블"
},
"languageSelectorLabel": {
"message": "언어",
"description": "언어 선택기 레이블"
},
"themeLabel": {
"message": "테마",
"description": "테마 선택기 레이블"
},
"lightTheme": {
"message": "라이트",
"description": "라이트 테마 옵션"
},
"darkTheme": {
"message": "다크",
"description": "다크 테마 옵션"
},
"autoTheme": {
"message": "자동",
"description": "자동 테마 옵션"
},
"advancedSettingsLabel": {
"message": "고급 설정",
"description": "고급 설정 섹션 레이블"
},
"debugModeLabel": {
"message": "디버그 모드",
"description": "디버그 모드 토글 레이블"
},
"verboseLoggingLabel": {
"message": "상세 로깅",
"description": "상세 로깅 토글 레이블"
},
"successNotification": {
"message": "작업이 성공적으로 완료되었습니다",
"description": "일반 성공 알림"
},
"warningNotification": {
"message": "경고: 계속하기 전에 검토하세요",
"description": "일반 경고 알림"
},
"infoNotification": {
"message": "정보",
"description": "일반 정보 알림"
},
"configCopiedNotification": {
"message": "설정이 클립보드에 복사되었습니다",
"description": "설정 복사 성공 메시지"
},
"dataClearedNotification": {
"message": "데이터가 성공적으로 삭제되었습니다",
"description": "데이터 삭제 성공 메시지"
},
"bytesUnit": {
"message": "바이트",
"description": "바이트 단위"
},
"kilobytesUnit": {
"message": "KB",
"description": "킬로바이트 단위"
},
"megabytesUnit": {
"message": "MB",
"description": "메가바이트 단위"
},
"gigabytesUnit": {
"message": "GB",
"description": "기가바이트 단위"
},
"itemsUnit": {
"message": "개",
"description": "항목 개수 단위"
},
"pagesUnit": {
"message": "페이지",
"description": "페이지 수 단위"
}
}
================================================
FILE: app/chrome-extension/_locales/zh_CN/messages.json
================================================
{
"extensionName": {
"message": "chrome-mcp-server",
"description": "扩展名称"
},
"extensionDescription": {
"message": "使用你自己的 Chrome 浏览器暴露浏览器功能",
"description": "扩展描述"
},
"nativeServerConfigLabel": {
"message": "Native Server 配置",
"description": "本地服务器设置的主要节标题"
},
"semanticEngineLabel": {
"message": "语义引擎",
"description": "语义引擎的主要节标题"
},
"embeddingModelLabel": {
"message": "Embedding模型",
"description": "模型选择的主要节标题"
},
"indexDataManagementLabel": {
"message": "索引数据管理",
"description": "数据管理的主要节标题"
},
"modelCacheManagementLabel": {
"message": "模型缓存管理",
"description": "缓存管理的主要节标题"
},
"statusLabel": {
"message": "状态",
"description": "通用状态标签"
},
"runningStatusLabel": {
"message": "运行状态",
"description": "服务器运行状态标签"
},
"connectionStatusLabel": {
"message": "连接状态",
"description": "连接状态标签"
},
"lastUpdatedLabel": {
"message": "最后更新:",
"description": "最后更新时间戳标签"
},
"connectButton": {
"message": "连接",
"description": "连接按钮文本"
},
"disconnectButton": {
"message": "断开",
"description": "断开连接按钮文本"
},
"connectingStatus": {
"message": "连接中...",
"description": "连接状态消息"
},
"connectedStatus": {
"message": "已连接",
"description": "已连接状态消息"
},
"disconnectedStatus": {
"message": "已断开",
"description": "已断开状态消息"
},
"detectingStatus": {
"message": "检测中...",
"description": "检测状态消息"
},
"serviceRunningStatus": {
"message": "服务运行中 (端口: $PORT$)",
"description": "带端口号的服务运行状态",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "服务未连接",
"description": "服务未连接状态"
},
"connectedServiceNotStartedStatus": {
"message": "已连接,服务未启动",
"description": "已连接但服务未启动状态"
},
"mcpServerConfigLabel": {
"message": "MCP 服务器配置",
"description": "MCP 服务器配置节标签"
},
"connectionPortLabel": {
"message": "连接端口",
"description": "连接端口输入标签"
},
"refreshStatusButton": {
"message": "刷新状态",
"description": "刷新状态按钮提示"
},
"copyConfigButton": {
"message": "复制配置",
"description": "复制配置按钮文本"
},
"retryButton": {
"message": "重试",
"description": "重试按钮文本"
},
"cancelButton": {
"message": "取消",
"description": "取消按钮文本"
},
"confirmButton": {
"message": "确认",
"description": "确认按钮文本"
},
"saveButton": {
"message": "保存",
"description": "保存按钮文本"
},
"closeButton": {
"message": "关闭",
"description": "关闭按钮文本"
},
"resetButton": {
"message": "重置",
"description": "重置按钮文本"
},
"initializingStatus": {
"message": "初始化中...",
"description": "初始化进度消息"
},
"processingStatus": {
"message": "处理中...",
"description": "处理进度消息"
},
"loadingStatus": {
"message": "加载中...",
"description": "加载进度消息"
},
"clearingStatus": {
"message": "清空中...",
"description": "清空进度消息"
},
"cleaningStatus": {
"message": "清理中...",
"description": "清理进度消息"
},
"downloadingStatus": {
"message": "下载中...",
"description": "下载进度消息"
},
"semanticEngineReadyStatus": {
"message": "语义引擎已就绪",
"description": "语义引擎就绪状态"
},
"semanticEngineInitializingStatus": {
"message": "语义引擎初始化中...",
"description": "语义引擎初始化状态"
},
"semanticEngineInitFailedStatus": {
"message": "语义引擎初始化失败",
"description": "语义引擎初始化失败状态"
},
"semanticEngineNotInitStatus": {
"message": "语义引擎未初始化",
"description": "语义引擎未初始化状态"
},
"initSemanticEngineButton": {
"message": "初始化语义引擎",
"description": "初始化语义引擎按钮文本"
},
"reinitializeButton": {
"message": "重新初始化",
"description": "重新初始化按钮文本"
},
"downloadingModelStatus": {
"message": "下载模型中... $PROGRESS$%",
"description": "带百分比的模型下载进度",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "切换模型中...",
"description": "模型切换进度消息"
},
"modelLoadedStatus": {
"message": "模型已加载",
"description": "模型成功加载状态"
},
"modelFailedStatus": {
"message": "模型加载失败",
"description": "模型加载失败状态"
},
"lightweightModelDescription": {
"message": "轻量级多语言模型",
"description": "轻量级模型选项的描述"
},
"betterThanSmallDescription": {
"message": "比e5-small稍大,但效果更好",
"description": "中等模型选项的描述"
},
"multilingualModelDescription": {
"message": "多语言语义模型",
"description": "多语言模型选项的描述"
},
"fastPerformance": {
"message": "快速",
"description": "快速性能指示器"
},
"balancedPerformance": {
"message": "平衡",
"description": "平衡性能指示器"
},
"accuratePerformance": {
"message": "精确",
"description": "精确性能指示器"
},
"networkErrorMessage": {
"message": "网络连接错误,请检查网络连接后重试",
"description": "网络连接错误消息"
},
"modelCorruptedErrorMessage": {
"message": "模型文件损坏或不完整,请重试下载",
"description": "模型损坏错误消息"
},
"unknownErrorMessage": {
"message": "未知错误,请检查你的网络是否可以访问HuggingFace",
"description": "未知错误回退消息"
},
"permissionDeniedErrorMessage": {
"message": "权限被拒绝",
"description": "权限被拒绝错误消息"
},
"timeoutErrorMessage": {
"message": "操作超时",
"description": "超时错误消息"
},
"indexedPagesLabel": {
"message": "已索引页面",
"description": "已索引页面数量标签"
},
"indexSizeLabel": {
"message": "索引大小",
"description": "索引大小标签"
},
"activeTabsLabel": {
"message": "活跃标签页",
"description": "活跃标签页数量标签"
},
"vectorDocumentsLabel": {
"message": "向量文档",
"description": "向量文档数量标签"
},
"cacheSizeLabel": {
"message": "缓存大小",
"description": "缓存大小标签"
},
"cacheEntriesLabel": {
"message": "缓存条目",
"description": "缓存条目数量标签"
},
"clearAllDataButton": {
"message": "清空所有数据",
"description": "清空所有数据按钮文本"
},
"clearAllCacheButton": {
"message": "清空所有缓存",
"description": "清空所有缓存按钮文本"
},
"cleanExpiredCacheButton": {
"message": "清理过期缓存",
"description": "清理过期缓存按钮文本"
},
"exportDataButton": {
"message": "导出数据",
"description": "导出数据按钮文本"
},
"importDataButton": {
"message": "导入数据",
"description": "导入数据按钮文本"
},
"confirmClearDataTitle": {
"message": "确认清空数据",
"description": "清空数据确认对话框标题"
},
"settingsTitle": {
"message": "设置",
"description": "设置对话框标题"
},
"aboutTitle": {
"message": "关于",
"description": "关于对话框标题"
},
"helpTitle": {
"message": "帮助",
"description": "帮助对话框标题"
},
"clearDataWarningMessage": {
"message": "此操作将清空所有已索引的网页内容和向量数据,包括:",
"description": "清空数据警告消息"
},
"clearDataList1": {
"message": "所有网页的文本内容索引",
"description": "清空数据列表第一项"
},
"clearDataList2": {
"message": "向量嵌入数据",
"description": "清空数据列表第二项"
},
"clearDataList3": {
"message": "搜索历史和缓存",
"description": "清空数据列表第三项"
},
"clearDataIrreversibleWarning": {
"message": "此操作不可撤销!清空后需要重新浏览网页来重建索引。",
"description": "不可逆操作警告"
},
"confirmClearButton": {
"message": "确认清空",
"description": "确认清空操作按钮"
},
"cacheDetailsLabel": {
"message": "缓存详情",
"description": "缓存详情节标签"
},
"noCacheDataMessage": {
"message": "暂无缓存数据",
"description": "无缓存数据可用消息"
},
"loadingCacheInfoStatus": {
"message": "正在加载缓存信息...",
"description": "加载缓存信息状态"
},
"processingCacheStatus": {
"message": "处理缓存中...",
"description": "处理缓存状态"
},
"expiredLabel": {
"message": "已过期",
"description": "过期项标签"
},
"bookmarksBarLabel": {
"message": "书签栏",
"description": "书签栏文件夹名称"
},
"newTabLabel": {
"message": "新标签页",
"description": "新标签页标签"
},
"currentPageLabel": {
"message": "当前页面",
"description": "当前页面标签"
},
"menuLabel": {
"message": "菜单",
"description": "菜单辅助功能标签"
},
"navigationLabel": {
"message": "导航",
"description": "导航辅助功能标签"
},
"mainContentLabel": {
"message": "主要内容",
"description": "主要内容辅助功能标签"
},
"languageSelectorLabel": {
"message": "语言",
"description": "语言选择器标签"
},
"themeLabel": {
"message": "主题",
"description": "主题选择器标签"
},
"lightTheme": {
"message": "浅色",
"description": "浅色主题选项"
},
"darkTheme": {
"message": "深色",
"description": "深色主题选项"
},
"autoTheme": {
"message": "自动",
"description": "自动主题选项"
},
"advancedSettingsLabel": {
"message": "高级设置",
"description": "高级设置节标签"
},
"debugModeLabel": {
"message": "调试模式",
"description": "调试模式切换标签"
},
"verboseLoggingLabel": {
"message": "详细日志",
"description": "详细日志切换标签"
},
"successNotification": {
"message": "操作成功完成",
"description": "通用成功通知"
},
"warningNotification": {
"message": "警告:请在继续之前检查",
"description": "通用警告通知"
},
"infoNotification": {
"message": "信息",
"description": "通用信息通知"
},
"configCopiedNotification": {
"message": "配置已复制到剪贴板",
"description": "配置复制成功消息"
},
"dataClearedNotification": {
"message": "数据清空成功",
"description": "数据清空成功消息"
},
"bytesUnit": {
"message": "字节",
"description": "字节单位"
},
"kilobytesUnit": {
"message": "KB",
"description": "千字节单位"
},
"megabytesUnit": {
"message": "MB",
"description": "兆字节单位"
},
"gigabytesUnit": {
"message": "GB",
"description": "吉字节单位"
},
"itemsUnit": {
"message": "项",
"description": "项目计数单位"
},
"pagesUnit": {
"message": "页",
"description": "页面计数单位"
},
"userscriptsManagerTitle": { "message": "脚本管理器", "description": "Options 页标题" },
"emergencySwitchLabel": { "message": "紧急开关", "description": "紧急关闭开关" },
"createRunSectionTitle": { "message": "创建 / 运行", "description": "创建与运行分区标题" },
"nameLabel": { "message": "名称", "description": "名称输入标签" },
"runAtLabel": { "message": "运行时机", "description": "runAt 选择标签" },
"runAtAuto": { "message": "自动", "description": "runAt auto" },
"runAtDocumentStart": { "message": "document_start", "description": "runAt document_start" },
"runAtDocumentEnd": { "message": "document_end", "description": "runAt document_end" },
"runAtDocumentIdle": { "message": "document_idle", "description": "runAt document_idle" },
"worldLabel": { "message": "执行上下文", "description": "world 选择标签" },
"worldAuto": { "message": "自动", "description": "world auto" },
"worldIsolated": { "message": "隔离 (ISOLATED)", "description": "ISOLATED world" },
"worldMain": { "message": "页面 (MAIN)", "description": "MAIN world" },
"modeLabel": { "message": "模式", "description": "模式选择标签" },
"modeAuto": { "message": "自动", "description": "mode auto" },
"modePersistent": { "message": "持久", "description": "mode persistent" },
"modeCss": { "message": "仅样式 (CSS)", "description": "mode css" },
"modeOnce": { "message": "一次运行 (CDP)", "description": "mode once" },
"allFramesLabel": { "message": "全部 frame", "description": "allFrames 复选框" },
"persistLabel": { "message": "持久化", "description": "persist 复选框" },
"dnrFallbackLabel": { "message": "DNR 回退", "description": "DNR fallback 复选框" },
"matchesInputLabel": { "message": "匹配(逗号分隔)", "description": "matches 输入" },
"excludesInputLabel": { "message": "排除(逗号分隔)", "description": "excludes 输入" },
"tagsInputLabel": { "message": "标签(逗号分隔)", "description": "tags 输入" },
"scriptLabel": { "message": "脚本", "description": "脚本文本标签" },
"applyButton": { "message": "应用", "description": "应用按钮" },
"runOnceButton": { "message": "一次运行(CDP)", "description": "一次运行按钮" },
"listSectionTitle": { "message": "脚本列表", "description": "列表分区标题" },
"queryLabel": { "message": "搜索", "description": "查询输入标签" },
"statusAll": { "message": "全部", "description": "状态-全部" },
"statusEnabled": { "message": "启用", "description": "状态-启用" },
"statusDisabled": { "message": "禁用", "description": "状态-禁用" },
"domainLabel": { "message": "域名", "description": "域名过滤标签" },
"exportAllButton": { "message": "导出全部", "description": "导出按钮" },
"tableHeaderName": { "message": "名称", "description": "表头-名称" },
"tableHeaderWorld": { "message": "执行上下文", "description": "表头-World" },
"tableHeaderRunAt": { "message": "运行时机", "description": "表头-RunAt" },
"tableHeaderUpdated": { "message": "更新时间", "description": "表头-更新时间" },
"deleteButton": { "message": "删除", "description": "删除按钮" },
"placeholderOptional": { "message": "可选", "description": "通用可选占位符" },
"placeholderMatchesExample": {
"message": "例如:https://*.example.com/*",
"description": "匹配示例占位符"
},
"placeholderScriptHint": { "message": "在此粘贴 JS/CSS/TM", "description": "脚本文本域占位符" },
"placeholderDomainHint": { "message": "example.com", "description": "域名筛选占位符" }
}
================================================
FILE: app/chrome-extension/_locales/zh_TW/messages.json
================================================
{
"extensionName": {
"message": "chrome-mcp-server",
"description": "擴充功能名稱"
},
"extensionDescription": {
"message": "使用您自己的 Chrome 瀏覽器暴露瀏覽器功能",
"description": "擴充功能描述"
},
"nativeServerConfigLabel": {
"message": "原生伺服器設定",
"description": "本機伺服器設定的主要區段標題"
},
"semanticEngineLabel": {
"message": "語意引擎",
"description": "語意引擎的主要區段標題"
},
"embeddingModelLabel": {
"message": "Embedding 模型",
"description": "模型選擇的主要區段標題"
},
"indexDataManagementLabel": {
"message": "索引資料管理",
"description": "資料管理主要區段標題"
},
"modelCacheManagementLabel": {
"message": "模型快取管理",
"description": "快取管理主要區段標題"
},
"statusLabel": {
"message": "狀態",
"description": "通用狀態標籤"
},
"runningStatusLabel": {
"message": "執行狀態",
"description": "伺服器執行狀態標籤"
},
"connectionStatusLabel": {
"message": "連線狀態",
"description": "連線狀態標籤"
},
"lastUpdatedLabel": {
"message": "最後更新:",
"description": "最後更新時間戳標籤"
},
"connectButton": {
"message": "連線",
"description": "連線按鈕文字"
},
"disconnectButton": {
"message": "中斷連線",
"description": "中斷連線按鈕文字"
},
"connectingStatus": {
"message": "連線中...",
"description": "連線狀態訊息"
},
"connectedStatus": {
"message": "已連線",
"description": "已連線狀態訊息"
},
"disconnectedStatus": {
"message": "已中斷",
"description": "已中斷狀態訊息"
},
"detectingStatus": {
"message": "偵測中...",
"description": "偵測狀態訊息"
},
"serviceRunningStatus": {
"message": "服務執行中 (連結埠: $PORT$)",
"description": "含連結埠號的服務執行狀態",
"placeholders": {
"port": {
"content": "$1",
"example": "12306"
}
}
},
"serviceNotConnectedStatus": {
"message": "服務未連線",
"description": "服務未連線狀態"
},
"connectedServiceNotStartedStatus": {
"message": "已連線,服務未啟動",
"description": "已連線但服務未啟動狀態"
},
"mcpServerConfigLabel": {
"message": "MCP 伺服器設定",
"description": "MCP 伺服器設定區段標籤"
},
"connectionPortLabel": {
"message": "連結埠",
"description": "連結埠輸入標籤"
},
"refreshStatusButton": {
"message": "重新整理狀態",
"description": "重新整理狀態按鈕提示"
},
"copyConfigButton": {
"message": "複製設定",
"description": "複製設定按鈕文字"
},
"retryButton": {
"message": "重試",
"description": "重試按鈕文字"
},
"cancelButton": {
"message": "取消",
"description": "取消按鈕文字"
},
"confirmButton": {
"message": "確認",
"description": "確認按鈕文字"
},
"saveButton": {
"message": "儲存",
"description": "儲存按鈕文字"
},
"closeButton": {
"message": "關閉",
"description": "關閉按鈕文字"
},
"resetButton": {
"message": "重設",
"description": "重設按鈕文字"
},
"initializingStatus": {
"message": "初始化中...",
"description": "初始化進度訊息"
},
"processingStatus": {
"message": "處理中...",
"description": "處理進度訊息"
},
"loadingStatus": {
"message": "載入中...",
"description": "載入進度訊息"
},
"clearingStatus": {
"message": "清除中...",
"description": "清除進度訊息"
},
"cleaningStatus": {
"message": "清理中...",
"description": "清理進度訊息"
},
"downloadingStatus": {
"message": "下載中...",
"description": "下載進度訊息"
},
"semanticEngineReadyStatus": {
"message": "語意引擎已就緒",
"description": "語意引擎就緒狀態"
},
"semanticEngineInitializingStatus": {
"message": "語意引擎初始化中...",
"description": "語意引擎初始化狀態"
},
"semanticEngineInitFailedStatus": {
"message": "語意引擎初始化失敗",
"description": "語意引擎初始化失敗狀態"
},
"semanticEngineNotInitStatus": {
"message": "語意引擎未初始化",
"description": "語意引擎未初始化狀態"
},
"initSemanticEngineButton": {
"message": "初始化語意引擎",
"description": "初始化語意引擎按鈕文字"
},
"reinitializeButton": {
"message": "重新初始化",
"description": "重新初始化按鈕文字"
},
"downloadingModelStatus": {
"message": "正在下載模型... $PROGRESS$%",
"description": "含百分比的模型下載進度",
"placeholders": {
"progress": {
"content": "$1",
"example": "50"
}
}
},
"switchingModelStatus": {
"message": "正在切換模型...",
"description": "模型切換進度訊息"
},
"modelLoadedStatus": {
"message": "模型已載入",
"description": "模型成功載入狀態"
},
"modelFailedStatus": {
"message": "模型載入失敗",
"description": "模型載入失敗狀態"
},
"lightweightModelDescription": {
"message": "輕量級多語言模型",
"description": "輕量級模型選項描述"
},
"betterThanSmallDescription": {
"message": "比 e5-small 稍大,但效果更佳",
"description": "中等模型選項描述"
},
"multilingualModelDescription": {
"message": "多語言語意模型",
"description": "多語言模型選項描述"
},
"fastPerformance": {
"message": "快速",
"description": "快速效能指標"
},
"balancedPerformance": {
"message": "平衡",
"description": "平衡效能指標"
},
"accuratePerformance": {
"message": "精確",
"description": "精確效能指標"
},
"networkErrorMessage": {
"message": "網路連線錯誤,請檢查網路後再試一次",
"description": "網路連線錯誤訊息"
},
"modelCorruptedErrorMessage": {
"message": "模型檔案毀損或不完整,請重新下載",
"description": "模型毀損錯誤訊息"
},
"unknownErrorMessage": {
"message": "未知錯誤,請檢查您的網路是否可存取 HuggingFace",
"description": "未知錯誤回退訊息"
},
"permissionDeniedErrorMessage": {
"message": "權限被拒絕",
"description": "權限被拒絕錯誤訊息"
},
"timeoutErrorMessage": {
"message": "操作逾時",
"description": "逾時錯誤訊息"
},
"indexedPagesLabel": {
"message": "已索引頁面",
"description": "已索引頁面數量標籤"
},
"indexSizeLabel": {
"message": "索引大小",
"description": "索引大小標籤"
},
"activeTabsLabel": {
"message": "作用中分頁",
"description": "作用中分頁數量標籤"
},
"vectorDocumentsLabel": {
"message": "向量文件",
"description": "向量文件數量標籤"
},
"cacheSizeLabel": {
"message": "快取大小",
"description": "快取大小標籤"
},
"cacheEntriesLabel": {
"message": "快取項目",
"description": "快取項目數量標籤"
},
"clearAllDataButton": {
"message": "清除所有資料",
"description": "清除所有資料按鈕文字"
},
"clearAllCacheButton": {
"message": "清除所有快取",
"description": "清除所有快取按鈕文字"
},
"cleanExpiredCacheButton": {
"message": "清理過期快取",
"description": "清理過期快取按鈕文字"
},
"exportDataButton": {
"message": "匯出資料",
"description": "匯出資料按鈕文字"
},
"importDataButton": {
"message": "匯入資料",
"description": "匯入資料按鈕文字"
},
"confirmClearDataTitle": {
"message": "確認清除資料",
"description": "清除資料確認對話框標題"
},
"settingsTitle": {
"message": "設定",
"description": "設定對話框標題"
},
"aboutTitle": {
"message": "關於",
"description": "關於對話框標題"
},
"helpTitle": {
"message": "說明",
"description": "說明對話框標題"
},
"clearDataWarningMessage": {
"message": "此操作將清除所有已索引的網頁內容與向量資料,包括:",
"description": "清除資料警告訊息"
},
"clearDataList1": {
"message": "所有網頁的文字內容索引",
"description": "清除資料列表第一項"
},
"clearDataList2": {
"message": "向量嵌入資料",
"description": "清除資料列表第二項"
},
"clearDataList3": {
"message": "搜尋歷史與快取",
"description": "清除資料列表第三項"
},
"clearDataIrreversibleWarning": {
"message": "此操作無法復原!清除後需重新瀏覽網頁以重建索引。",
"description": "不可逆操作警告"
},
"confirmClearButton": {
"message": "確認清除",
"description": "確認清除操作按鈕"
},
"cacheDetailsLabel": {
"message": "快取詳細資訊",
"description": "快取詳細資訊區段標籤"
},
"noCacheDataMessage": {
"message": "尚無快取資料",
"description": "無可用快取資料訊息"
},
"loadingCacheInfoStatus": {
"message": "正在載入快取資訊...",
"description": "載入快取資訊狀態"
},
"processingCacheStatus": {
"message": "正在處理快取...",
"description": "處理快取狀態"
},
"expiredLabel": {
"message": "已過期",
"description": "過期項目標籤"
},
"bookmarksBarLabel": {
"message": "書籤列",
"description": "書籤列資料夾名稱"
},
"newTabLabel": {
"message": "新分頁",
"description": "新分頁標籤"
},
"currentPageLabel": {
"message": "目前頁面",
"description": "目前頁面標籤"
},
"menuLabel": {
"message": "功能表",
"description": "功能表無障礙標籤"
},
"navigationLabel": {
"message": "導覽",
"description": "導覽無障礙標籤"
},
"mainContentLabel": {
"message": "主要內容",
"description": "主要內容無障礙標籤"
},
"languageSelectorLabel": {
"message": "語言",
"description": "語言選擇器標籤"
},
"themeLabel": {
"message": "主題",
"description": "主題選擇器標籤"
},
"lightTheme": {
"message": "淺色",
"description": "淺色主題選項"
},
"darkTheme": {
"message": "深色",
"description": "深色主題選項"
},
"autoTheme": {
"message": "自動",
"description": "自動主題選項"
},
"advancedSettingsLabel": {
"message": "進階設定",
"description": "進階設定區段標籤"
},
"debugModeLabel": {
"message": "偵錯模式",
"description": "偵錯模式切換標籤"
},
"verboseLoggingLabel": {
"message": "詳細日誌",
"description": "詳細日誌切換標籤"
},
"successNotification": {
"message": "操作已成功完成",
"description": "通用成功通知"
},
"warningNotification": {
"message": "警告:請在繼續前檢查",
"description": "通用警告通知"
},
"infoNotification": {
"message": "資訊",
"description": "通用資訊通知"
},
"configCopiedNotification": {
"message": "設定已複製到剪貼簿",
"description": "設定複製成功訊息"
},
"dataClearedNotification": {
"message": "資料清除成功",
"description": "資料清除成功訊息"
},
"bytesUnit": {
"message": "bytes",
"description": "位元組單位"
},
"kilobytesUnit": {
"message": "KB",
"description": "千位元組單位"
},
"megabytesUnit": {
"message": "MB",
"description": "百萬位元組單位"
},
"gigabytesUnit": {
"message": "GB",
"description": "十億位元組單位"
},
"itemsUnit": {
"message": "項目",
"description": "項目計數單位"
},
"pagesUnit": {
"message": "頁面",
"description": "頁面計數單位"
}
}
================================================
FILE: app/chrome-extension/common/agent-models.ts
================================================
/**
* Agent CLI Model Definitions.
*
* Static model definitions for each CLI type.
* Based on the pattern from Claudable (other/cweb).
*/
import type { CodexReasoningEffort } from 'chrome-mcp-shared';
// ============================================================
// Types
// ============================================================
export interface ModelDefinition {
id: string;
name: string;
description?: string;
supportsImages?: boolean;
/** Supported reasoning effort levels for Codex models */
supportedReasoningEfforts?: readonly CodexReasoningEffort[];
}
export type AgentCliType = 'claude' | 'codex' | 'cursor' | 'qwen' | 'glm';
// ============================================================
// Claude Models
// ============================================================
export const CLAUDE_MODELS: ModelDefinition[] = [
{
id: 'claude-sonnet-4-5-20250929',
name: 'Claude Sonnet 4.5',
description: 'Balanced model with large context window',
supportsImages: true,
},
{
id: 'claude-opus-4-5-20251101',
name: 'Claude Opus 4.5',
description: 'Strongest reasoning model',
supportsImages: true,
},
{
id: 'claude-haiku-4-5-20251001',
name: 'Claude Haiku 4.5',
description: 'Fast and cost-efficient',
supportsImages: true,
},
];
export const CLAUDE_DEFAULT_MODEL = 'claude-sonnet-4-5-20250929';
// ============================================================
// Codex Models
// ============================================================
/** Standard reasoning efforts supported by all models */
const CODEX_STANDARD_EFFORTS: readonly CodexReasoningEffort[] = ['low', 'medium', 'high'];
/** Extended reasoning efforts (includes xhigh) - only for gpt-5.2 and gpt-5.1-codex-max */
const CODEX_EXTENDED_EFFORTS: readonly CodexReasoningEffort[] = ['low', 'medium', 'high', 'xhigh'];
export const CODEX_MODELS: ModelDefinition[] = [
{
id: 'gpt-5.1',
name: 'GPT-5.1',
description: 'OpenAI high-quality reasoning model',
supportedReasoningEfforts: CODEX_STANDARD_EFFORTS,
},
{
id: 'gpt-5.2',
name: 'GPT-5.2',
description: 'OpenAI flagship reasoning model with extended effort support',
supportedReasoningEfforts: CODEX_EXTENDED_EFFORTS,
},
{
id: 'gpt-5.1-codex',
name: 'GPT-5.1 Codex',
description: 'Coding-optimized model for agent workflows',
supportedReasoningEfforts: CODEX_STANDARD_EFFORTS,
},
{
id: 'gpt-5.1-codex-max',
name: 'GPT-5.1 Codex Max',
description: 'Highest quality coding model with extended effort support',
supportedReasoningEfforts: CODEX_EXTENDED_EFFORTS,
},
{
id: 'gpt-5.1-codex-mini',
name: 'GPT-5.1 Codex Mini',
description: 'Fast, cost-efficient coding model',
supportedReasoningEfforts: CODEX_STANDARD_EFFORTS,
},
];
export const CODEX_DEFAULT_MODEL = 'gpt-5.1';
// Codex model alias normalization
const CODEX_ALIAS_MAP: Record<string, string> = {
gpt5: 'gpt-5.1',
gpt_5: 'gpt-5.1',
'gpt-5': 'gpt-5.1',
'gpt-5.0': 'gpt-5.1',
};
const CODEX_KNOWN_IDS = new Set(CODEX_MODELS.map((model) => model.id));
/**
* Normalize a Codex model ID, handling aliases and falling back to default.
*/
export function normalizeCodexModelId(model?: string | null): string {
if (!model || typeof model !== 'string') {
return CODEX_DEFAULT_MODEL;
}
const trimmed = model.trim();
if (!trimmed) {
return CODEX_DEFAULT_MODEL;
}
const lower = trimmed.toLowerCase();
if (CODEX_ALIAS_MAP[lower]) {
return CODEX_ALIAS_MAP[lower];
}
if (CODEX_KNOWN_IDS.has(lower)) {
return lower;
}
// If the exact casing exists, allow it
if (CODEX_KNOWN_IDS.has(trimmed)) {
return trimmed;
}
return CODEX_DEFAULT_MODEL;
}
/**
* Get supported reasoning efforts for a Codex model.
* Returns standard efforts (low/medium/high) for unknown models.
*/
export function getCodexReasoningEfforts(modelId?: string | null): readonly CodexReasoningEffort[] {
const normalized = normalizeCodexModelId(modelId);
const model = CODEX_MODELS.find((m) => m.id === normalized);
return model?.supportedReasoningEfforts ?? CODEX_STANDARD_EFFORTS;
}
/**
* Check if a model supports xhigh reasoning effort.
*/
export function supportsXhighEffort(modelId?: string | null): boolean {
const efforts = getCodexReasoningEfforts(modelId);
return efforts.includes('xhigh');
}
// ============================================================
// Cursor Models
// ============================================================
export const CURSOR_MODELS: ModelDefinition[] = [
{
id: 'auto',
name: 'Auto',
description: 'Cursor auto-selects the best model',
},
{
id: 'claude-sonnet-4-5-20250929',
name: 'Claude Sonnet 4.5',
description: 'Anthropic Claude via Cursor',
supportsImages: true,
},
{
id: 'gpt-4.1',
name: 'GPT-4.1',
description: 'OpenAI model via Cursor',
},
];
export const CURSOR_DEFAULT_MODEL = 'auto';
// ============================================================
// Qwen Models
// ============================================================
export const QWEN_MODELS: ModelDefinition[] = [
{
id: 'qwen3-coder-plus',
name: 'Qwen3 Coder Plus',
description: 'Balanced 32k context model for coding',
},
{
id: 'qwen3-coder-pro',
name: 'Qwen3 Coder Pro',
description: 'Larger 128k context with stronger reasoning',
},
{
id: 'qwen3-coder',
name: 'Qwen3 Coder',
description: 'Fast iteration model',
},
];
export const QWEN_DEFAULT_MODEL = 'qwen3-coder-plus';
// ============================================================
// GLM Models
// ============================================================
export const GLM_MODELS: ModelDefinition[] = [
{
id: 'glm-4.6',
name: 'GLM 4.6',
description: 'Zhipu GLM 4.6 agent runtime',
},
];
export const GLM_DEFAULT_MODEL = 'glm-4.6';
// ============================================================
// Aggregated Definitions
// ============================================================
export const CLI_MODEL_DEFINITIONS: Record<AgentCliType, ModelDefinition[]> = {
claude: CLAUDE_MODELS,
codex: CODEX_MODELS,
cursor: CURSOR_MODELS,
qwen: QWEN_MODELS,
glm: GLM_MODELS,
};
export const CLI_DEFAULT_MODELS: Record<AgentCliType, string> = {
claude: CLAUDE_DEFAULT_MODEL,
codex: CODEX_DEFAULT_MODEL,
cursor: CURSOR_DEFAULT_MODEL,
qwen: QWEN_DEFAULT_MODEL,
glm: GLM_DEFAULT_MODEL,
};
// ============================================================
// Helper Functions
// ============================================================
/**
* Get model definitions for a specific CLI type.
*/
export function getModelsForCli(cli: string | null | undefined): ModelDefinition[] {
if (!cli) return [];
const key = cli.toLowerCase() as AgentCliType;
return CLI_MODEL_DEFINITIONS[key] || [];
}
/**
* Get the default model for a CLI type.
*/
export function getDefaultModelForCli(cli: string | null | undefined): string {
if (!cli) return '';
const key = cli.toLowerCase() as AgentCliType;
return CLI_DEFAULT_MODELS[key] || '';
}
/**
* Get display name for a model ID.
*/
export function getModelDisplayName(
cli: string | null | undefined,
modelId: string | null | undefined,
): string {
if (!cli || !modelId) return modelId || '';
const models = getModelsForCli(cli);
const model = models.find((m) => m.id === modelId);
return model?.name || modelId;
}
================================================
FILE: app/chrome-extension/common/constants.ts
================================================
/**
* Chrome Extension Constants
* Centralized configuration values and magic constants
*/
// Native Host Configuration
export const NATIVE_HOST = {
NAME: 'com.chromemcp.nativehost',
DEFAULT_PORT: 12306,
} as const;
// Chrome Extension Icons
export const ICONS = {
NOTIFICATION: 'icon/48.png',
} as const;
// Timeouts and Delays (in milliseconds)
export const TIMEOUTS = {
DEFAULT_WAIT: 1000,
NETWORK_CAPTURE_MAX: 30000,
NETWORK_CAPTURE_IDLE: 3000,
SCREENSHOT_DELAY: 100,
KEYBOARD_DELAY: 50,
CLICK_DELAY: 100,
} as const;
// Limits and Thresholds
export const LIMITS = {
MAX_NETWORK_REQUESTS: 100,
MAX_SEARCH_RESULTS: 50,
MAX_BOOKMARK_RESULTS: 100,
MAX_HISTORY_RESULTS: 100,
SIMILARITY_THRESHOLD: 0.1,
VECTOR_DIMENSIONS: 384,
} as const;
// Error Messages
export const ERROR_MESSAGES = {
NATIVE_CONNECTION_FAILED: 'Failed to connect to native host',
NATIVE_DISCONNECTED: 'Native connection disconnected',
SERVER_STATUS_LOAD_FAILED: 'Failed to load server status',
SERVER_STATUS_SAVE_FAILED: 'Failed to save server status',
TOOL_EXECUTION_FAILED: 'Tool execution failed',
INVALID_PARAMETERS: 'Invalid parameters provided',
PERMISSION_DENIED: 'Permission denied',
TAB_NOT_FOUND: 'Tab not found',
ELEMENT_NOT_FOUND: 'Element not found',
NETWORK_ERROR: 'Network error occurred',
} as const;
// Success Messages
export const SUCCESS_MESSAGES = {
TOOL_EXECUTED: 'Tool executed successfully',
CONNECTION_ESTABLISHED: 'Connection established',
SERVER_STARTED: 'Server started successfully',
SERVER_STOPPED: 'Server stopped successfully',
} as const;
// External Links
export const LINKS = {
TROUBLESHOOTING: 'https://github.com/hangwin/mcp-chrome/blob/master/docs/TROUBLESHOOTING.md',
} as const;
// File Extensions and MIME Types
export const FILE_TYPES = {
STATIC_EXTENSIONS: [
'.css',
'.js',
'.png',
'.jpg',
'.jpeg',
'.gif',
'.svg',
'.ico',
'.woff',
'.woff2',
'.ttf',
],
FILTERED_MIME_TYPES: ['text/html', 'text/css', 'text/javascript', 'application/javascript'],
IMAGE_FORMATS: ['png', 'jpeg', 'webp'] as const,
} as const;
// Network Filtering
export const NETWORK_FILTERS = {
// Substring match against full URL (not just hostname) to support patterns like 'facebook.com/tr'
EXCLUDED_DOMAINS: [
// Google
'google-analytics.com',
'googletagmanager.com',
'analytics.google.com',
'doubleclick.net',
'googlesyndication.com',
'googleads.g.doubleclick.net',
'stats.g.doubleclick.net',
'adservice.google.com',
'pagead2.googlesyndication.com',
// Amazon
'amazon-adsystem.com',
// Microsoft
'bat.bing.com',
'clarity.ms',
// Facebook
'connect.facebook.net',
'facebook.com/tr',
// Twitter
'analytics.twitter.com',
'ads-twitter.com',
// Other ad networks
'ads.yahoo.com',
'adroll.com',
'adnxs.com',
'criteo.com',
'quantserve.com',
'scorecardresearch.com',
// Analytics & session recording
'segment.io',
'amplitude.com',
'mixpanel.com',
'optimizely.com',
'static.hotjar.com',
'script.hotjar.com',
'crazyegg.com',
'clicktale.net',
'mouseflow.com',
'fullstory.com',
// LinkedIn (tracking pixels)
'linkedin.com/px',
],
// Static resource extensions (used when includeStatic=false)
STATIC_RESOURCE_EXTENSIONS: [
'.jpg',
'.jpeg',
'.png',
'.gif',
'.svg',
'.webp',
'.ico',
'.bmp',
'.cur',
'.css',
'.scss',
'.less',
'.js',
'.jsx',
'.ts',
'.tsx',
'.map',
'.woff',
'.woff2',
'.ttf',
'.eot',
'.otf',
'.mp3',
'.mp4',
'.avi',
'.mov',
'.wmv',
'.flv',
'.webm',
'.ogg',
'.wav',
'.pdf',
'.zip',
'.rar',
'.7z',
'.iso',
'.dmg',
'.doc',
'.docx',
'.xls',
'.xlsx',
'.ppt',
'.pptx',
],
// MIME types treated as static/binary (filtered when includeStatic=false)
STATIC_MIME_TYPES_TO_FILTER: [
'image/',
'font/',
'audio/',
'video/',
'text/css',
'text/javascript',
'application/javascript',
'application/x-javascript',
'application/pdf',
'application/zip',
'application/octet-stream',
],
// API-like MIME types (never filtered by MIME)
API_MIME_TYPES: [
'application/json',
'application/xml',
'text/xml',
'text/plain',
'text/event-stream',
'application/x-www-form-urlencoded',
'application/graphql',
'application/grpc',
'application/protobuf',
'application/x-protobuf',
'application/x-json',
'application/ld+json',
'application/problem+json',
'application/problem+xml',
'application/soap+xml',
'application/vnd.api+json',
],
STATIC_RESOURCE_TYPES: ['stylesheet', 'image', 'font', 'media', 'other'],
} as const;
// Semantic Similarity Configuration
export const SEMANTIC_CONFIG = {
DEFAULT_MODEL: 'sentence-transformers/all-MiniLM-L6-v2',
CHUNK_SIZE: 512,
CHUNK_OVERLAP: 50,
BATCH_SIZE: 32,
CACHE_SIZE: 1000,
} as const;
// Storage Keys
export const STORAGE_KEYS = {
SERVER_STATUS: 'serverStatus',
NATIVE_SERVER_PORT: 'nativeServerPort',
NATIVE_AUTO_CONNECT_ENABLED: 'nativeAutoConnectEnabled',
SEMANTIC_MODEL: 'selectedModel',
USER_PREFERENCES: 'userPreferences',
VECTOR_INDEX: 'vectorIndex',
USERSCRIPTS: 'userscripts',
USERSCRIPTS_DISABLED: 'userscripts_disabled',
// Record & Replay storage keys
RR_FLOWS: 'rr_flows',
RR_RUNS: 'rr_runs',
RR_PUBLISHED: 'rr_published_flows',
RR_SCHEDULES: 'rr_schedules',
RR_TRIGGERS: 'rr_triggers',
// Persistent recording state (guards resume across navigations/service worker restarts)
RR_RECORDING_STATE: 'rr_recording_state',
} as const;
// Notification Configuration
export const NOTIFICATIONS = {
PRIORITY: 2,
TYPE: 'basic' as const,
} as const;
export enum ExecutionWorld {
ISOLATED = 'ISOLATED',
MAIN = 'MAIN',
}
================================================
FILE: app/chrome-extension/common/element-marker-types.ts
================================================
// Element marker types shared across background, content scripts, and popup
export type UrlMatchType = 'exact' | 'prefix' | 'host';
export interface ElementMarker {
id: string;
// Original URL where the marker was created
url: string;
// Normalized pieces to support matching
origin: string; // scheme + host + port
host: string; // hostname
path: string; // pathname part only
matchType: UrlMatchType; // default: 'prefix'
name: string; // Human-friendly name, e.g., "Login Button"
selector: string; // Selector string
selectorType?: 'css' | 'xpath'; // Default: css
listMode?: boolean; // Whether this marker was created in list mode (allows multiple matches)
action?: 'click' | 'fill' | 'custom'; // Intended action hint (optional)
createdAt: number;
updatedAt: number;
}
export interface UpsertMarkerRequest {
id?: string;
url: string;
name: string;
selector: string;
selectorType?: 'css' | 'xpath';
listMode?: boolean;
matchType?: UrlMatchType;
action?: 'click' | 'fill' | 'custom';
}
// Validation actions for MCP-integrated verification
export enum MarkerValidationAction {
Hover = 'hover',
LeftClick = 'left_click',
RightClick = 'right_click',
DoubleClick = 'double_click',
TypeText = 'type_text',
PressKeys = 'press_keys',
Scroll = 'scroll',
}
export interface MarkerValidationRequest {
selector: string;
selectorType?: 'css' | 'xpath';
action: MarkerValidationAction;
// Optional payload for certain actions
text?: string; // for type_text
keys?: string; // for press_keys
// Event options for click-like actions
button?: 'left' | 'right' | 'middle';
bubbles?: boolean;
cancelable?: boolean;
modifiers?: { altKey?: boolean; ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean };
// Targeting options
coordinates?: { x: number; y: number }; // absolute viewport coords
offsetX?: number; // relative to element center if relativeTo = 'element'
offsetY?: number;
relativeTo?: 'element' | 'viewport';
// Navigation options for click-like actions
waitForNavigation?: boolean;
timeoutMs?: number;
// Scroll options
scrollDirection?: 'up' | 'down' | 'left' | 'right';
scrollAmount?: number; // pixels per tick
}
export interface MarkerValidationResponse {
success: boolean;
resolved?: boolean;
ref?: string;
center?: { x: number; y: number };
tool?: { name: string; ok: boolean; error?: string };
error?: string;
}
export interface MarkerQuery {
url?: string; // If present, query by URL match; otherwise list all
}
================================================
FILE: app/chrome-extension/common/message-types.ts
================================================
/**
* Consolidated message type constants for Chrome extension communication
* Note: Native message types are imported from the shared package
*/
import type { RealtimeEvent } from 'chrome-mcp-shared';
// Message targets for routing
export enum MessageTarget {
Offscreen = 'offscreen',
ContentScript = 'content_script',
Background = 'background',
}
// Background script message types
export const BACKGROUND_MESSAGE_TYPES = {
SWITCH_SEMANTIC_MODEL: 'switch_semantic_model',
GET_MODEL_STATUS: 'get_model_status',
UPDATE_MODEL_STATUS: 'update_model_status',
GET_STORAGE_STATS: 'get_storage_stats',
CLEAR_ALL_DATA: 'clear_all_data',
GET_SERVER_STATUS: 'get_server_status',
REFRESH_SERVER_STATUS: 'refresh_server_status',
SERVER_STATUS_CHANGED: 'server_status_changed',
INITIALIZE_SEMANTIC_ENGINE: 'initialize_semantic_engine',
// Record & Replay background control and queries
RR_START_RECORDING: 'rr_start_recording',
RR_STOP_RECORDING: 'rr_stop_recording',
RR_PAUSE_RECORDING: 'rr_pause_recording',
RR_RESUME_RECORDING: 'rr_resume_recording',
RR_GET_RECORDING_STATUS: 'rr_get_recording_status',
RR_LIST_FLOWS: 'rr_list_flows',
RR_FLOWS_CHANGED: 'rr_flows_changed',
RR_GET_FLOW: 'rr_get_flow',
RR_DELETE_FLOW: 'rr_delete_flow',
RR_PUBLISH_FLOW: 'rr_publish_flow',
RR_UNPUBLISH_FLOW: 'rr_unpublish_flow',
RR_RUN_FLOW: 'rr_run_flow',
RR_SAVE_FLOW: 'rr_save_flow',
RR_EXPORT_FLOW: 'rr_export_flow',
RR_EXPORT_ALL: 'rr_export_all',
RR_IMPORT_FLOW: 'rr_import_flow',
RR_LIST_RUNS: 'rr_list_runs',
// Triggers
RR_LIST_TRIGGERS: 'rr_list_triggers',
RR_SAVE_TRIGGER: 'rr_save_trigger',
RR_DELETE_TRIGGER: 'rr_delete_trigger',
RR_REFRESH_TRIGGERS: 'rr_refresh_triggers',
// Scheduling
RR_SCHEDULE_FLOW: 'rr_schedule_flow',
RR_UNSCHEDULE_FLOW: 'rr_unschedule_flow',
RR_LIST_SCHEDULES: 'rr_list_schedules',
// Element marker management
ELEMENT_MARKER_LIST_ALL: 'element_marker_list_all',
ELEMENT_MARKER_LIST_FOR_URL: 'element_marker_list_for_url',
ELEMENT_MARKER_SAVE: 'element_marker_save',
ELEMENT_MARKER_UPDATE: 'element_marker_update',
ELEMENT_MARKER_DELETE: 'element_marker_delete',
ELEMENT_MARKER_VALIDATE: 'element_marker_validate',
ELEMENT_MARKER_START: 'element_marker_start_from_popup',
// Element picker (human-in-the-loop element selection)
ELEMENT_PICKER_UI_EVENT: 'element_picker_ui_event',
ELEMENT_PICKER_FRAME_EVENT: 'element_picker_frame_event',
// Web editor (in-page visual editing)
WEB_EDITOR_TOGGLE: 'web_editor_toggle',
WEB_EDITOR_APPLY: 'web_editor_apply',
WEB_EDITOR_STATUS_QUERY: 'web_editor_status_query',
// Web editor <-> AgentChat integration (Phase 1.1)
WEB_EDITOR_APPLY_BATCH: 'web_editor_apply_batch',
WEB_EDITOR_TX_CHANGED: 'web_editor_tx_changed',
WEB_EDITOR_HIGHLIGHT_ELEMENT: 'web_editor_highlight_element',
// Web editor <-> AgentChat integration (Phase 2 - Revert)
WEB_EDITOR_REVERT_ELEMENT: 'web_editor_revert_element',
// Web editor <-> AgentChat integration - Selection sync
WEB_EDITOR_SELECTION_CHANGED: 'web_editor_selection_changed',
// Web editor <-> AgentChat integration - Clear selection (sidepanel -> web-editor)
WEB_EDITOR_CLEAR_SELECTION: 'web_editor_clear_selection',
// Web editor <-> AgentChat integration - Cancel execution
WEB_EDITOR_CANCEL_EXECUTION: 'web_editor_cancel_execution',
// Web editor props (Phase 7.1.6 early injection)
WEB_EDITOR_PROPS_REGISTER_EARLY_INJECTION: 'web_editor_props_register_early_injection',
// Web editor props - open source file in VSCode
WEB_EDITOR_OPEN_SOURCE: 'web_editor_open_source',
// Quick Panel <-> AgentChat integration
QUICK_PANEL_SEND_TO_AI: 'quick_panel_send_to_ai',
QUICK_PANEL_CANCEL_AI: 'quick_panel_cancel_ai',
// Quick Panel Search - Tabs bridge
QUICK_PANEL_TABS_QUERY: 'quick_panel_tabs_query',
QUICK_PANEL_TAB_ACTIVATE: 'quick_panel_tab_activate',
QUICK_PANEL_TAB_CLOSE: 'quick_panel_tab_close',
} as const;
// Offscreen message types
export const OFFSCREEN_MESSAGE_TYPES = {
SIMILARITY_ENGINE_INIT: 'similarityEngineInit',
SIMILARITY_ENGINE_COMPUTE: 'similarityEngineCompute',
SIMILARITY_ENGINE_BATCH_COMPUTE: 'similarityEngineBatchCompute',
SIMILARITY_ENGINE_STATUS: 'similarityEngineStatus',
// GIF encoding
GIF_ADD_FRAME: 'gifAddFrame',
GIF_FINISH: 'gifFinish',
GIF_RESET: 'gifReset',
} as const;
// Content script message types
export const CONTENT_MESSAGE_TYPES = {
WEB_FETCHER_GET_TEXT_CONTENT: 'webFetcherGetTextContent',
WEB_FETCHER_GET_HTML_CONTENT: 'getHtmlContent',
NETWORK_CAPTURE_PING: 'network_capture_ping',
CLICK_HELPER_PING: 'click_helper_ping',
FILL_HELPER_PING: 'fill_helper_ping',
KEYBOARD_HELPER_PING: 'keyboard_helper_ping',
SCREENSHOT_HELPER_PING: 'screenshot_helper_ping',
INTERACTIVE_ELEMENTS_HELPER_PING: 'interactive_elements_helper_ping',
ACCESSIBILITY_TREE_HELPER_PING: 'chrome_read_page_ping',
WAIT_HELPER_PING: 'wait_helper_ping',
DOM_OBSERVER_PING: 'dom_observer_ping',
} as const;
// Tool action message types (for chrome.runtime.sendMessage)
export const TOOL_MESSAGE_TYPES = {
// Screenshot related
SCREENSHOT_PREPARE_PAGE_FOR_CAPTURE: 'preparePageForCapture',
SCREENSHOT_GET_PAGE_DETAILS: 'getPageDetails',
SCREENSHOT_GET_ELEMENT_DETAILS: 'getElementDetails',
SCREENSHOT_SCROLL_PAGE: 'scrollPage',
SCREENSHOT_RESET_PAGE_AFTER_CAPTURE: 'resetPageAfterCapture',
// Web content fetching
WEB_FETCHER_GET_HTML_CONTENT: 'getHtmlContent',
WEB_FETCHER_GET_TEXT_CONTENT: 'getTextContent',
// User interactions
CLICK_ELEMENT: 'clickElement',
FILL_ELEMENT: 'fillElement',
SIMULATE_KEYBOARD: 'simulateKeyboard',
// Interactive elements
GET_INTERACTIVE_ELEMENTS: 'getInteractiveElements',
// Accessibility tree
GENERATE_ACCESSIBILITY_TREE: 'generateAccessibilityTree',
RESOLVE_REF: 'resolveRef',
ENSURE_REF_FOR_SELECTOR: 'ensureRefForSelector',
VERIFY_FINGERPRINT: 'verifyFingerprint',
DISPATCH_HOVER_FOR_REF: 'dispatchHoverForRef',
// Network requests
NETWORK_SEND_REQUEST: 'sendPureNetworkRequest',
// Wait helper
WAIT_FOR_TEXT: 'waitForText',
// Semantic similarity engine
SIMILARITY_ENGINE_INIT: 'similarityEngineInit',
SIMILARITY_ENGINE_COMPUTE_BATCH: 'similarityEngineComputeBatch',
// Record & Replay content script bridge
RR_RECORDER_CONTROL: 'rr_recorder_control',
RR_RECORDER_EVENT: 'rr_recorder_event',
// Record & Replay timeline feed (background -> content overlay)
RR_TIMELINE_UPDATE: 'rr_timeline_update',
// Quick Panel AI streaming events (background -> content script)
QUICK_PANEL_AI_EVENT: 'quick_panel_ai_event',
// DOM observer trigger bridge
SET_DOM_TRIGGERS: 'set_dom_triggers',
DOM_TRIGGER_FIRED: 'dom_trigger_fired',
// Record & Replay overlay: variable collection
COLLECT_VARIABLES: 'collectVariables',
// Element marker overlay control (content-side)
ELEMENT_MARKER_START: 'element_marker_start',
// Element picker (tool-driven, background <-> content scripts)
ELEMENT_PICKER_START: 'elementPickerStart',
ELEMENT_PICKER_STOP: 'elementPickerStop',
ELEMENT_PICKER_SET_ACTIVE_REQUEST: 'elementPickerSetActiveRequest',
ELEMENT_PICKER_UI_PING: 'elementPickerUiPing',
ELEMENT_PICKER_UI_SHOW: 'elementPickerUiShow',
ELEMENT_PICKER_UI_UPDATE: 'elementPickerUiUpdate',
ELEMENT_PICKER_UI_HIDE: 'elementPickerUiHide',
} as const;
// Type unions for type safety
export type BackgroundMessageType =
(typeof BACKGROUND_MESSAGE_TYPES)[keyof typeof BACKGROUND_MESSAGE_TYPES];
export type OffscreenMessageType =
(typeof OFFSCREEN_MESSAGE_TYPES)[keyof typeof OFFSCREEN_MESSAGE_TYPES];
export type ContentMessageType = (typeof CONTENT_MESSAGE_TYPES)[keyof typeof CONTENT_MESSAGE_TYPES];
export type ToolMessageType = (typeof TOOL_MESSAGE_TYPES)[keyof typeof TOOL_MESSAGE_TYPES];
// Legacy enum for backward compatibility (will be deprecated)
export enum SendMessageType {
// Screenshot related message types
ScreenshotPreparePageForCapture = 'preparePageForCapture',
ScreenshotGetPageDetails = 'getPageDetails',
ScreenshotGetElementDetails = 'getElementDetails',
ScreenshotScrollPage = 'scrollPage',
ScreenshotResetPageAfterCapture = 'resetPageAfterCapture',
// Web content fetching related message types
WebFetcherGetHtmlContent = 'getHtmlContent',
WebFetcherGetTextContent = 'getTextContent',
// Click related message types
ClickElement = 'clickElement',
// Input filling related message types
FillElement = 'fillElement',
// Interactive elements related message types
GetInteractiveElements = 'getInteractiveElements',
// Network request capture related message types
NetworkSendRequest = 'sendPureNetworkRequest',
// Keyboard event related message types
SimulateKeyboard = 'simulateKeyboard',
// Semantic similarity engine related message types
SimilarityEngineInit = 'similarityEngineInit',
SimilarityEngineComputeBatch = 'similarityEngineComputeBatch',
}
// ============================================================
// Quick Panel <-> AgentChat Message Contracts
// ============================================================
/**
* Context information that can be attached to a Quick Panel AI request.
* Allows passing page-specific data to enhance the AI's understanding.
*/
export interface QuickPanelAIContext {
/** Current page URL */
pageUrl?: string;
/** User's text selection on the page */
selectedText?: string;
/**
* Optional element metadata from the page.
* Kept as unknown to avoid tight coupling with specific element types.
*/
elementInfo?: unknown;
}
/**
* Payload for sending a message to AI via Quick Panel.
*/
export interface QuickPanelSendToAIPayload {
/** The user's instruction/question for the AI */
instruction: string;
/** Optional contextual information from the page */
context?: QuickPanelAIContext;
}
/**
* Response from QUICK_PANEL_SEND_TO_AI message handler.
*/
export type QuickPanelSendToAIResponse =
| { success: true; requestId: string; sessionId: string }
| { success: false; error: string };
/**
* Message structure for sending to AI.
*/
export interface QuickPanelSendToAIMessage {
type: typeof BACKGROUND_MESSAGE_TYPES.QUICK_PANEL_SEND_TO_AI;
payload: QuickPanelSendToAIPayload;
}
/**
* Payload for cancelling an active AI request.
*/
export interface QuickPanelCancelAIPayload {
/** The request ID to cancel */
requestId: string;
/**
* Optional session ID for fallback when background state is missing.
* This can happen after MV3 Service Worker restarts.
*/
sessionId?: string;
}
/**
* Response from QUICK_PANEL_CANCEL_AI message handler.
*/
export type QuickPanelCancelAIResponse = { success: true } | { success: false; error: string };
/**
* Message structure for cancelling AI request.
*/
export interface QuickPanelCancelAIMessage {
type: typeof BACKGROUND_MESSAGE_TYPES.QUICK_PANEL_CANCEL_AI;
payload: QuickPanelCancelAIPayload;
}
/**
* Message pushed from background to content script with AI streaming events.
* Uses the same RealtimeEvent type as AgentChat for consistency.
*/
export interface QuickPanelAIEventMessage {
action: typeof TOOL_MESSAGE_TYPES.QUICK_PANEL_AI_EVENT;
requestId: string;
sessionId: string;
event: RealtimeEvent;
}
// ============================================================
// Quick Panel Search - Tabs Bridge Contracts
// ============================================================
/**
* Payload for querying open tabs.
*/
export interface QuickPanelTabsQueryPayload {
/**
* When true (default), query tabs across all windows.
* When false, restrict results to the sender's window.
*/
includeAllWindows?: boolean;
}
/**
* Summary of a single tab returned from the background.
*/
export interface QuickPanelTabSummary {
tabId: number;
windowId: number;
title: string;
url: string;
favIconUrl?: string;
active: boolean;
pinned: boolean;
audible: boolean;
muted: boolean;
index: number;
lastAccessed?: number;
}
/**
* Response from QUICK_PANEL_TABS_QUERY message handler.
*/
export type QuickPanelTabsQueryResponse =
| {
success: true;
tabs: QuickPanelTabSummary[];
currentTabId: number | null;
currentWindowId: number | null;
}
| { success: false; error: string };
/**
* Message structure for querying tabs.
*/
export interface QuickPanelTabsQueryMessage {
type: typeof BACKGROUND_MESSAGE_TYPES.QUICK_PANEL_TABS_QUERY;
payload?: QuickPanelTabsQueryPayload;
}
/**
* Payload for activating a tab.
*/
export interface QuickPanelActivateTabPayload {
tabId: number;
windowId?: number;
}
/**
* Response from QUICK_PANEL_TAB_ACTIVATE message handler.
*/
export type QuickPanelActivateTabResponse = { success: true } | { success: false; error: string };
/**
* Message structure for activating a tab.
*/
export interface QuickPanelActivateTabMessage {
type: typeof BACKGROUND_MESSAGE_TYPES.QUICK_PANEL_TAB_ACTIVATE;
payload: QuickPanelActivateTabPayload;
}
/**
* Payload for closing a tab.
*/
export interface QuickPanelCloseTabPayload {
tabId: number;
}
/**
* Response from QUICK_PANEL_TAB_CLOSE message handler.
*/
export type QuickPanelCloseTabResponse = { success: true } | { success: false; error: string };
/**
* Message structure for closing a tab.
*/
export interface QuickPanelCloseTabMessage {
type: typeof BACKGROUND_MESSAGE_TYPES.QUICK_PANEL_TAB_CLOSE;
payload: QuickPanelCloseTabPayload;
}
================================================
FILE: app/chrome-extension/common/node-types.ts
================================================
// node-types.ts — centralized node type constants for Builder/UI layer
// Combines all executable Step types with UI-only nodes (e.g., trigger, delay)
import { STEP_TYPES } from './step-types';
export const NODE_TYPES = {
// Executable step types (spread from STEP_TYPES)
...STEP_TYPES,
// UI-only nodes
TRIGGER: 'trigger',
DELAY: 'delay',
} as const;
export type NodeTypeConst = (typeof NODE_TYPES)[keyof typeof NODE_TYPES];
================================================
FILE: app/chrome-extension/common/rr-v3-keepalive-protocol.ts
================================================
/**
* @fileoverview RR V3 Keepalive Protocol Constants
* @description Shared protocol constants for Background-Offscreen keepalive communication
*/
/** Keepalive Port 名称 */
export const RR_V3_KEEPALIVE_PORT_NAME = 'rr_v3_keepalive' as const;
/** Keepalive 消息类型 */
export type KeepaliveMessageType =
| 'keepalive.ping'
| 'keepalive.pong'
| 'keepalive.start'
| 'keepalive.stop';
/** Keepalive 消息 */
export interface KeepaliveMessage {
type: KeepaliveMessageType;
timestamp: number;
}
/** 默认心跳间隔(毫秒) - Offscreen 每隔这个间隔发送 ping */
export const DEFAULT_KEEPALIVE_PING_INTERVAL_MS = 20_000;
/** 最大心跳间隔(毫秒)- Chrome MV3 SW 约 30s 空闲后终止 */
export const MAX_KEEPALIVE_PING_INTERVAL_MS = 25_000;
================================================
FILE: app/chrome-extension/common/step-types.ts
================================================
// step-types.ts — re-export shared constants to keep single source of truth
export { STEP_TYPES } from 'chrome-mcp-shared';
export type StepTypeConst =
(typeof import('chrome-mcp-shared'))['STEP_TYPES'][keyof (typeof import('chrome-mcp-shared'))['STEP_TYPES']];
================================================
FILE: app/chrome-extension/common/tool-handler.ts
================================================
import type { CallToolResult, TextContent, ImageContent } from '@modelcontextprotocol/sdk/types.js';
export interface ToolResult extends CallToolResult {
content: (TextContent | ImageContent)[];
isError: boolean;
}
export interface ToolExecutor {
execute(args: any): Promise<ToolResult>;
}
export const createErrorResponse = (
message: string = 'Unknown error, please try again',
): ToolResult => {
return {
content: [
{
type: 'text',
text: message,
},
],
isError: true,
};
};
================================================
FILE: app/chrome-extension/common/web-editor-types.ts
================================================
/**
* Web Editor V2 - Shared Type Definitions
*
* This module defines types shared between:
* - Background script (injection control)
* - Inject script (web-editor-v2.ts)
* - Future: UI panels
*/
// =============================================================================
// Editor State
// =============================================================================
/** Current state of the web editor */
export interface WebEditorState {
/** Whether the editor is currently active */
active: boolean;
/** Editor version for compatibility checks */
version: 2;
}
// =============================================================================
// Message Protocol (Background <-> Inject Script)
// =============================================================================
/**
* Action types for web editor V2 messages
*
* IMPORTANT: V2 uses versioned action names (suffix _v2) to avoid
* conflicts with V1 when both scripts might be injected in the same tab.
* This prevents double-response race conditions.
*
* V1 uses: web_editor_ping, web_editor_toggle, etc.
* V2 uses: web_editor_ping_v2, web_editor_toggle_v2, etc.
*/
export const WEB_EDITOR_V2_ACTIONS = {
/** Check if V2 editor is injected and get status */
PING: 'web_editor_ping_v2',
/** Toggle V2 editor on/off */
TOGGLE: 'web_editor_toggle_v2',
/** Start V2 editor */
START: 'web_editor_start_v2',
/** Stop V2 editor */
STOP: 'web_editor_stop_v2',
/** Highlight an element (from sidepanel hover) */
HIGHLIGHT_ELEMENT: 'web_editor_highlight_element_v2',
/** Revert an element to its original state (Phase 2 - Selective Undo) */
REVERT_ELEMENT: 'web_editor_revert_element_v2',
/** Clear selection (from sidepanel after send) */
CLEAR_SELECTION: 'web_editor_clear_selection_v2',
} as const;
/**
* Legacy V1 action types (for reference and background compatibility)
* These are used when USE_WEB_EDITOR_V2 is false
*/
export const WEB_EDITOR_V1_ACTIONS = {
PING: 'web_editor_ping',
TOGGLE: 'web_editor_toggle',
START: 'web_editor_start',
STOP: 'web_editor_stop',
APPLY: 'web_editor_apply',
} as const;
export type WebEditorV2Action = (typeof WEB_EDITOR_V2_ACTIONS)[keyof typeof WEB_EDITOR_V2_ACTIONS];
export type WebEditorV1Action = (typeof WEB_EDITOR_V1_ACTIONS)[keyof typeof WEB_EDITOR_V1_ACTIONS];
/** Editor version literal type */
export type WebEditorVersion = 1 | 2;
/** Ping request (V2) */
export interface WebEditorV2PingRequest {
action: typeof WEB_EDITOR_V2_ACTIONS.PING;
}
/** Ping response (V2) */
export interface WebEditorV2PingResponse {
status: 'pong';
active: boolean;
version: 2;
}
/** Toggle request (V2) */
export interface WebEditorV2ToggleRequest {
action: typeof WEB_EDITOR_V2_ACTIONS.TOGGLE;
}
/** Toggle response (V2) */
export interface WebEditorV2ToggleResponse {
active: boolean;
}
/** Start request (V2) */
export interface WebEditorV2StartRequest {
action: typeof WEB_EDITOR_V2_ACTIONS.START;
}
/** Start response (V2) */
export interface WebEditorV2StartResponse {
active: boolean;
}
/** Stop request (V2) */
export interface WebEditorV2StopRequest {
action: typeof WEB_EDITOR_V2_ACTIONS.STOP;
}
/** Stop response (V2) */
export interface WebEditorV2StopResponse {
active: boolean;
}
/** Union types for V2 type-safe message handling */
export type WebEditorV2Request =
| WebEditorV2PingRequest
| WebEditorV2ToggleRequest
| WebEditorV2StartRequest
| WebEditorV2StopRequest;
export type WebEditorV2Response =
| WebEditorV2PingResponse
| WebEditorV2ToggleResponse
| WebEditorV2StartResponse
| WebEditorV2StopResponse;
// =============================================================================
// Element Locator (Phase 1 - Basic Structure)
// =============================================================================
/**
* Framework debug source information
* Extracted from React Fiber or Vue component instance
*/
export interface DebugSource {
/** Source file path */
file: string;
/** Line number (1-based) */
line?: number;
/** Column number (1-based) */
column?: number;
/** Component name (if available) */
componentName?: string;
}
/**
* Element Locator - Primary key for element identification
*
* Uses multiple strategies to locate elements, supporting:
* - HMR/DOM changes recovery
* - Cross-session persistence
* - Framework-agnostic identification
*/
export interface ElementLocator {
/** CSS selector candidates (ordered by specificity) */
selectors: string[];
/** Structural fingerprint for similarity matching */
fingerprint: string;
/** Framework debug information (React/Vue) */
debugSource?: DebugSource;
/** DOM tree path (child indices from root) */
path: number[];
/** iframe selector chain (from top to target frame) - Phase 4 */
frameChain?: string[];
/** Shadow DOM host selector chain - Phase 2 */
shadowHostChain?: string[];
}
// =============================================================================
// Transaction System (Phase 1 - Basic Structure, Low Priority)
// =============================================================================
/** Transaction operation types */
export type TransactionType = 'style' | 'text' | 'class' | 'move' | 'structure';
/**
* Transaction snapshot for undo/redo
* Captures element state before/after changes
*/
export interface TransactionSnapshot {
/** Element locator for re-identification */
locator: ElementLocator;
/** innerHTML snapshot (for structure changes) */
html?: string;
/** Changed style properties */
styles?: Record<string, string>;
/** Class list tokens (from `class` attribute) */
classes?: string[];
/** Text content */
text?: string;
}
/**
* Move position data
* Captures a concrete insertion point under a parent element
*/
export interface MoveOperationData {
/** Target parent element locator */
parentLocator: ElementLocator;
/** Insert position index (among element children) */
insertIndex: number;
/** Anchor sibling element locator (for stable positioning) */
anchorLocator?: ElementLocator;
/** Position relative to anchor */
anchorPosition: 'before' | 'after';
}
/**
* Move transaction data
* Captures both source and destination for undo/redo
*/
export interface MoveTransactionData {
/** Original location before move */
from: MoveOperationData;
/** Target location after move */
to: MoveOperationData;
}
/**
* Structure operation data
* For wrap/unwrap/delete/duplicate operations (Phase 5.5)
*/
export interface StructureOperationData {
/** Structure action type */
action: 'wrap' | 'unwrap' | 'delete' | 'duplicate';
/** Wrapper tag for wrap/unwrap actions */
wrapperTag?: string;
/** Wrapper inline styles for wrap/unwrap actions */
wrapperStyles?: Record<string, string>;
/**
* Deterministic insertion position for undo/redo.
* Required for delete (restore) and duplicate (re-create).
*/
position?: MoveOperationData;
/**
* Serialized element HTML for undo/redo.
* Must be a single-root element outerHTML string.
* Used by delete (restore original) and duplicate (re-create clone).
*/
html?: string;
}
/**
* Transaction record for undo/redo system
*/
export interface Transaction {
/** Unique transaction ID */
id: string;
/** Operation type */
type: TransactionType;
/** Target element locator */
targetLocator: ElementLocator;
/**
* Stable element identifier for cross-transaction grouping.
* Used by AgentChat integration for element chips aggregation.
* Optional for backward compatibility with existing transactions.
*/
elementKey?: string;
/** State before change */
before: TransactionSnapshot;
/** State after change */
after: TransactionSnapshot;
/** Move-specific data */
moveData?: MoveTransactionData;
/** Structure-specific data */
structureData?: StructureOperationData;
/** Timestamp */
timestamp: number;
/** Whether merged with previous transaction */
merged: boolean;
}
// =============================================================================
// AgentChat Integration Types (Phase 1.1)
// =============================================================================
/** Stable element identifier for aggregating transactions across UI contexts */
export type WebEditorElementKey = string;
/**
* Net effect payload for a single element aggregated from the undo stack.
* Designed to be directly consumable by prompt builders.
*/
export interface NetEffectPayload {
/** Stable element key */
elementKey: WebEditorElementKey;
/** Locator snapshot for element re-identification */
locator: ElementLocator;
/**
* Aggregated style changes (first before -> last after).
* Contains ONLY the affected properties, not a full style snapshot.
* Empty string value means the property was removed/unset.
*/
styleChanges?: {
before: Record<string, string>;
after: Record<string, string>;
};
/** Aggregated text change (first before -> last after) */
textChange?: {
before: string;
after: string;
};
/** Aggregated class changes (first before -> last after) */
classChanges?: {
before: string[];
after: string[];
};
}
/** High-level change category for UI display */
export type ElementChangeType = 'style' | 'text' | 'class' | 'mixed';
/**
* Element change summary for Chips rendering in AgentChat.
* Aggregates multiple transactions for the same element.
*/
export interface ElementChangeSummary {
/** Stable element identifier */
elementKey: WebEditorElementKey;
/** Short label for Chips display (e.g., "button#submit") */
label: string;
/** Full label for tooltips with more context */
fullLabel: string;
/** Locator snapshot for highlighting and element recovery */
locator: ElementLocator;
/** High-level change category */
type: ElementChangeType;
/** Detailed change statistics for UI tooltips */
changes: {
style?: {
/** Number of new style properties added */
added: number;
/** Number of style properties removed */
removed: number;
/** Number of style properties modified */
modified: number;
/** List of affected style property names */
details: string[];
};
text?: {
/** Truncated preview of original text */
beforePreview: string;
/** Truncated preview of new text */
afterPreview: string;
};
class?: {
/** Classes added */
added: string[];
/** Classes removed */
removed: string[];
};
};
/** Contributing transaction IDs in chronological order */
transactionIds: string[];
/** Net effect payload for batch Apply */
netEffect: NetEffectPayload;
/** Timestamp of the most recent transaction */
updatedAt: number;
/** Debug source information if available */
debugSource?: DebugSource;
}
/** Action types for TX change events */
export type WebEditorTxChangeAction = 'push' | 'merge' | 'undo' | 'redo' | 'clear' | 'rollback';
/**
* TX change broadcast payload sent to Sidepanel/AgentChat.
* Emitted when the undo stack changes (push, undo, redo, clear).
*/
export interface WebEditorTxChangedPayload {
/** Source tab ID for multi-tab isolation */
tabId: number;
/** Action that triggered this change (for UI animations/incremental updates) */
action: WebEditorTxChangeAction;
/** Aggregated element-level summaries from the current undo stack */
elements: ElementChangeSummary[];
/** Current undo stack size */
undoCount: number;
/** Current redo stack size */
redoCount: number;
/** Whether there are applicable changes (style/text/class) */
hasApplicableChanges: boolean;
/** Page URL for context */
pageUrl?: string;
}
/**
* Batch Apply payload sent from web-editor to background.
*/
export interface WebEditorApplyBatchPayload {
/** Source tab ID */
tabId: number;
/** Element changes to apply */
elements: ElementChangeSummary[];
/** Element keys excluded by user */
excludedKeys: WebEditorElementKey[];
/** Page URL for context */
pageUrl?: string;
}
/**
* Highlight element request sent from AgentChat to the active tab.
*/
export interface WebEditorHighlightElementPayload {
/** Target tab ID */
tabId: number;
/** Element key to highlight */
elementKey: WebEditorElementKey;
/** Locator for element identification */
locator: ElementLocator;
/** Highlight mode: 'hover' to show, 'clear' to hide */
mode: 'hover' | 'clear';
}
/**
* Revert element request sent from AgentChat to the active tab.
* Used for Phase 2 - Selective Undo (reverting individual element changes).
*/
export interface WebEditorRevertElementPayload {
/** Target tab ID */
tabId: number;
/** Element key to revert */
elementKey: WebEditorElementKey;
}
/**
* Revert element response from content script.
*/
export interface WebEditorRevertElementResponse {
/** Whether the revert was successful */
success: boolean;
/** What was reverted (for UI feedback) */
reverted?: {
style?: boolean;
text?: boolean;
class?: boolean;
};
/** Error message if revert failed */
error?: string;
}
// =============================================================================
// Selection Sync Types
// =============================================================================
/**
* Summary of currently selected element.
* Lightweight payload for selection sync (no transaction data).
*/
export interface SelectedElementSummary {
/** Stable element identifier */
elementKey: WebEditorElementKey;
/** Locator for element identification and highlighting */
locator: ElementLocator;
/** Short display label (e.g., "div#app") */
label: string;
/** Full label with context (e.g., "body > div#app") */
fullLabel: string;
/** Tag name of the element */
tagName: string;
/** Timestamp for deduplication */
updatedAt: number;
}
/**
* Selection change broadcast payload.
* Sent immediately when user selects/deselects elements (no debounce).
*/
export interface WebEditorSelectionChangedPayload {
/** Source tab ID (filled by background from sender.tab.id) */
tabId: number;
/** Currently selected element, or null if deselected */
selected: SelectedElementSummary | null;
/** Page URL for context */
pageUrl?: string;
}
// =============================================================================
// Execution Cancel Types
// =============================================================================
/**
* Payload for canceling an ongoing Apply execution.
* Sent from web-editor toolbar or sidepanel to background.
*/
export interface WebEditorCancelExecutionPayload {
/** Session ID of the execution to cancel */
sessionId: string;
/** Request ID of the execution to cancel */
requestId: string;
}
/**
* Response from cancel execution request.
*/
export interface WebEditorCancelExecutionResponse {
/** Whether the cancel request was successful */
success: boolean;
/** Error message if cancellation failed */
error?: string;
}
// =============================================================================
// Public API Interface
// =============================================================================
/**
* Web Editor V2 Public API
* Exposed on window.__MCP_WEB_EDITOR_V2__
*/
export interface WebEditorV2Api {
/** Start the editor */
start: () => void;
/** Stop the editor */
stop: () => void;
/** Toggle editor on/off, returns new state */
toggle: () => boolean;
/** Get current state */
getState: () => WebEditorState;
/**
* Revert a specific element to its original state (Phase 2 - Selective Undo).
* Creates a compensating transaction that can be undone.
*/
revertElement: (elementKey: WebEditorElementKey) => Promise<WebEditorRevertElementResponse>;
/**
* Clear current selection (called from sidepanel after send).
* Triggers deselect and broadcasts null selection.
*/
clearSelection: () => void;
}
// =============================================================================
// Global Declaration
// =============================================================================
declare global {
interface Window {
__MCP_WEB_EDITOR_V2__?: WebEditorV2Api;
}
}
================================================
FILE: app/chrome-extension/entrypoints/background/element-marker/element-marker-storage.ts
================================================
// IndexedDB storage for element markers (URL -> marked selectors)
// Uses the shared IndexedDbClient for robust transaction handling.
import { IndexedDbClient } from '@/utils/indexeddb-client';
import type { ElementMarker, UpsertMarkerRequest } from '@/common/element-marker-types';
const DB_NAME = 'element_marker_storage';
const DB_VERSION = 1;
const STORE = 'markers';
const idb = new IndexedDbClient(DB_NAME, DB_VERSION, (db, oldVersion) => {
switch (oldVersion) {
case 0: {
const store = db.createObjectStore(STORE, { keyPath: 'id' });
// Useful indexes for lookups
store.createIndex('by_host', 'host', { unique: false });
store.createIndex('by_origin', 'origin', { unique: false });
store.createIndex('by_path', 'path', { unique: false });
}
}
});
function normalizeUrl(raw: string): { url: string; origin: string; host: string; path: string } {
try {
const u = new URL(raw);
return { url: raw, origin: u.origin, host: u.hostname, path: u.pathname };
} catch {
return { url: raw, origin: '', host: '', path: '' };
}
}
function now(): number {
return Date.now();
}
export async function listAllMarkers(): Promise<ElementMarker[]> {
return idb.getAll<ElementMarker>(STORE);
}
export async function listMarkersForUrl(url: string): Promise<ElementMarker[]> {
const { origin, path, host } = normalizeUrl(url);
const all = await idb.getAll<ElementMarker>(STORE);
// Simple matching policy:
// - exact: origin + path must match exactly
// - prefix: origin matches and marker.path is a prefix of current path
// - host: host matches regardless of path
return all.filter((m) => {
if (!m) return false;
if (m.matchType === 'exact') return m.origin === origin && m.path === path;
if (m.matchType === 'host') return !!m.host && m.host === host;
// default 'prefix'
return m.origin === origin && (m.path ? path.startsWith(m.path) : true);
});
}
export async function saveMarker(req: UpsertMarkerRequest): Promise<ElementMarker> {
const { url: rawUrl, selector } = req;
if (!rawUrl || !selector) throw new Error('url and selector are required');
const { url, origin, host, path } = normalizeUrl(rawUrl);
const ts = now();
const marker: ElementMarker = {
id: req.id || (globalThis.crypto?.randomUUID?.() ?? `${ts}_${Math.random()}`),
url,
origin,
host,
path,
matchType: req.matchType || 'prefix',
name: req.name || selector,
selector,
selectorType: req.selectorType || 'css',
listMode: req.listMode || false,
action: req.action || 'custom',
createdAt: ts,
updatedAt: ts,
};
await idb.put<ElementMarker>(STORE, marker);
return marker;
}
export async function updateMarker(marker: ElementMarker): Promise<void> {
const existing = await idb.get<ElementMarker>(STORE, marker.id);
if (!existing) throw new Error('marker not found');
// Preserve createdAt from existing record, only update updatedAt
const updated: ElementMarker = {
...marker,
createdAt: existing.createdAt, // Never overwrite createdAt
updatedAt: now(),
};
await idb.put<ElementMarker>(STORE, updated);
}
export async function deleteMarker(id: string): Promise<void> {
await idb.delete(STORE, id);
}
================================================
FILE: app/chrome-extension/entrypoints/background/element-marker/index.ts
================================================
import { BACKGROUND_MESSAGE_TYPES } from '@/common/message-types';
import type {
UpsertMarkerRequest,
ElementMarker,
MarkerValidationRequest,
MarkerValidationAction,
} from '@/common/element-marker-types';
import {
deleteMarker,
listAllMarkers,
listMarkersForUrl,
saveMarker,
updateMarker,
} from './element-marker-storage';
import { computerTool } from '@/entrypoints/background/tools/browser/computer';
import { clickTool } from '@/entrypoints/background/tools/browser/interaction';
import { keyboardTool } from '@/entrypoints/background/tools/browser/keyboard';
const CONTEXT_MENU_ID = 'element_marker_mark';
/**
* Extract error message from MCP tool result
*/
function extractToolError(result: any): string | undefined {
if (!result) return undefined;
// Check for error in result content array
if (Array.isArray(result.content)) {
for (const item of result.content) {
if (item?.text) {
try {
const parsed = JSON.parse(item.text);
if (parsed?.error) return parsed.error;
if (parsed?.message) return parsed.message;
} catch {
// Not JSON, use as-is
return item.text;
}
}
}
}
// Fallback to direct error field
return result.error || (result.isError ? 'unknown tool error' : undefined);
}
async function ensureContextMenu() {
try {
// Guard: contextMenus permission may be missing
if (!(chrome as any).contextMenus?.create) return;
// Remove and re-create our single menu to avoid duplication
try {
await chrome.contextMenus.remove(CONTEXT_MENU_ID);
} catch {}
await chrome.contextMenus.create({
id: CONTEXT_MENU_ID,
title: '标注元素',
contexts: ['all'],
});
} catch (e) {
console.warn('ElementMarker: ensureContextMenu failed:', e);
}
}
/**
* Check if element-marker.js is already injected in the tab
* Uses a short timeout to avoid hanging on unresponsive tabs
*/
async function isMarkerInjected(tabId: number): Promise<boolean> {
try {
const response = await Promise.race([
chrome.tabs.sendMessage(tabId, { action: 'element_marker_ping' }),
new Promise<null>((resolve) => setTimeout(() => resolve(null), 300)),
]);
return response?.status === 'pong';
} catch {
return false;
}
}
/**
* Inject element-marker.js into the tab if not already injected
*/
async function injectMarkerHelper(tabId: number) {
// Check if already injected via ping
const alreadyInjected = await isMarkerInjected(tabId);
if (!alreadyInjected) {
try {
await chrome.scripting.executeScript({
target: { tabId, allFrames: true },
files: ['inject-scripts/element-marker.js'],
world: 'ISOLATED',
} as any);
} catch (e) {
// Script injection may fail on some pages (e.g., chrome:// URLs)
console.warn('ElementMarker: script injection failed:', e);
}
}
try {
await chrome.tabs.sendMessage(tabId, { action: 'element_marker_start' } as any);
} catch (e) {
console.warn('ElementMarker: start overlay failed:', e);
}
}
export function initElementMarkerListeners() {
// Ensure context menu on startup
ensureContextMenu().catch(() => {});
// Respond to RR triggers refresh by re-ensuring our menu a bit later
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
try {
switch (message?.type) {
// Handle element marker start from popup
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_START: {
const tabId = message.tabId;
if (typeof tabId !== 'number') {
sendResponse({ success: false, error: 'invalid tabId' });
return true;
}
injectMarkerHelper(tabId)
.then(() => sendResponse({ success: true }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_LIST_ALL: {
listAllMarkers()
.then((markers) => sendResponse({ success: true, markers }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_LIST_FOR_URL: {
const url = String(message.url || '');
listMarkersForUrl(url)
.then((markers) => sendResponse({ success: true, markers }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_SAVE: {
const req = message.marker as UpsertMarkerRequest;
saveMarker(req)
.then((marker) => sendResponse({ success: true, marker }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_UPDATE: {
const marker = message.marker as ElementMarker;
updateMarker(marker)
.then(() => sendResponse({ success: true }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_DELETE: {
const id = String(message.id || '');
if (!id) {
sendResponse({ success: false, error: 'invalid id' });
return true;
}
deleteMarker(id)
.then(() => sendResponse({ success: true }))
.catch((e) => sendResponse({ success: false, error: e?.message || String(e) }));
return true;
}
case BACKGROUND_MESSAGE_TYPES.ELEMENT_MARKER_VALIDATE: {
// Validate via MCP tool chain
(async () => {
const req = message as {
selector: string;
selectorType?: 'css' | 'xpath';
action: MarkerValidationAction;
listMode?: boolean;
text?: string;
keys?: string;
button?: 'left' | 'right' | 'middle';
bubbles?: boolean;
cancelable?: boolean;
modifiers?: any;
coordinates?: { x: number; y: number };
offsetX?: number;
offsetY?: number;
relativeTo?: 'element' | 'viewport';
};
// enrich typing with optional nav + scroll params
(req as any).waitForNavigation = (message as any).waitForNavigation;
(req as any).timeoutMs = (message as any).timeoutMs;
(req as any).scrollDirection = (message as any).scrollDirection;
(req as any).scrollAmount = (message as any).scrollAmount;
const selector = String(req.selector || '').trim();
const selectorType = (req.selectorType || 'css') as 'css' | 'xpath';
const action = req.action as MarkerValidationAction;
if (!selector) return sendResponse({ success: false, error: 'selector is required' });
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
const tab = tabs[0];
if (!tab?.id) return sendResponse({ success: false, error: 'active tab not found' });
// 1) Ensure helper
try {
await chrome.scripting.executeScript({
target: { tabId: tab.id, allFrames: true },
files: ['inject-scripts/accessibility-tree-helper.js'],
world: 'ISOLATED',
} as any);
} catch {}
// 2) Resolve selector -> ref/center via helper (same as tools)
let ensured: any;
try {
ensured = await chrome.tabs.sendMessage(tab.id, {
action: 'ensureRefForSelector',
selector,
isXPath: selectorType === 'xpath',
allowMultiple: !!req.listMode,
} as any);
} catch (e) {
return sendResponse({
success: false,
error: String(e instanceof Error ? e.message : e),
});
}
if (!ensured || !ensured.success || !ensured.ref) {
return sendResponse({
success: false,
error: ensured?.error || 'failed to resolve selector',
});
}
const base = {
success: true,
resolved: true,
ref: ensured.ref,
center: ensured.center,
} as any;
// Compute optional coordinates from offsets
let coords: { x: number; y: number } | undefined = undefined;
if (
req.coordinates &&
typeof req.coordinates.x === 'number' &&
typeof req.coordinates.y === 'number'
) {
coords = { x: Math.round(req.coordinates.x), y: Math.round(req.coordinates.y) };
} else if (
req.relativeTo === 'element' &&
ensured.center &&
(typeof req.offsetX === 'number' || typeof req.offsetY === 'number')
) {
const dx = Number.isFinite(req.offsetX as any) ? (req.offsetX as number) : 0;
const dy = Number.isFinite(req.offsetY as any) ? (req.offsetY as number) : 0;
coords = { x: ensured.center.x + dx, y: ensured.center.y + dy };
}
// 3) Dispatch to appropriate tool for end-to-end validation
try {
switch (action) {
case 'hover': {
const r = await computerTool.execute(
coords
? { action: 'hover', coordinates: coords }
: ({ action: 'hover', ref: ensured.ref } as any),
);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'computer.hover', ok: !r.isError, error };
break;
}
case 'left_click': {
const r = await clickTool.execute({
...(coords ? { coordinates: coords } : { ref: ensured.ref }),
waitForNavigation: !!req.waitForNavigation,
timeout: Number.isFinite(req.timeoutMs as any)
? (req.timeoutMs as number)
: 3000,
button: (req.button || 'left') as any,
modifiers: req.modifiers || {},
} as any);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'interaction.click', ok: !r.isError, error };
break;
}
case 'double_click': {
const r = await clickTool.execute({
...(coords ? { coordinates: coords } : { ref: ensured.ref }),
double: true,
waitForNavigation: !!req.waitForNavigation,
timeout: Number.isFinite(req.timeoutMs as any)
? (req.timeoutMs as number)
: 3000,
button: (req.button || 'left') as any,
modifiers: req.modifiers || {},
} as any);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'interaction.click(double)', ok: !r.isError, error };
break;
}
case 'right_click': {
const r = await clickTool.execute({
...(coords ? { coordinates: coords } : { ref: ensured.ref }),
waitForNavigation: !!req.waitForNavigation,
timeout: Number.isFinite(req.timeoutMs as any)
? (req.timeoutMs as number)
: 3000,
button: 'right',
modifiers: req.modifiers || {},
} as any);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'interaction.click(right)', ok: !r.isError, error };
break;
}
case 'scroll': {
const direction = (req as any).scrollDirection || 'down';
const amount = Number.isFinite((req as any).scrollAmount)
? Number((req as any).scrollAmount)
: 300;
const payload = coords
? {
action: 'scroll',
scrollDirection: direction,
scrollAmount: amount,
coordinates: coords,
}
: ({
action: 'scroll',
scrollDirection: direction,
scrollAmount: amount,
ref: ensured.ref,
} as any);
const r = await computerTool.execute(payload as any);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'computer.scroll', ok: !r.isError, error };
break;
}
case 'type_text': {
const text = String(req.text || '');
const r = await computerTool.execute({ action: 'type', ref: ensured.ref, text });
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'computer.type', ok: !r.isError, error };
break;
}
case 'press_keys': {
const keys = String(req.keys || '');
// Focus first by ref to ensure key target
try {
await clickTool.execute({
ref: ensured.ref,
waitForNavigation: false,
timeout: 2000,
});
} catch {}
const r = await keyboardTool.execute({ keys, delay: 0 } as any);
const error = r.isError ? extractToolError(r) : undefined;
base.tool = { name: 'keyboard.simulate', ok: !r.isError, error };
break;
}
default: {
base.tool = { name: 'noop', ok: true };
}
}
} catch (e) {
console.warn('[ElementMarker] Validation failed before tool execution', e);
base.tool = {
name: 'unknown',
ok: false,
error: String(e instanceof Error ? e.message : e),
};
}
// Log tool failures for debugging
if (base.tool && base.tool.ok === false) {
console.warn('[ElementMarker] Tool validation failure', {
action,
toolName: base.tool.name,
error: base.tool.error,
selector,
selectorType,
});
}
return sendResponse(base);
})();
return true;
}
// When RR refresh (or similar) happens, re-add our menu
case BACKGROUND_MESSAGE_TYPES.RR_REFRESH_TRIGGERS:
case BACKGROUND_MESSAGE_TYPES.RR_SAVE_TRIGGER:
case BACKGROUND_MESSAGE_TYPES.RR_DELETE_TRIGGER: {
setTimeout(() => ensureContextMenu().catch(() => {}), 300);
break;
}
}
} catch (e) {
sendResponse({ success: false, error: (e as any)?.message || String(e) });
}
return false;
});
// Context menu click routing
if ((chrome as any).contextMenus?.onClicked?.addListener) {
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
try {
if (info.menuItemId === CONTEXT_MENU_ID && tab?.id) {
await injectMarkerHelper(tab.id);
}
} catch (e) {
console.warn('ElementMarker: context menu click failed:', e);
}
});
}
}
================================================
FILE: app/chrome-extension/entrypoints/background/index.ts
================================================
import { initNativeHostListener } from './native-host';
import {
initSemanticSimilarityListener,
initializeSemanticEngineIfCached,
} from './semantic-similarity';
import { initStorageManagerListener } from './storage-manager';
import { cleanupModelCache } from '@/utils/semantic-similarity-engine';
import { initRecordReplayListeners } from './record-replay';
import { initElementMarkerListeners } from './element-marker';
import { initWebEditorListeners } from './web-editor';
import { initQuickPanelAgentHandler } from './quick-panel/agent-handler';
import { initQuickPanelCommands } from './quick-panel/commands';
import { initQuickPanelTabsHandler } from './quick-panel/tabs-handler';
// Record-Replay V3 (feature flag)
import { bootstrapV3 } from './record-replay-v3/bootstrap';
/**
* Feature flag for RR-V3
* Set to true to enable the new Record-Replay V3 engine
*/
const ENABLE_RR_V3 = true;
/**
* Background script entry point
* Initializes all background services and listeners
*/
export default defineBackground(() => {
// Open welcome page on first install
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
// Open the welcome/onboarding page for new installations
chrome.tabs.create({
url: chrome.runtime.getURL('/welcome.html'),
});
}
});
// Initialize core services
initNativeHostListener();
initSemanticSimilarityListener();
initStorageManagerListener();
// Record & Replay V1/V2 listeners
initRecordReplayListeners();
// Record & Replay V3 (new engine)
if (ENABLE_RR_V3) {
bootstrapV3()
.then((runtime) => {
console.log(`[RR-V3] Bootstrap complete, ownerId: ${runtime.ownerId}`);
})
.catch((error) => {
console.error('[RR-V3] Bootstrap failed:', error);
});
}
// Element marker: context menu + CRUD listeners
initElementMarkerListeners();
// Web editor: toggle edit-mode overlay
initWebEditorListeners();
// Quick Panel: send messages to AgentChat via background-stream bridge
initQuickPanelAgentHandler();
// Quick Panel: tabs search bridge for content script UI
initQuickPanelTabsHandler();
// Quick Panel: keyboard shortcut handler
initQuickPanelCommands();
// Conditionally initialize semantic similarity engine if model cache exists
initializeSemanticEngineIfCached()
.then((initialized) => {
if (initialized) {
console.log('Background: Semantic similarity engine initialized from cache');
} else {
console.log(
'Background: Semantic similarity engine initialization skipped (no cache found)',
);
}
})
.catch((error) => {
console.warn('Background: Failed to conditionally initialize semantic engine:', error);
});
// Initial cleanup on startup
cleanupModelCache().catch((error) => {
console.warn('Background: Initial cache cleanup failed:', error);
});
});
================================================
FILE: app/chrome-extension/entrypoints/background/keepalive-manager.ts
================================================
/**
* @fileoverview Keepalive Manager
* @description Global singleton service for managing Service Worker keepalive.
*
* This module provides a unified interface for acquiring and releasing keepalive
* references. Multiple modules can acquire keepalive independently using tags,
* and the underlying keepalive mechanism will remain active as long as at least
* one reference is held.
*/
import {
createOffscreenKeepaliveController,
type KeepaliveController,
} from './record-replay-v3/engine/keepalive/offscreen-keepalive';
const LOG_PREFIX = '[KeepaliveManager]';
/**
* Singleton keepalive controller instance.
* Created lazily to avoid initialization issues during module loading.
*/
let controller: KeepaliveController | null = null;
/**
* Get or create the singleton keepalive controller.
*/
function getController(): KeepaliveController {
if (!controller) {
controller = createOffscreenKeepaliveController({ logger: console });
console.debug(`${LOG_PREFIX} Controller initialized`);
}
return controller;
}
/**
* Acquire a keepalive reference with a tag.
*
* @param tag - Identifier for the reference (e.g., 'native-host', 'rr-engine')
* @returns A release function to call when keepalive is no longer needed
*
* @example
* ```typescript
* const release = acquireKeepalive('native-host');
* // ... do work that needs SW to stay alive ...
* release(); // Release when done
* ```
*/
export function acquireKeepalive(tag: string): () => void {
try {
const release = getController().acquire(tag);
console.debug(`${LOG_PREFIX} Acquired keepalive for tag: ${tag}`);
return () => {
try {
release();
console.debug(`${LOG_PREFIX} Released keepalive for tag: ${tag}`);
} catch (error) {
console.warn(`${LOG_PREFIX} Failed to release keepalive for ${tag}:`, error);
}
};
} catch (error) {
console.warn(`${LOG_PREFIX} Failed to acquire keepalive for ${tag}:`, error);
return () => {};
}
}
/**
* Check if keepalive is currently active (any references held).
*/
export function isKeepaliveActive(): boolean {
try {
return getController().isActive();
} catch {
return false;
}
}
/**
* Get the current keepalive reference count.
* Useful for debugging.
*/
export function getKeepaliveRefCount(): number {
try {
return getController().getRefCount();
} catch {
return 0;
}
}
================================================
FILE: app/chrome-extension/entrypoints/background/native-host.ts
================================================
import { NativeMessageType } from 'chrome-mcp-shared';
import { BACKGROUND_MESSAGE_TYPES } from '@/common/message-types';
import { NATIVE_HOST, STORAGE_KEYS, ERROR_MESSAGES, SUCCESS_MESSAGES } from '@/common/constants';
import { handleCallTool } from './tools';
import { listPublished, getFlow } from './record-replay/flow-store';
import { acquireKeepalive } from './keepalive-manager';
const LOG_PREFIX = '[NativeHost]';
let nativePort: chrome.runtime.Port | null = null;
export const HOST_NAME = NATIVE_HOST.NAME;
// ==================== Reconnect Configuration ====================
const RECONNECT_BASE_DELAY_MS = 500;
const RECONNECT_MAX_DELAY_MS = 60_000;
const RECONNECT_MAX_FAST_ATTEMPTS = 8;
const RECONNECT_COOLDOWN_DELAY_MS = 5 * 60_000;
// ==================== Auto-connect State ====================
let keepaliveRelease: (() => void) | null = null;
let autoConnectEnabled = true;
let autoConnectLoaded = false;
let ensurePromise: Promise<boolean> | null = null;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
let reconnectAttempts = 0;
let manualDisconnect = false;
/**
* Server status management interface
*/
interface ServerStatus {
isRunning: boolean;
port?: number;
lastUpdated: number;
}
let currentServerStatus: ServerStatus = {
isRunning: false,
lastUpdated: Date.now(),
};
/**
* Save server status to chrome.storage
*/
async function saveServerStatus(status: ServerStatus): Promise<void> {
try {
await chrome.storage.local.set({ [STORAGE_KEYS.SERVER_STATUS]: status });
} catch (error) {
console.error(ERROR_MESSAGES.SERVER_STATUS_SAVE_FAILED, error);
}
}
/**
* Load server status from chrome.storage
*/
async function loadServerStatus(): Promise<ServerStatus> {
try {
const result = await chrome.storage.local.get([STORAGE_KEYS.SERVER_STATUS]);
if (result[STORAGE_KEYS.SERVER_STATUS]) {
return result[STORAGE_KEYS.SERVER_STATUS];
}
} catch (error) {
console.error(ERROR_MESSAGES.SERVER_STATUS_LOAD_FAILED, error);
}
return {
isRunning: false,
lastUpdated: Date.now(),
};
}
/**
* Broadcast server status change to all listeners
*/
function broadcastServerStatusChange(status: ServerStatus): void {
chrome.runtime
.sendMessage({
type: BACKGROUND_MESSAGE_TYPES.SERVER_STATUS_CHANGED,
payload: status,
})
.catch(() => {
// Ignore errors if no listeners are present
});
}
// ==================== Port Normalization ====================
/**
* Normalize a port value to a valid port number or null.
*/
function normalizePort(value: unknown): number | null {
const n =
typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : Number.NaN;
if (!Number.isFinite(n)) return null;
const port = Math.floor(n);
if (port <= 0 || port > 65535) return null;
return port;
}
// ==================== Reconnect Utilities ====================
/**
* Add jitter to a delay value to avoid thundering herd.
*/
function withJitter(ms: number): number {
const ratio = 0.7 + Math.random() * 0.6;
return Math.max(0, Math.round(ms * ratio));
}
/**
* Calculate reconnect delay based on attempt number.
* Uses exponential backoff with jitter, then switches to cooldown interval.
*/
function getReconnectDelayMs(attempt: number): number {
if (attempt >= RECONNECT_MAX_FAST_ATTEMPTS) {
return withJitter(RECONNECT_COOLDOWN_DELAY_MS);
}
const delay = Math.min(RECONNECT_BASE_DELAY_MS * Math.pow(2, attempt), RECONNECT_MAX_DELAY_MS);
return withJitter(delay);
}
/**
* Clear the reconnect timer if active.
*/
function clearReconnectTimer(): void {
if (!reconnectTimer) return;
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
/**
* Reset reconnect state after successful connection.
*/
function resetReconnectState(): void {
reconnectAttempts = 0;
clearReconnectTimer();
}
// ==================== Keepalive Management ====================
/**
* Sync keepalive hold based on autoConnectEnabled state.
* When auto-connect is enabled, we hold a keepalive reference to keep SW alive.
*/
function syncKeepaliveHold(): void {
if (autoConnectEnabled) {
if (!keepaliveRelease) {
keepaliveRelease = acquireKeepalive('native-host');
console.debug(`${LOG_PREFIX} Acquired keepalive`);
}
return;
}
if (keepaliveRelease) {
try {
keepaliveRelease();
console.debug(`${LOG_PREFIX} Released keepalive`);
} catch {
// Ignore
}
keepaliveRelease = null;
}
}
// ==================== Auto-connect Settings ====================
/**
* Load the nativeAutoConnectEnabled setting from storage.
*/
async function loadNativeAutoConnectEnabled(): Promise<boolean> {
try {
const result = await chrome.storage.local.get([STORAGE_KEYS.NATIVE_AUTO_CONNECT_ENABLED]);
const raw = result[STORAGE_KEYS.NATIVE_AUTO_CONNECT_ENABLED];
if (typeof raw === 'boolean') return raw;
} catch (error) {
console.warn(`${LOG_PREFIX} Failed to load nativeAutoConnectEnabled`, error);
}
return true; // Default to enabled
}
/**
* Set the nativeAutoConnectEnabled setting and persist to storage.
*/
async function setNativeAutoConnectEnabled(enabled: boolean): Promise<void> {
autoConnectEnabled = enabled;
autoConnectLoaded = true;
try {
await chrome.storage.local.set({ [STORAGE_KEYS.NATIVE_AUTO_CONNECT_ENABLED]: enabled });
console.debug(`${LOG_PREFIX} Set nativeAutoConnectEnabled=${enabled}`);
} catch (error) {
console.warn(`${LOG_PREFIX} Failed to persist nativeAutoConnectEnabled`, error);
}
syncKeepaliveHold();
}
// ==================== Port Preference ====================
/**
* Get the preferred port for connecting to native server.
* Priority: explicit override > user preference > last known port > default
*/
async function getPreferredPort(override?: unknown): Promise<number> {
const explicit = normalizePort(override);
if (explicit) return explicit;
try {
const result = await chrome.storage.local.get([
STORAGE_KEYS.NATIVE_SERVER_PORT,
STORAGE_KEYS.SERVER_STATUS,
]);
const userPort = normalizePort(result[STORAGE_KEYS.NATIVE_SERVER_PORT]);
if (userPort) return userPort;
const status = result[STORAGE_KEYS.SERVER_STATUS] as Partial<ServerStatus> | undefined;
const statusPort = normalizePort(status?.port);
if (statusPort) return statusPort;
} catch (error) {
console.warn(`${LOG_PREFIX} Failed to read preferred port`, error);
}
const inMemoryPort = normalizePort(currentServerStatus.port);
if (inMemoryPort) return inMemoryPort;
return NATIVE_HOST.DEFAULT_PORT;
}
// ==================== Reconnect Scheduling ====================
/**
* Schedule a reconnect attempt with exponential backoff.
*/
function scheduleReconnect(reason: string): void {
if (nativePort) return;
if (manualDisconnect) return;
if (!autoConnectEnabled) return;
if (reconnectTimer) return;
const delay = getReconnectDelayMs(reconnectAttempts);
console.debug(
`${LOG_PREFIX} Reconnect scheduled in ${delay}ms (attempt=${reconnectAttempts}, reason=${reason})`,
);
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
if (nativePort) return;
if (manualDisconnect || !autoConnectEnabled) return;
reconnectAttempts += 1;
void ensureNativeConnected(`reconnect:${reason}`).catch(() => {});
}, delay);
}
// ==================== Server Status Update ====================
/**
* Mark server as stopped and broadcast the change.
*/
async function markServerStopped(reason: string): Promise<void> {
currentServerStatus = {
isRunning: false,
port: currentServerStatus.port,
lastUpdated: Date.now(),
};
try {
await saveServerStatus(currentServerStatus);
} catch {
// Ignore
}
broadcastServerStatusChange(currentServerStatus);
console.debug(`${LOG_PREFIX} Server marked stopped (${reason})`);
}
// ==================== Core Ensure Function ====================
/**
* Ensure native connection is established.
* This is the main entry point for auto-connect logic.
*
* @param trigger - Description of what triggered this call (for logging)
* @param portOverride - Optional explicit port to use
* @returns Whether the connection is now established
*/
async function ensureNativeConnected(trigger: string, portOverride?: unknown): Promise<boolean> {
// Concurrency protection: only one ensure flow at a time
if (ensurePromise) return ensurePromise;
ensurePromise = (async () => {
// Load auto-connect setting if not yet loaded
if (!autoConnectLoaded) {
autoConnectEnabled = await loadNativeAutoConnectEnabled();
autoConnectLoaded = true;
syncKeepaliveHold();
}
// If auto-connect is disabled, do nothing
if (!autoConnectEnabled) {
console.debug(`${LOG_PREFIX} Auto-connect disabled, skipping ensure (trigger=${trigger})`);
return false;
}
// Sync keepalive hold
syncKeepaliveHold();
// Already connected
if (nativePort) {
console.debug(`${LOG_PREFIX} Already connected (trigger=${trigger})`);
return true;
}
// Get the port to use
const port = await getPreferredPort(portOverride);
console.debug(`${LOG_PREFIX} Attempting connection on port ${port} (trigger=${trigger})`);
// Attempt connection
const ok = connectNativeHost(port);
if (!ok) {
console.warn(`${LOG_PREFIX} Connection failed (trigger=${trigger})`);
scheduleReconnect(`connect_failed:${trigger}`);
return false;
}
console.debug(`${LOG_PREFIX} Connection initiated successfully (trigger=${trigger})`);
// Note: Don't reset reconnect state here. Wait for SERVER_STARTED confirmation.
// Chrome may return a Port but disconnect immediately if native host is missing.
return true;
})().finally(() => {
ensurePromise = null;
});
return ensurePromise;
}
/**
* Connect to the native messaging host
* @returns Whether the connection was initiated successfully
*/
export function connectNativeHost(port: number = NATIVE_HOST.DEFAULT_PORT): boolean {
if (nativePort) {
return true;
}
try {
nativePort = chrome.runtime.connectNative(HOST_NAME);
nativePort.onMessage.addListener(async (message) => {
if (message.type === NativeMessageType.PROCESS_DATA && message.requestId) {
const requestId = message.requestId;
const requestPayload = message.payload;
nativePort?.postMessage({
responseToRequestId: requestId,
payload: {
status: 'success',
message: SUCCESS_MESSAGES.TOOL_EXECUTED,
data: requestPayload,
},
});
} else if (message.type === NativeMessageType.CALL_TOOL && message.requestId) {
const requestId = message.requestId;
try {
const result = await handleCallTool(message.payload);
nativePort?.postMessage({
responseToRequestId: requestId,
payload: {
status: 'success',
message: SUCCESS_MESSAGES.TOOL_EXECUTED,
data: result,
},
});
} catch (error) {
nativePort?.postMessage({
responseToRequestId: requestId,
payload: {
status: 'error',
message: ERROR_MESSAGES.TOOL_EXECUTION_FAILED,
error: error instanceof Error ? error.message : String(error),
},
});
}
} else if (message.type === 'rr_list_published_flows' && message.requestId) {
const requestId = message.requestId;
try {
const published = await listPublished();
const items = [] as any[];
for (const p of published) {
const flow = await getFlow(p.id);
if (!flow) continue;
items.push({
id: p.id,
slug: p.slug,
version: p.version,
name: p.name,
description: p.description || flow.description || '',
variables: flow.variables || [],
meta: flow.meta || {},
});
}
nativePort?.postMessage({
responseToRequestId: requestId,
payload: { status: 'success', items },
});
} catch (error: any) {
nativePort?.postMessage({
responseToRequestId: requestId,
payload: { status: 'error', error: error?.message || String(error) },
});
}
} else if (message.type === NativeMessageType.SERVER_STARTED) {
const port = message.payload?.port;
currentServerStatus = {
isRunning: true,
port: port,
lastUpdated: Date.now(),
};
await saveServerStatus(currentServerStatus);
broadcastServerStatusChange(currentServerStatus);
// Server is confirmed running - now we can reset reconnect state
resetReconnectState();
console.log(`${SUCCESS_MESSAGES.SERVER_STARTED} on port ${port}`);
} else if (message.type === NativeMessageType.SERVER_STOPPED) {
currentServerStatus = {
isRunning: false,
port: currentServerStatus.port, // Keep last known port for reconnection
lastUpdated: Date.now(),
};
await saveServerStatus(currentServerStatus);
broadcastServerStatusChange(currentServerStatus);
console.log(SUCCESS_MESSAGES.SERVER_STOPPED);
} else if (message.type === NativeMessageType.ERROR_FROM_NATIVE_HOST) {
console.error('Error from native host:', message.payload?.message || 'Unknown error');
} else if (message.type === 'file_operation_response') {
// Forward file operation response back to the requesting tool
chrome.runtime.sendMessage(message).catch(() => {
// Ignore if no listeners
});
}
});
nativePort.onDisconnect.addListener(() => {
console.warn(ERROR_MESSAGES.NATIVE_DISCONNECTED, chrome.runtime.lastError);
nativePort = null;
// Mark server as stopped since native host disconnection means server is down
void markServerStopped('native_port_disconnected');
// Handle reconnection based on disconnect reason
if (manualDisconnect) {
manualDisconnect = false;
return;
}
if (!autoConnectEnabled) return;
scheduleReconnect('native_port_disconnected');
});
nativePort.postMessage({ type: NativeMessageType.START, payload: { port } });
// Note: Don't reset reconnect state here. Wait for SERVER_STARTED confirmation.
// Chrome may return a Port but disconnect immediately if native host is missing.
return true;
} catch (error) {
console.warn(ERROR_MESSAGES.NATIVE_CONNECTION_FAILED, error);
nativePort = null;
return false;
}
}
/**
* Initialize native host listeners and load initial state
*/
export const initNativeHostListener = () => {
// Initialize server status from storage
loadServerStatus()
.then((status) => {
currentServerStatus = status;
})
.catch((error) => {
console.error(ERROR_MESSAGES.SERVER_STATUS_LOAD_FAILED, error);
});
// Auto-connect on SW activation (covers SW restart after idle termination)
void ensureNativeConnected('sw_startup').catch(() => {});
// Auto-connect on Chrome browser startup
chrome.runtime.onStartup.addListener(() => {
void ensureNativeConnected('onStartup').catch(() => {});
});
// Auto-connect on extension install/update
chrome.runtime.onInstalled.addListener(() => {
void ensureNativeConnected('onInstalled').catch(() => {});
});
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
// Allow UI to call tools directly
if (message && message.type === 'call_tool' && message.name) {
handleCallTool({ name: message.name, args: message.args })
.then((res) => sendResponse({ success: true, result: res }))
.catch((err) =>
sendResponse({ success: false, error: err instanceof Error ? err.message : String(err) }),
);
return true;
}
const msgType = typeof message === 'string' ? message : message?.type;
// ENSURE_NATIVE: Trigger ensure without changing autoConnectEnabled
if (msgType === NativeMessageType.ENSURE_NATIVE) {
const portOverride = typeof message === 'object' ? message.port : undefined;
ensureNativeConnected('ui_ensure', portOverride)
.then((connected) => {
sendResponse({ success: true, connected, autoConnectEnabled });
})
.catch((e) => {
sendResponse({ success: false, connected: nativePort !== null, error: String(e) });
});
return true;
}
// CONNECT_NATIVE: Explicit user connect, re-enables auto-connect
if (msgType === NativeMessageType.CONNECT_NATIVE) {
const portOverride = typeof message === 'object' ? message.port : undefined;
const normalized = normalizePort(portOverride);
(async () => {
// Explicit user connect: re-enable auto-connect
await setNativeAutoConnectEnabled(true);
if (normalized) {
// Best-effort: persist preferred port
try {
await chrome.storage.local.set({ [STORAGE_KEYS.NATIVE_SERVER_PORT]: normalized });
} catch {
// Ignore
}
}
return ensureNativeConnected('ui_connect', normalized ?? undefined);
})()
.then((connected) => {
sendResponse({ success: true, connected });
})
.catch((e) => {
sendResponse({ success: false, connected: nativePort !== null, error: String(e) });
});
return true;
}
if (msgType === NativeMessageType.PING_NATIVE) {
const connected = nativePort !== null;
sendResponse({ connected, autoConnectEnabled });
return true;
}
// DISCONNECT_NATIVE: Explicit user disconnect, disables auto-connect
if (msgType === NativeMessageType.DISCONNECT_NATIVE) {
(async () => {
// Explicit user disconnect: disable auto-connect and stop reconnect loop
await setNativeAutoConnectEnabled(false);
clearReconnectTimer();
reconnectAttempts = 0;
syncKeepaliveHold();
if (nativePort) {
// Only set manualDisconnect if we actually have a port to disconnect.
// This prevents the flag from persisting when there's no active connection.
manualDisconnect = true;
try {
nativePort.disconnect();
} catch {
// Ignore
}
nativePort = null;
}
await markServerStopped('manual_disconnect');
})()
.then(() => {
sendResponse({ success: true });
})
.catch((e) => {
sendResponse({ success: false, error: String(e) });
});
return true;
}
if (message.type === BACKGROUND_MESSAGE_TYPES.GET_SERVER_STATUS) {
sendResponse({
success: true,
serverStatus: currentServerStatus,
connected: nativePort !== null,
});
return true;
}
if (message.type === BACKGROUND_MESSAGE_TYPES.REFRESH_SERVER_STATUS) {
loadServerStatus()
.then((storedStatus) => {
currentServerStatus = storedStatus;
sendResponse({
success: true,
serverStatus: currentServerStatus,
connected: nativePort !== null,
});
})
.catch((error) => {
console.error(ERROR_MESSAGES.SERVER_STATUS_LOAD_FAILED, error);
sendResponse({
success: false,
error: ERROR_MESSAGES.SERVER_STATUS_LOAD_FAILED,
serverStatus: currentServerStatus,
connected: nativePort !== null,
});
});
return true;
}
// Forward file operation messages to native host
if (message.type === 'forward_to_native' && message.message) {
if (nativePort) {
nativePort.postMessage(message.message);
sendResponse({ success: true });
} else {
sendResponse({ success: false, error: 'Native host not connected' });
}
return true;
}
});
};
================================================
FILE: app/chrome-extension/entrypoints/background/quick-panel/agent-handler.ts
================================================
/**
* Quick Panel Agent Handler
*
* Background service that bridges Quick Panel (content script) with the native-server Agent.
* Handles message routing, SSE streaming, and lifecycle management for AI chat requests.
*
* Architecture:
* - Quick Panel sends QUICK_PANEL_SEND_TO_AI via chrome.runtime.sendMessage
* - This handler subscribes to SSE first, then fires POST /act
* - Incoming RealtimeEvents are filtered by requestId and forwarded to the originating tab
* - Keepalive is explicitly managed to prevent MV3 Service Worker suspension during streaming
*
* @see https://developer.chrome.com/docs/extensions/mv3/service_workers/
*/
import type { AgentActRequest, RealtimeEvent } from 'chrome-mcp-shared';
import { NativeMessageType } from 'chrome-mcp-shared';
import { NATIVE_HOST, STORAGE_KEYS } from '@/common/constants';
import {
BACKGROUND_MESSAGE_TYPES,
TOOL_MESSAGE_TYPES,
type QuickPanelAIEventMessage,
type QuickPanelCancelAIMessage,
type QuickPanelCancelAIResponse,
type QuickPanelSendToAIMessage,
type QuickPanelSendToAIResponse,
} from '@/common/message-types';
import { acquireKeepalive } from '../keepalive-manager';
import { openAgentChatSidepanel } from '../utils/sidepanel';
// ============================================================
// Constants
// ============================================================
const LOG_PREFIX = '[QuickPanelAgent]';
const KEEPALIVE_TAG = 'quick-panel-ai';
/** Storage key for AgentChat selected session ID (owned by sidepanel composables) */
const STORAGE_KEY_SELECTED_SESSION = 'agent-selected-session-id';
/** Timeout for initial SSE connection establishment */
const SSE_CONNECT_TIMEOUT_MS = 3000;
/** Safety timeout for entire request lifecycle (15 minutes) */
const REQUEST_TIMEOUT_MS = 15 * 60 * 1000;
/** Flag indicating SSE connection was successful */
const SSE_CONNECTED = Symbol('SSE_CONNECTED');
/** Flag indicating SSE connection timed out but we should continue */
const SSE_TIMEOUT = Symbol('SSE_TIMEOUT');
// ============================================================
// Types
// ============================================================
/**
* Represents an active streaming request from Quick Panel.
*
* Background maintains this state to:
* 1. Route SSE events to the correct tab
* 2. Manage keepalive lifecycle
* 3. Handle cancellation and cleanup
*/
interface ActiveRequest {
readonly requestId: string;
readonly sessionId: string;
readonly instruction: string;
readonly tabId: number;
readonly windowId?: number;
readonly frameId?: number;
readonly port: number;
readonly createdAt: number;
readonly abortController: AbortController;
readonly releaseKeepalive: () => void;
readonly timeoutId: ReturnType<typeof setTimeout>;
}
// ============================================================
// State
// ============================================================
/** Active streaming requests indexed by requestId */
const activeRequests = new Map<string, ActiveRequest>();
/** Initialization flag to prevent duplicate listeners */
let initialized = false;
// ============================================================
// Utility Functions
// ============================================================
function normalizeString(value: unknown): string {
return typeof value === 'string' ? value : '';
}
function normalizePort(value: unknown): number | null {
const num =
typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : Number.NaN;
if (!Number.isFinite(num)) return null;
const port = Math.floor(num);
if (port <= 0 || port > 65535) return null;
return port;
}
function createRequestId(): string {
// Prefer crypto.randomUUID for proper UUID format
try {
const id = crypto?.randomUUID?.();
if (id) return id;
} catch {
// Fallback for environments without crypto.randomUUID
}
return `req_${Date.now()}_${Math.random().toString(16).slice(2)}`;
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isTerminalStatus(status: string): boolean {
return status === 'completed' || status === 'error' || status === 'cancelled';
}
// ============================================================
// Event Factories
// ============================================================
function createErrorEvent(sessionId: string, requestId: string, error: string): RealtimeEvent {
return {
type: 'error',
error: error || 'Unknown error',
data: { sessionId, requestId },
};
}
function createCancelledStatusEvent(
sessionId: string,
requestId: string,
message?: string,
): RealtimeEvent {
return {
type: 'status',
data: {
sessionId,
status: 'cancelled',
requestId,
message: message || 'Cancelled by user',
},
};
}
// ============================================================
// Event Forwarding
// ============================================================
/**
* Forward a RealtimeEvent to the Quick Panel in the originating tab.
* Handles receiver unavailability gracefully by cleaning up the request.
*/
function forwardEventToQuickPanel(request: ActiveRequest, event: RealtimeEvent): void {
const message: QuickPanelAIEventMessage = {
action: TOOL_MESSAGE_TYPES.QUICK_PANEL_AI_EVENT,
requestId: request.requestId,
sessionId: request.sessionId,
event,
};
const sendOptions =
typeof request.frameId === 'number' ? { frameId: request.frameId } : undefined;
const sendPromise = sendOptions
? chrome.tabs.sendMessage(request.tabId, message, sendOptions)
: chrome.tabs.sendMessage(request.tabId, message);
sendPromise.catch((err) => {
const msg = err instanceof Error ? err.message : String(err);
// Detect receiver unavailability (tab closed, navigated, Quick Panel closed)
const receiverGone =
msg.includes('Receiving end does not exist') ||
msg.includes('No tab with id') ||
msg.includes('The message port closed');
if (receiverGone) {
cleanupRequest(request.requestId, 'receiver_unavailable');
}
});
}
// ============================================================
// Request Lifecycle Management
// ============================================================
/**
* Clean up an active request and release all associated resources.
* Idempotent - safe to call multiple times.
*/
function cleanupRequest(requestId: string, reason: string): void {
const request = activeRequests.get(requestId);
if (!request) return;
activeRequests.delete(requestId);
// Clear timeout
try {
clearTimeout(request.timeoutId);
} catch {
// Ignor
gitextract_0pcja33u/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── build-release.yml
├── .gitignore
├── .husky/
│ ├── commit-msg
│ └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│ └── extensions.json
├── LICENSE
├── README.md
├── README_zh.md
├── app/
│ ├── chrome-extension/
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── _locales/
│ │ │ ├── de/
│ │ │ │ └── messages.json
│ │ │ ├── en/
│ │ │ │ └── messages.json
│ │ │ ├── ja/
│ │ │ │ └── messages.json
│ │ │ ├── ko/
│ │ │ │ └── messages.json
│ │ │ ├── zh_CN/
│ │ │ │ └── messages.json
│ │ │ └── zh_TW/
│ │ │ └── messages.json
│ │ ├── common/
│ │ │ ├── agent-models.ts
│ │ │ ├── constants.ts
│ │ │ ├── element-marker-types.ts
│ │ │ ├── message-types.ts
│ │ │ ├── node-types.ts
│ │ │ ├── rr-v3-keepalive-protocol.ts
│ │ │ ├── step-types.ts
│ │ │ ├── tool-handler.ts
│ │ │ └── web-editor-types.ts
│ │ ├── entrypoints/
│ │ │ ├── background/
│ │ │ │ ├── element-marker/
│ │ │ │ │ ├── element-marker-storage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── keepalive-manager.ts
│ │ │ │ ├── native-host.ts
│ │ │ │ ├── quick-panel/
│ │ │ │ │ ├── agent-handler.ts
│ │ │ │ │ ├── commands.ts
│ │ │ │ │ └── tabs-handler.ts
│ │ │ │ ├── record-replay/
│ │ │ │ │ ├── actions/
│ │ │ │ │ │ ├── adapter.ts
│ │ │ │ │ │ ├── handlers/
│ │ │ │ │ │ │ ├── assert.ts
│ │ │ │ │ │ │ ├── click.ts
│ │ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ │ ├── control-flow.ts
│ │ │ │ │ │ │ ├── delay.ts
│ │ │ │ │ │ │ ├── dom.ts
│ │ │ │ │ │ │ ├── drag.ts
│ │ │ │ │ │ │ ├── extract.ts
│ │ │ │ │ │ │ ├── fill.ts
│ │ │ │ │ │ │ ├── http.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── key.ts
│ │ │ │ │ │ │ ├── navigate.ts
│ │ │ │ │ │ │ ├── screenshot.ts
│ │ │ │ │ │ │ ├── script.ts
│ │ │ │ │ │ │ ├── scroll.ts
│ │ │ │ │ │ │ ├── tabs.ts
│ │ │ │ │ │ │ └── wait.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── registry.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── engine/
│ │ │ │ │ │ ├── constants.ts
│ │ │ │ │ │ ├── execution-mode.ts
│ │ │ │ │ │ ├── logging/
│ │ │ │ │ │ │ └── run-logger.ts
│ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ ├── breakpoint.ts
│ │ │ │ │ │ │ ├── manager.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ │ ├── policies/
│ │ │ │ │ │ │ ├── retry.ts
│ │ │ │ │ │ │ └── wait.ts
│ │ │ │ │ │ ├── runners/
│ │ │ │ │ │ │ ├── after-script-queue.ts
│ │ │ │ │ │ │ ├── control-flow-runner.ts
│ │ │ │ │ │ │ ├── step-executor.ts
│ │ │ │ │ │ │ ├── step-runner.ts
│ │ │ │ │ │ │ └── subflow-runner.ts
│ │ │ │ │ │ ├── scheduler.ts
│ │ │ │ │ │ ├── state-manager.ts
│ │ │ │ │ │ └── utils/
│ │ │ │ │ │ └── expression.ts
│ │ │ │ │ ├── flow-runner.ts
│ │ │ │ │ ├── flow-store.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── legacy-types.ts
│ │ │ │ │ ├── nodes/
│ │ │ │ │ │ ├── assert.ts
│ │ │ │ │ │ ├── click.ts
│ │ │ │ │ │ ├── conditional.ts
│ │ │ │ │ │ ├── download-screenshot-attr-event-frame-loop.ts
│ │ │ │ │ │ ├── drag.ts
│ │ │ │ │ │ ├── execute-flow.ts
│ │ │ │ │ │ ├── extract.ts
│ │ │ │ │ │ ├── fill.ts
│ │ │ │ │ │ ├── http.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── key.ts
│ │ │ │ │ │ ├── loops.ts
│ │ │ │ │ │ ├── navigate.ts
│ │ │ │ │ │ ├── script.ts
│ │ │ │ │ │ ├── scroll.ts
│ │ │ │ │ │ ├── tabs.ts
│ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ └── wait.ts
│ │ │ │ │ ├── recording/
│ │ │ │ │ │ ├── browser-event-listener.ts
│ │ │ │ │ │ ├── content-injection.ts
│ │ │ │ │ │ ├── content-message-handler.ts
│ │ │ │ │ │ ├── flow-builder.ts
│ │ │ │ │ │ ├── recorder-manager.ts
│ │ │ │ │ │ └── session-manager.ts
│ │ │ │ │ ├── rr-utils.ts
│ │ │ │ │ ├── selector-engine.ts
│ │ │ │ │ ├── storage/
│ │ │ │ │ │ └── indexeddb-manager.ts
│ │ │ │ │ ├── trigger-store.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── record-replay-v3/
│ │ │ │ │ ├── bootstrap.ts
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── debug.ts
│ │ │ │ │ │ ├── errors.ts
│ │ │ │ │ │ ├── events.ts
│ │ │ │ │ │ ├── flow.ts
│ │ │ │ │ │ ├── ids.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── json.ts
│ │ │ │ │ │ ├── policy.ts
│ │ │ │ │ │ ├── triggers.ts
│ │ │ │ │ │ └── variables.ts
│ │ │ │ │ ├── engine/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── keepalive/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── offscreen-keepalive.ts
│ │ │ │ │ │ ├── kernel/
│ │ │ │ │ │ │ ├── artifacts.ts
│ │ │ │ │ │ │ ├── breakpoints.ts
│ │ │ │ │ │ │ ├── debug-controller.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── kernel.ts
│ │ │ │ │ │ │ ├── recovery-kernel.ts
│ │ │ │ │ │ │ ├── runner.ts
│ │ │ │ │ │ │ └── traversal.ts
│ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── register-v2-replay-nodes.ts
│ │ │ │ │ │ │ ├── registry.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── v2-action-adapter.ts
│ │ │ │ │ │ ├── queue/
│ │ │ │ │ │ │ ├── enqueue-run.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── leasing.ts
│ │ │ │ │ │ │ ├── queue.ts
│ │ │ │ │ │ │ └── scheduler.ts
│ │ │ │ │ │ ├── recovery/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── recovery-coordinator.ts
│ │ │ │ │ │ ├── storage/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── storage-port.ts
│ │ │ │ │ │ ├── transport/
│ │ │ │ │ │ │ ├── events-bus.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── rpc-server.ts
│ │ │ │ │ │ │ └── rpc.ts
│ │ │ │ │ │ └── triggers/
│ │ │ │ │ │ ├── command-trigger.ts
│ │ │ │ │ │ ├── context-menu-trigger.ts
│ │ │ │ │ │ ├── cron-trigger.ts
│ │ │ │ │ │ ├── dom-trigger.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── interval-trigger.ts
│ │ │ │ │ │ ├── manual-trigger.ts
│ │ │ │ │ │ ├── once-trigger.ts
│ │ │ │ │ │ ├── trigger-handler.ts
│ │ │ │ │ │ ├── trigger-manager.ts
│ │ │ │ │ │ └── url-trigger.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── storage/
│ │ │ │ │ ├── db.ts
│ │ │ │ │ ├── events.ts
│ │ │ │ │ ├── flows.ts
│ │ │ │ │ ├── import/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── v2-reader.ts
│ │ │ │ │ │ └── v2-to-v3.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── persistent-vars.ts
│ │ │ │ │ ├── queue.ts
│ │ │ │ │ ├── runs.ts
│ │ │ │ │ └── triggers.ts
│ │ │ │ ├── semantic-similarity.ts
│ │ │ │ ├── storage-manager.ts
│ │ │ │ ├── tools/
│ │ │ │ │ ├── base-browser.ts
│ │ │ │ │ ├── browser/
│ │ │ │ │ │ ├── bookmark.ts
│ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ ├── computer.ts
│ │ │ │ │ │ ├── console-buffer.ts
│ │ │ │ │ │ ├── console.ts
│ │ │ │ │ │ ├── dialog.ts
│ │ │ │ │ │ ├── download.ts
│ │ │ │ │ │ ├── element-picker.ts
│ │ │ │ │ │ ├── file-upload.ts
│ │ │ │ │ │ ├── gif-auto-capture.ts
│ │ │ │ │ │ ├── gif-enhanced-renderer.ts
│ │ │ │ │ │ ├── gif-recorder.ts
│ │ │ │ │ │ ├── history.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── inject-script.ts
│ │ │ │ │ │ ├── interaction.ts
│ │ │ │ │ │ ├── javascript.ts
│ │ │ │ │ │ ├── keyboard.ts
│ │ │ │ │ │ ├── network-capture-debugger.ts
│ │ │ │ │ │ ├── network-capture-web-request.ts
│ │ │ │ │ │ ├── network-capture.ts
│ │ │ │ │ │ ├── network-request.ts
│ │ │ │ │ │ ├── performance.ts
│ │ │ │ │ │ ├── read-page.ts
│ │ │ │ │ │ ├── screenshot.ts
│ │ │ │ │ │ ├── userscript.ts
│ │ │ │ │ │ ├── vector-search.ts
│ │ │ │ │ │ ├── web-fetcher.ts
│ │ │ │ │ │ └── window.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── record-replay.ts
│ │ │ │ ├── utils/
│ │ │ │ │ └── sidepanel.ts
│ │ │ │ └── web-editor/
│ │ │ │ └── index.ts
│ │ │ ├── builder/
│ │ │ │ ├── App.vue
│ │ │ │ ├── index.html
│ │ │ │ └── main.ts
│ │ │ ├── content.ts
│ │ │ ├── element-picker.content.ts
│ │ │ ├── offscreen/
│ │ │ │ ├── gif-encoder.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ └── rr-keepalive.ts
│ │ │ ├── options/
│ │ │ │ ├── App.vue
│ │ │ │ ├── index.html
│ │ │ │ └── main.ts
│ │ │ ├── popup/
│ │ │ │ ├── App.vue
│ │ │ │ ├── components/
│ │ │ │ │ ├── ConfirmDialog.vue
│ │ │ │ │ ├── ElementMarkerManagement.vue
│ │ │ │ │ ├── LocalModelPage.vue
│ │ │ │ │ ├── ModelCacheManagement.vue
│ │ │ │ │ ├── ProgressIndicator.vue
│ │ │ │ │ ├── ScheduleDialog.vue
│ │ │ │ │ ├── builder/
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── Canvas.vue
│ │ │ │ │ │ │ ├── EdgePropertyPanel.vue
│ │ │ │ │ │ │ ├── KeyValueEditor.vue
│ │ │ │ │ │ │ ├── PropertyPanel.vue
│ │ │ │ │ │ │ ├── Sidebar.vue
│ │ │ │ │ │ │ ├── TriggerPanel.vue
│ │ │ │ │ │ │ ├── nodes/
│ │ │ │ │ │ │ │ ├── NodeCard.vue
│ │ │ │ │ │ │ │ ├── NodeIf.vue
│ │ │ │ │ │ │ │ └── node-util.ts
│ │ │ │ │ │ │ └── properties/
│ │ │ │ │ │ │ ├── PropertyAssert.vue
│ │ │ │ │ │ │ ├── PropertyClick.vue
│ │ │ │ │ │ │ ├── PropertyCloseTab.vue
│ │ │ │ │ │ │ ├── PropertyDelay.vue
│ │ │ │ │ │ │ ├── PropertyDrag.vue
│ │ │ │ │ │ │ ├── PropertyExecuteFlow.vue
│ │ │ │ │ │ │ ├── PropertyExtract.vue
│ │ │ │ │ │ │ ├── PropertyFill.vue
│ │ │ │ │ │ │ ├── PropertyForeach.vue
│ │ │ │ │ │ │ ├── PropertyFormRenderer.vue
│ │ │ │ │ │ │ ├── PropertyFromSpec.vue
│ │ │ │ │ │ │ ├── PropertyHandleDownload.vue
│ │ │ │ │ │ │ ├── PropertyHttp.vue
│ │ │ │ │ │ │ ├── PropertyIf.vue
│ │ │ │ │ │ │ ├── PropertyKey.vue
│ │ │ │ │ │ │ ├── PropertyLoopElements.vue
│ │ │ │ │ │ │ ├── PropertyNavigate.vue
│ │ │ │ │ │ │ ├── PropertyOpenTab.vue
│ │ │ │ │ │ │ ├── PropertyScreenshot.vue
│ │ │ │ │ │ │ ├── PropertyScript.vue
│ │ │ │ │ │ │ ├── PropertyScroll.vue
│ │ │ │ │ │ │ ├── PropertySetAttribute.vue
│ │ │ │ │ │ │ ├── PropertySwitchFrame.vue
│ │ │ │ │ │ │ ├── PropertySwitchTab.vue
│ │ │ │ │ │ │ ├── PropertyTrigger.vue
│ │ │ │ │ │ │ ├── PropertyTriggerEvent.vue
│ │ │ │ │ │ │ ├── PropertyWait.vue
│ │ │ │ │ │ │ ├── PropertyWhile.vue
│ │ │ │ │ │ │ └── SelectorEditor.vue
│ │ │ │ │ │ ├── model/
│ │ │ │ │ │ │ ├── form-widget-registry.ts
│ │ │ │ │ │ │ ├── node-spec-registry.ts
│ │ │ │ │ │ │ ├── node-spec.ts
│ │ │ │ │ │ │ ├── node-specs-builtin.ts
│ │ │ │ │ │ │ ├── toast.ts
│ │ │ │ │ │ │ ├── transforms.ts
│ │ │ │ │ │ │ ├── ui-nodes.ts
│ │ │ │ │ │ │ ├── validation.ts
│ │ │ │ │ │ │ └── variables.ts
│ │ │ │ │ │ ├── store/
│ │ │ │ │ │ │ └── useBuilderStore.ts
│ │ │ │ │ │ └── widgets/
│ │ │ │ │ │ ├── FieldCode.vue
│ │ │ │ │ │ ├── FieldDuration.vue
│ │ │ │ │ │ ├── FieldExpression.vue
│ │ │ │ │ │ ├── FieldKeySequence.vue
│ │ │ │ │ │ ├── FieldSelector.vue
│ │ │ │ │ │ ├── FieldTargetLocator.vue
│ │ │ │ │ │ └── VarInput.vue
│ │ │ │ │ └── icons/
│ │ │ │ │ ├── BoltIcon.vue
│ │ │ │ │ ├── CheckIcon.vue
│ │ │ │ │ ├── DatabaseIcon.vue
│ │ │ │ │ ├── DocumentIcon.vue
│ │ │ │ │ ├── EditIcon.vue
│ │ │ │ │ ├── MarkerIcon.vue
│ │ │ │ │ ├── RecordIcon.vue
│ │ │ │ │ ├── RefreshIcon.vue
│ │ │ │ │ ├── StopIcon.vue
│ │ │ │ │ ├── TabIcon.vue
│ │ │ │ │ ├── TrashIcon.vue
│ │ │ │ │ ├── VectorIcon.vue
│ │ │ │ │ ├── WorkflowIcon.vue
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ └── style.css
│ │ │ ├── quick-panel.content.ts
│ │ │ ├── shared/
│ │ │ │ ├── composables/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── useRRV3Rpc.ts
│ │ │ │ └── utils/
│ │ │ │ ├── index.ts
│ │ │ │ └── rr-flow-convert.ts
│ │ │ ├── sidepanel/
│ │ │ │ ├── App.vue
│ │ │ │ ├── components/
│ │ │ │ │ ├── AgentChat.vue
│ │ │ │ │ ├── SidepanelNavigator.vue
│ │ │ │ │ ├── agent/
│ │ │ │ │ │ ├── AttachmentPreview.vue
│ │ │ │ │ │ ├── ChatInput.vue
│ │ │ │ │ │ ├── CliSettings.vue
│ │ │ │ │ │ ├── ConnectionStatus.vue
│ │ │ │ │ │ ├── MessageItem.vue
│ │ │ │ │ │ ├── MessageList.vue
│ │ │ │ │ │ ├── ProjectCreateForm.vue
│ │ │ │ │ │ ├── ProjectSelector.vue
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── agent-chat/
│ │ │ │ │ │ ├── AgentChatShell.vue
│ │ │ │ │ │ ├── AgentComposer.vue
│ │ │ │ │ │ ├── AgentConversation.vue
│ │ │ │ │ │ ├── AgentOpenProjectMenu.vue
│ │ │ │ │ │ ├── AgentProjectMenu.vue
│ │ │ │ │ │ ├── AgentRequestThread.vue
│ │ │ │ │ │ ├── AgentSessionListItem.vue
│ │ │ │ │ │ ├── AgentSessionMenu.vue
│ │ │ │ │ │ ├── AgentSessionSettingsPanel.vue
│ │ │ │ │ │ ├── AgentSessionsView.vue
│ │ │ │ │ │ ├── AgentSettingsMenu.vue
│ │ │ │ │ │ ├── AgentTimeline.vue
│ │ │ │ │ │ ├── AgentTimelineItem.vue
│ │ │ │ │ │ ├── AgentTopBar.vue
│ │ │ │ │ │ ├── ApplyMessageChip.vue
│ │ │ │ │ │ ├── AttachmentCachePanel.vue
│ │ │ │ │ │ ├── ComposerDrawer.vue
│ │ │ │ │ │ ├── ElementChip.vue
│ │ │ │ │ │ ├── FakeCaretOverlay.vue
│ │ │ │ │ │ ├── SelectionChip.vue
│ │ │ │ │ │ ├── WebEditorChanges.vue
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── timeline/
│ │ │ │ │ │ ├── ThinkingNode.vue
│ │ │ │ │ │ ├── TimelineNarrativeStep.vue
│ │ │ │ │ │ ├── TimelineStatusStep.vue
│ │ │ │ │ │ ├── TimelineToolCallStep.vue
│ │ │ │ │ │ ├── TimelineToolResultCardStep.vue
│ │ │ │ │ │ ├── TimelineUserPromptStep.vue
│ │ │ │ │ │ └── markstream-thinking.ts
│ │ │ │ │ ├── rr-v3/
│ │ │ │ │ │ └── DebuggerPanel.vue
│ │ │ │ │ └── workflows/
│ │ │ │ │ ├── WorkflowListItem.vue
│ │ │ │ │ ├── WorkflowsView.vue
│ │ │ │ │ └── index.ts
│ │ │ │ ├── composables/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useAgentChat.ts
│ │ │ │ │ ├── useAgentChatViewRoute.ts
│ │ │ │ │ ├── useAgentInputPreferences.ts
│ │ │ │ │ ├── useAgentProjects.ts
│ │ │ │ │ ├── useAgentServer.ts
│ │ │ │ │ ├── useAgentSessions.ts
│ │ │ │ │ ├── useAgentTheme.ts
│ │ │ │ │ ├── useAgentThreads.ts
│ │ │ │ │ ├── useAttachments.ts
│ │ │ │ │ ├── useFakeCaret.ts
│ │ │ │ │ ├── useFloatingDrag.ts
│ │ │ │ │ ├── useOpenProjectPreference.ts
│ │ │ │ │ ├── useRRV3Debugger.ts
│ │ │ │ │ ├── useRRV3Rpc.ts
│ │ │ │ │ ├── useTextareaAutoResize.ts
│ │ │ │ │ ├── useWebEditorTxState.ts
│ │ │ │ │ └── useWorkflowsV3.ts
│ │ │ │ ├── index.html
│ │ │ │ ├── main.ts
│ │ │ │ ├── styles/
│ │ │ │ │ └── agent-chat.css
│ │ │ │ └── utils/
│ │ │ │ └── loading-texts.ts
│ │ │ ├── styles/
│ │ │ │ └── tailwind.css
│ │ │ ├── web-editor-v2/
│ │ │ │ ├── attr-ui-refactor.md
│ │ │ │ ├── constants.ts
│ │ │ │ ├── core/
│ │ │ │ │ ├── css-compare.ts
│ │ │ │ │ ├── cssom-styles-collector.ts
│ │ │ │ │ ├── debug-source.ts
│ │ │ │ │ ├── design-tokens/
│ │ │ │ │ │ ├── design-tokens-service.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── token-detector.ts
│ │ │ │ │ │ ├── token-resolver.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── editor.ts
│ │ │ │ │ ├── element-key.ts
│ │ │ │ │ ├── event-controller.ts
│ │ │ │ │ ├── execution-tracker.ts
│ │ │ │ │ ├── hmr-consistency.ts
│ │ │ │ │ ├── locator.ts
│ │ │ │ │ ├── message-listener.ts
│ │ │ │ │ ├── payload-builder.ts
│ │ │ │ │ ├── perf-monitor.ts
│ │ │ │ │ ├── position-tracker.ts
│ │ │ │ │ ├── props-bridge.ts
│ │ │ │ │ ├── snap-engine.ts
│ │ │ │ │ ├── transaction-aggregator.ts
│ │ │ │ │ └── transaction-manager.ts
│ │ │ │ ├── drag/
│ │ │ │ │ └── drag-reorder-controller.ts
│ │ │ │ ├── overlay/
│ │ │ │ │ ├── canvas-overlay.ts
│ │ │ │ │ └── handles-controller.ts
│ │ │ │ ├── selection/
│ │ │ │ │ └── selection-engine.ts
│ │ │ │ ├── ui/
│ │ │ │ │ ├── breadcrumbs.ts
│ │ │ │ │ ├── floating-drag.ts
│ │ │ │ │ ├── icons.ts
│ │ │ │ │ ├── property-panel/
│ │ │ │ │ │ ├── class-editor.ts
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── alignment-grid.ts
│ │ │ │ │ │ │ ├── icon-button-group.ts
│ │ │ │ │ │ │ ├── input-container.ts
│ │ │ │ │ │ │ ├── slider-input.ts
│ │ │ │ │ │ │ └── token-pill.ts
│ │ │ │ │ │ ├── components-tree.ts
│ │ │ │ │ │ ├── controls/
│ │ │ │ │ │ │ ├── appearance-control.ts
│ │ │ │ │ │ │ ├── background-control.ts
│ │ │ │ │ │ │ ├── border-control.ts
│ │ │ │ │ │ │ ├── color-field.ts
│ │ │ │ │ │ │ ├── css-helpers.ts
│ │ │ │ │ │ │ ├── effects-control.ts
│ │ │ │ │ │ │ ├── gradient-control.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── layout-control.ts
│ │ │ │ │ │ │ ├── number-stepping.ts
│ │ │ │ │ │ │ ├── position-control.ts
│ │ │ │ │ │ │ ├── size-control.ts
│ │ │ │ │ │ │ ├── spacing-control.ts
│ │ │ │ │ │ │ ├── token-picker.ts
│ │ │ │ │ │ │ └── typography-control.ts
│ │ │ │ │ │ ├── css-defaults.ts
│ │ │ │ │ │ ├── css-panel.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── property-panel.ts
│ │ │ │ │ │ ├── props-panel.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── shadow-host.ts
│ │ │ │ │ └── toolbar.ts
│ │ │ │ └── utils/
│ │ │ │ └── disposables.ts
│ │ │ ├── web-editor-v2.ts
│ │ │ └── welcome/
│ │ │ ├── App.vue
│ │ │ ├── index.html
│ │ │ └── main.ts
│ │ ├── env.d.ts
│ │ ├── eslint.config.js
│ │ ├── inject-scripts/
│ │ │ ├── accessibility-tree-helper.js
│ │ │ ├── click-helper.js
│ │ │ ├── dom-observer.js
│ │ │ ├── element-marker.js
│ │ │ ├── element-picker.js
│ │ │ ├── fill-helper.js
│ │ │ ├── inject-bridge.js
│ │ │ ├── interactive-elements-helper.js
│ │ │ ├── keyboard-helper.js
│ │ │ ├── network-helper.js
│ │ │ ├── props-agent.js
│ │ │ ├── recorder.js
│ │ │ ├── screenshot-helper.js
│ │ │ ├── wait-helper.js
│ │ │ ├── web-editor.js
│ │ │ └── web-fetcher-helper.js
│ │ ├── package.json
│ │ ├── shared/
│ │ │ ├── element-picker/
│ │ │ │ ├── controller.ts
│ │ │ │ └── index.ts
│ │ │ ├── quick-panel/
│ │ │ │ ├── core/
│ │ │ │ │ ├── agent-bridge.ts
│ │ │ │ │ ├── search-engine.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── providers/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── tabs-provider.ts
│ │ │ │ └── ui/
│ │ │ │ ├── ai-chat-panel.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── markdown-renderer.ts
│ │ │ │ ├── message-renderer.ts
│ │ │ │ ├── panel-shell.ts
│ │ │ │ ├── quick-entries.ts
│ │ │ │ ├── search-input.ts
│ │ │ │ ├── shadow-host.ts
│ │ │ │ └── styles.ts
│ │ │ └── selector/
│ │ │ ├── dom-path.ts
│ │ │ ├── fingerprint.ts
│ │ │ ├── generator.ts
│ │ │ ├── index.ts
│ │ │ ├── locator.ts
│ │ │ ├── shadow-dom.ts
│ │ │ ├── stability.ts
│ │ │ ├── strategies/
│ │ │ │ ├── anchor-relpath.ts
│ │ │ │ ├── aria.ts
│ │ │ │ ├── css-path.ts
│ │ │ │ ├── css-unique.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── testid.ts
│ │ │ │ └── text.ts
│ │ │ └── types.ts
│ │ ├── tailwind.config.ts
│ │ ├── tests/
│ │ │ ├── __mocks__/
│ │ │ │ └── hnswlib-wasm-static.ts
│ │ │ ├── record-replay/
│ │ │ │ ├── _test-helpers.ts
│ │ │ │ ├── adapter-policy.contract.test.ts
│ │ │ │ ├── flow-store-strip-steps.contract.test.ts
│ │ │ │ ├── high-risk-actions.integration.test.ts
│ │ │ │ ├── hybrid-actions.integration.test.ts
│ │ │ │ ├── script-control-flow.integration.test.ts
│ │ │ │ ├── session-dag-sync.contract.test.ts
│ │ │ │ ├── step-executor.contract.test.ts
│ │ │ │ └── tab-cursor.integration.test.ts
│ │ │ ├── record-replay-v3/
│ │ │ │ ├── command-trigger.test.ts
│ │ │ │ ├── context-menu-trigger.test.ts
│ │ │ │ ├── cron-trigger.test.ts
│ │ │ │ ├── debugger.contract.test.ts
│ │ │ │ ├── dom-trigger.test.ts
│ │ │ │ ├── e2e.integration.test.ts
│ │ │ │ ├── events.contract.test.ts
│ │ │ │ ├── interval-trigger.test.ts
│ │ │ │ ├── manual-trigger.test.ts
│ │ │ │ ├── once-trigger.test.ts
│ │ │ │ ├── queue.contract.test.ts
│ │ │ │ ├── recovery.test.ts
│ │ │ │ ├── rpc-api.test.ts
│ │ │ │ ├── runner.onError.contract.test.ts
│ │ │ │ ├── scheduler-integration.test.ts
│ │ │ │ ├── scheduler.test.ts
│ │ │ │ ├── spec-smoke.test.ts
│ │ │ │ ├── trigger-manager.test.ts
│ │ │ │ ├── triggers.test.ts
│ │ │ │ ├── url-trigger.test.ts
│ │ │ │ ├── v2-action-adapter.test.ts
│ │ │ │ ├── v2-adapter-integration.test.ts
│ │ │ │ ├── v2-to-v3-conversion.test.ts
│ │ │ │ └── v3-e2e-harness.ts
│ │ │ ├── vitest.setup.ts
│ │ │ └── web-editor-v2/
│ │ │ ├── design-tokens.test.ts
│ │ │ ├── drag-reorder-controller.test.ts
│ │ │ ├── event-controller.test.ts
│ │ │ ├── locator.test.ts
│ │ │ ├── property-panel-live-sync.test.ts
│ │ │ ├── selection-engine.test.ts
│ │ │ ├── snap-engine.test.ts
│ │ │ └── test-utils/
│ │ │ └── dom.ts
│ │ ├── tsconfig.json
│ │ ├── types/
│ │ │ ├── gifenc.d.ts
│ │ │ └── icons.d.ts
│ │ ├── utils/
│ │ │ ├── cdp-session-manager.ts
│ │ │ ├── content-indexer.ts
│ │ │ ├── i18n.ts
│ │ │ ├── image-utils.ts
│ │ │ ├── indexeddb-client.ts
│ │ │ ├── lru-cache.ts
│ │ │ ├── model-cache-manager.ts
│ │ │ ├── offscreen-manager.ts
│ │ │ ├── output-sanitizer.ts
│ │ │ ├── screenshot-context.ts
│ │ │ ├── semantic-similarity-engine.ts
│ │ │ ├── simd-math-engine.ts
│ │ │ ├── text-chunker.ts
│ │ │ └── vector-database.ts
│ │ ├── vitest.config.ts
│ │ ├── workers/
│ │ │ ├── ort-wasm-simd-threaded.jsep.mjs
│ │ │ ├── ort-wasm-simd-threaded.jsep.wasm
│ │ │ ├── ort-wasm-simd-threaded.mjs
│ │ │ ├── ort-wasm-simd-threaded.wasm
│ │ │ ├── simd_math.js
│ │ │ ├── simd_math_bg.wasm
│ │ │ └── similarity.worker.js
│ │ └── wxt.config.ts
│ └── native-server/
│ ├── .npmignore
│ ├── README.md
│ ├── debug.sh
│ ├── install.md
│ ├── jest.config.js
│ ├── package.json
│ ├── src/
│ │ ├── agent/
│ │ │ ├── attachment-service.ts
│ │ │ ├── ccr-detector.ts
│ │ │ ├── chat-service.ts
│ │ │ ├── db/
│ │ │ │ ├── client.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── schema.ts
│ │ │ ├── directory-picker.ts
│ │ │ ├── engines/
│ │ │ │ ├── claude.ts
│ │ │ │ ├── codex.ts
│ │ │ │ └── types.ts
│ │ │ ├── message-service.ts
│ │ │ ├── open-project.ts
│ │ │ ├── project-service.ts
│ │ │ ├── project-types.ts
│ │ │ ├── session-service.ts
│ │ │ ├── storage.ts
│ │ │ ├── stream-manager.ts
│ │ │ ├── tool-bridge.ts
│ │ │ └── types.ts
│ │ ├── cli.ts
│ │ ├── constant/
│ │ │ └── index.ts
│ │ ├── file-handler.ts
│ │ ├── index.ts
│ │ ├── mcp/
│ │ │ ├── mcp-server-stdio.ts
│ │ │ ├── mcp-server.ts
│ │ │ ├── register-tools.ts
│ │ │ └── stdio-config.json
│ │ ├── native-messaging-host.ts
│ │ ├── scripts/
│ │ │ ├── browser-config.ts
│ │ │ ├── build.ts
│ │ │ ├── constant.ts
│ │ │ ├── doctor.ts
│ │ │ ├── postinstall.ts
│ │ │ ├── register-dev.ts
│ │ │ ├── register.ts
│ │ │ ├── report.ts
│ │ │ ├── run_host.bat
│ │ │ ├── run_host.sh
│ │ │ └── utils.ts
│ │ ├── server/
│ │ │ ├── index.ts
│ │ │ ├── routes/
│ │ │ │ ├── agent.ts
│ │ │ │ └── index.ts
│ │ │ └── server.test.ts
│ │ ├── shims/
│ │ │ └── devtools.d.ts
│ │ ├── trace-analyzer.ts
│ │ ├── types/
│ │ │ └── devtools-frontend.d.ts
│ │ └── util/
│ │ └── logger.ts
│ └── tsconfig.json
├── commitlint.config.cjs
├── docs/
│ ├── ARCHITECTURE.md
│ ├── ARCHITECTURE_zh.md
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── CONTRIBUTING_zh.md
│ ├── ISSUE.md
│ ├── TOOLS.md
│ ├── TOOLS_zh.md
│ ├── TROUBLESHOOTING.md
│ ├── TROUBLESHOOTING_zh.md
│ ├── VisualEditor.md
│ ├── VisualEditor_zh.md
│ ├── WINDOWS_INSTALL_zh.md
│ └── mcp-cli-config.md
├── eslint.config.js
├── package.json
├── packages/
│ ├── shared/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── agent-types.ts
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ ├── labels.ts
│ │ │ ├── node-spec-registry.ts
│ │ │ ├── node-spec.ts
│ │ │ ├── node-specs-builtin.ts
│ │ │ ├── rr-graph.ts
│ │ │ ├── step-types.ts
│ │ │ ├── tools.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ └── wasm-simd/
│ ├── .gitignore
│ ├── BUILD.md
│ ├── Cargo.toml
│ ├── README.md
│ ├── package.json
│ └── src/
│ └── lib.rs
├── pnpm-workspace.yaml
├── prompt/
│ ├── content-analize.md
│ ├── excalidraw-prompt.md
│ └── modify-web.md
└── releases/
└── README.md
Showing preview only (450K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4919 symbols across 389 files)
FILE: app/chrome-extension/common/agent-models.ts
type ModelDefinition (line 14) | interface ModelDefinition {
type AgentCliType (line 23) | type AgentCliType = 'claude' | 'codex' | 'cursor' | 'qwen' | 'glm';
constant CLAUDE_MODELS (line 29) | const CLAUDE_MODELS: ModelDefinition[] = [
constant CLAUDE_DEFAULT_MODEL (line 50) | const CLAUDE_DEFAULT_MODEL = 'claude-sonnet-4-5-20250929';
constant CODEX_STANDARD_EFFORTS (line 57) | const CODEX_STANDARD_EFFORTS: readonly CodexReasoningEffort[] = ['low', ...
constant CODEX_EXTENDED_EFFORTS (line 59) | const CODEX_EXTENDED_EFFORTS: readonly CodexReasoningEffort[] = ['low', ...
constant CODEX_MODELS (line 61) | const CODEX_MODELS: ModelDefinition[] = [
constant CODEX_DEFAULT_MODEL (line 94) | const CODEX_DEFAULT_MODEL = 'gpt-5.1';
constant CODEX_ALIAS_MAP (line 97) | const CODEX_ALIAS_MAP: Record<string, string> = {
constant CODEX_KNOWN_IDS (line 104) | const CODEX_KNOWN_IDS = new Set(CODEX_MODELS.map((model) => model.id));
function normalizeCodexModelId (line 109) | function normalizeCodexModelId(model?: string | null): string {
function getCodexReasoningEfforts (line 140) | function getCodexReasoningEfforts(modelId?: string | null): readonly Cod...
function supportsXhighEffort (line 149) | function supportsXhighEffort(modelId?: string | null): boolean {
constant CURSOR_MODELS (line 158) | const CURSOR_MODELS: ModelDefinition[] = [
constant CURSOR_DEFAULT_MODEL (line 177) | const CURSOR_DEFAULT_MODEL = 'auto';
constant QWEN_MODELS (line 183) | const QWEN_MODELS: ModelDefinition[] = [
constant QWEN_DEFAULT_MODEL (line 201) | const QWEN_DEFAULT_MODEL = 'qwen3-coder-plus';
constant GLM_MODELS (line 207) | const GLM_MODELS: ModelDefinition[] = [
constant GLM_DEFAULT_MODEL (line 215) | const GLM_DEFAULT_MODEL = 'glm-4.6';
constant CLI_MODEL_DEFINITIONS (line 221) | const CLI_MODEL_DEFINITIONS: Record<AgentCliType, ModelDefinition[]> = {
constant CLI_DEFAULT_MODELS (line 229) | const CLI_DEFAULT_MODELS: Record<AgentCliType, string> = {
function getModelsForCli (line 244) | function getModelsForCli(cli: string | null | undefined): ModelDefinitio...
function getDefaultModelForCli (line 253) | function getDefaultModelForCli(cli: string | null | undefined): string {
function getModelDisplayName (line 262) | function getModelDisplayName(
FILE: app/chrome-extension/common/constants.ts
constant NATIVE_HOST (line 7) | const NATIVE_HOST = {
constant ICONS (line 13) | const ICONS = {
constant TIMEOUTS (line 18) | const TIMEOUTS = {
constant LIMITS (line 28) | const LIMITS = {
constant ERROR_MESSAGES (line 38) | const ERROR_MESSAGES = {
constant SUCCESS_MESSAGES (line 52) | const SUCCESS_MESSAGES = {
constant LINKS (line 60) | const LINKS = {
constant FILE_TYPES (line 65) | const FILE_TYPES = {
constant NETWORK_FILTERS (line 84) | const NETWORK_FILTERS = {
constant SEMANTIC_CONFIG (line 212) | const SEMANTIC_CONFIG = {
constant STORAGE_KEYS (line 221) | const STORAGE_KEYS = {
constant NOTIFICATIONS (line 241) | const NOTIFICATIONS = {
type ExecutionWorld (line 246) | enum ExecutionWorld {
FILE: app/chrome-extension/common/element-marker-types.ts
type UrlMatchType (line 3) | type UrlMatchType = 'exact' | 'prefix' | 'host';
type ElementMarker (line 5) | interface ElementMarker {
type UpsertMarkerRequest (line 25) | interface UpsertMarkerRequest {
type MarkerValidationAction (line 37) | enum MarkerValidationAction {
type MarkerValidationRequest (line 47) | interface MarkerValidationRequest {
type MarkerValidationResponse (line 72) | interface MarkerValidationResponse {
type MarkerQuery (line 81) | interface MarkerQuery {
FILE: app/chrome-extension/common/message-types.ts
type MessageTarget (line 9) | enum MessageTarget {
constant BACKGROUND_MESSAGE_TYPES (line 16) | const BACKGROUND_MESSAGE_TYPES = {
constant OFFSCREEN_MESSAGE_TYPES (line 94) | const OFFSCREEN_MESSAGE_TYPES = {
constant CONTENT_MESSAGE_TYPES (line 106) | const CONTENT_MESSAGE_TYPES = {
constant TOOL_MESSAGE_TYPES (line 121) | const TOOL_MESSAGE_TYPES = {
type BackgroundMessageType (line 182) | type BackgroundMessageType =
type OffscreenMessageType (line 184) | type OffscreenMessageType =
type ContentMessageType (line 186) | type ContentMessageType = (typeof CONTENT_MESSAGE_TYPES)[keyof typeof CO...
type ToolMessageType (line 187) | type ToolMessageType = (typeof TOOL_MESSAGE_TYPES)[keyof typeof TOOL_MES...
type SendMessageType (line 190) | enum SendMessageType {
type QuickPanelAIContext (line 230) | interface QuickPanelAIContext {
type QuickPanelSendToAIPayload (line 245) | interface QuickPanelSendToAIPayload {
type QuickPanelSendToAIResponse (line 255) | type QuickPanelSendToAIResponse =
type QuickPanelSendToAIMessage (line 262) | interface QuickPanelSendToAIMessage {
type QuickPanelCancelAIPayload (line 270) | interface QuickPanelCancelAIPayload {
type QuickPanelCancelAIResponse (line 283) | type QuickPanelCancelAIResponse = { success: true } | { success: false; ...
type QuickPanelCancelAIMessage (line 288) | interface QuickPanelCancelAIMessage {
type QuickPanelAIEventMessage (line 297) | interface QuickPanelAIEventMessage {
type QuickPanelTabsQueryPayload (line 311) | interface QuickPanelTabsQueryPayload {
type QuickPanelTabSummary (line 322) | interface QuickPanelTabSummary {
type QuickPanelTabsQueryResponse (line 339) | type QuickPanelTabsQueryResponse =
type QuickPanelTabsQueryMessage (line 351) | interface QuickPanelTabsQueryMessage {
type QuickPanelActivateTabPayload (line 359) | interface QuickPanelActivateTabPayload {
type QuickPanelActivateTabResponse (line 367) | type QuickPanelActivateTabResponse = { success: true } | { success: fals...
type QuickPanelActivateTabMessage (line 372) | interface QuickPanelActivateTabMessage {
type QuickPanelCloseTabPayload (line 380) | interface QuickPanelCloseTabPayload {
type QuickPanelCloseTabResponse (line 387) | type QuickPanelCloseTabResponse = { success: true } | { success: false; ...
type QuickPanelCloseTabMessage (line 392) | interface QuickPanelCloseTabMessage {
FILE: app/chrome-extension/common/node-types.ts
constant NODE_TYPES (line 6) | const NODE_TYPES = {
type NodeTypeConst (line 14) | type NodeTypeConst = (typeof NODE_TYPES)[keyof typeof NODE_TYPES];
FILE: app/chrome-extension/common/rr-v3-keepalive-protocol.ts
constant RR_V3_KEEPALIVE_PORT_NAME (line 7) | const RR_V3_KEEPALIVE_PORT_NAME = 'rr_v3_keepalive' as const;
type KeepaliveMessageType (line 10) | type KeepaliveMessageType =
type KeepaliveMessage (line 17) | interface KeepaliveMessage {
constant DEFAULT_KEEPALIVE_PING_INTERVAL_MS (line 23) | const DEFAULT_KEEPALIVE_PING_INTERVAL_MS = 20_000;
constant MAX_KEEPALIVE_PING_INTERVAL_MS (line 26) | const MAX_KEEPALIVE_PING_INTERVAL_MS = 25_000;
FILE: app/chrome-extension/common/step-types.ts
type StepTypeConst (line 3) | type StepTypeConst =
FILE: app/chrome-extension/common/tool-handler.ts
type ToolResult (line 3) | interface ToolResult extends CallToolResult {
type ToolExecutor (line 8) | interface ToolExecutor {
FILE: app/chrome-extension/common/web-editor-types.ts
type WebEditorState (line 15) | interface WebEditorState {
constant WEB_EDITOR_V2_ACTIONS (line 36) | const WEB_EDITOR_V2_ACTIONS = {
constant WEB_EDITOR_V1_ACTIONS (line 57) | const WEB_EDITOR_V1_ACTIONS = {
type WebEditorV2Action (line 65) | type WebEditorV2Action = (typeof WEB_EDITOR_V2_ACTIONS)[keyof typeof WEB...
type WebEditorV1Action (line 66) | type WebEditorV1Action = (typeof WEB_EDITOR_V1_ACTIONS)[keyof typeof WEB...
type WebEditorVersion (line 69) | type WebEditorVersion = 1 | 2;
type WebEditorV2PingRequest (line 72) | interface WebEditorV2PingRequest {
type WebEditorV2PingResponse (line 77) | interface WebEditorV2PingResponse {
type WebEditorV2ToggleRequest (line 84) | interface WebEditorV2ToggleRequest {
type WebEditorV2ToggleResponse (line 89) | interface WebEditorV2ToggleResponse {
type WebEditorV2StartRequest (line 94) | interface WebEditorV2StartRequest {
type WebEditorV2StartResponse (line 99) | interface WebEditorV2StartResponse {
type WebEditorV2StopRequest (line 104) | interface WebEditorV2StopRequest {
type WebEditorV2StopResponse (line 109) | interface WebEditorV2StopResponse {
type WebEditorV2Request (line 114) | type WebEditorV2Request =
type WebEditorV2Response (line 120) | type WebEditorV2Response =
type DebugSource (line 134) | interface DebugSource {
type ElementLocator (line 153) | interface ElementLocator {
type TransactionType (line 173) | type TransactionType = 'style' | 'text' | 'class' | 'move' | 'structure';
type TransactionSnapshot (line 179) | interface TransactionSnapshot {
type MoveOperationData (line 196) | interface MoveOperationData {
type MoveTransactionData (line 211) | interface MoveTransactionData {
type StructureOperationData (line 222) | interface StructureOperationData {
type Transaction (line 245) | interface Transaction {
type WebEditorElementKey (line 277) | type WebEditorElementKey = string;
type NetEffectPayload (line 283) | interface NetEffectPayload {
type ElementChangeType (line 310) | type ElementChangeType = 'style' | 'text' | 'class' | 'mixed';
type ElementChangeSummary (line 316) | interface ElementChangeSummary {
type WebEditorTxChangeAction (line 363) | type WebEditorTxChangeAction = 'push' | 'merge' | 'undo' | 'redo' | 'cle...
type WebEditorTxChangedPayload (line 369) | interface WebEditorTxChangedPayload {
type WebEditorApplyBatchPayload (line 389) | interface WebEditorApplyBatchPayload {
type WebEditorHighlightElementPayload (line 403) | interface WebEditorHighlightElementPayload {
type WebEditorRevertElementPayload (line 418) | interface WebEditorRevertElementPayload {
type WebEditorRevertElementResponse (line 428) | interface WebEditorRevertElementResponse {
type SelectedElementSummary (line 449) | interface SelectedElementSummary {
type WebEditorSelectionChangedPayload (line 468) | interface WebEditorSelectionChangedPayload {
type WebEditorCancelExecutionPayload (line 485) | interface WebEditorCancelExecutionPayload {
type WebEditorCancelExecutionResponse (line 495) | interface WebEditorCancelExecutionResponse {
type WebEditorV2Api (line 510) | interface WebEditorV2Api {
type Window (line 536) | interface Window {
FILE: app/chrome-extension/entrypoints/background/element-marker/element-marker-storage.ts
constant DB_NAME (line 7) | const DB_NAME = 'element_marker_storage';
constant DB_VERSION (line 8) | const DB_VERSION = 1;
constant STORE (line 9) | const STORE = 'markers';
function normalizeUrl (line 23) | function normalizeUrl(raw: string): { url: string; origin: string; host:...
function now (line 32) | function now(): number {
function listAllMarkers (line 36) | async function listAllMarkers(): Promise<ElementMarker[]> {
function listMarkersForUrl (line 40) | async function listMarkersForUrl(url: string): Promise<ElementMarker[]> {
function saveMarker (line 56) | async function saveMarker(req: UpsertMarkerRequest): Promise<ElementMark...
function updateMarker (line 80) | async function updateMarker(marker: ElementMarker): Promise<void> {
function deleteMarker (line 93) | async function deleteMarker(id: string): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/element-marker/index.ts
constant CONTEXT_MENU_ID (line 19) | const CONTEXT_MENU_ID = 'element_marker_mark';
function extractToolError (line 24) | function extractToolError(result: any): string | undefined {
function ensureContextMenu (line 47) | async function ensureContextMenu() {
function isMarkerInjected (line 69) | async function isMarkerInjected(tabId: number): Promise<boolean> {
function injectMarkerHelper (line 84) | async function injectMarkerHelper(tabId: number) {
function initElementMarkerListeners (line 108) | function initElementMarkerListeners() {
FILE: app/chrome-extension/entrypoints/background/index.ts
constant ENABLE_RR_V3 (line 22) | const ENABLE_RR_V3 = true;
FILE: app/chrome-extension/entrypoints/background/keepalive-manager.ts
constant LOG_PREFIX (line 16) | const LOG_PREFIX = '[KeepaliveManager]';
function getController (line 27) | function getController(): KeepaliveController {
function acquireKeepalive (line 48) | function acquireKeepalive(tag: string): () => void {
function isKeepaliveActive (line 69) | function isKeepaliveActive(): boolean {
function getKeepaliveRefCount (line 81) | function getKeepaliveRefCount(): number {
FILE: app/chrome-extension/entrypoints/background/native-host.ts
constant LOG_PREFIX (line 8) | const LOG_PREFIX = '[NativeHost]';
constant HOST_NAME (line 11) | const HOST_NAME = NATIVE_HOST.NAME;
constant RECONNECT_BASE_DELAY_MS (line 15) | const RECONNECT_BASE_DELAY_MS = 500;
constant RECONNECT_MAX_DELAY_MS (line 16) | const RECONNECT_MAX_DELAY_MS = 60_000;
constant RECONNECT_MAX_FAST_ATTEMPTS (line 17) | const RECONNECT_MAX_FAST_ATTEMPTS = 8;
constant RECONNECT_COOLDOWN_DELAY_MS (line 18) | const RECONNECT_COOLDOWN_DELAY_MS = 5 * 60_000;
type ServerStatus (line 33) | interface ServerStatus {
function saveServerStatus (line 47) | async function saveServerStatus(status: ServerStatus): Promise<void> {
function loadServerStatus (line 58) | async function loadServerStatus(): Promise<ServerStatus> {
function broadcastServerStatusChange (line 76) | function broadcastServerStatusChange(status: ServerStatus): void {
function normalizePort (line 92) | function normalizePort(value: unknown): number | null {
function withJitter (line 106) | function withJitter(ms: number): number {
function getReconnectDelayMs (line 115) | function getReconnectDelayMs(attempt: number): number {
function clearReconnectTimer (line 126) | function clearReconnectTimer(): void {
function resetReconnectState (line 135) | function resetReconnectState(): void {
function syncKeepaliveHold (line 146) | function syncKeepaliveHold(): void {
function loadNativeAutoConnectEnabled (line 170) | async function loadNativeAutoConnectEnabled(): Promise<boolean> {
function setNativeAutoConnectEnabled (line 184) | async function setNativeAutoConnectEnabled(enabled: boolean): Promise<vo...
function getPreferredPort (line 202) | async function getPreferredPort(override?: unknown): Promise<number> {
function scheduleReconnect (line 233) | function scheduleReconnect(reason: string): void {
function markServerStopped (line 259) | async function markServerStopped(reason: string): Promise<void> {
function ensureNativeConnected (line 284) | async function ensureNativeConnected(trigger: string, portOverride?: unk...
function connectNativeHost (line 338) | function connectNativeHost(port: number = NATIVE_HOST.DEFAULT_PORT): boo...
FILE: app/chrome-extension/entrypoints/background/quick-panel/agent-handler.ts
constant LOG_PREFIX (line 36) | const LOG_PREFIX = '[QuickPanelAgent]';
constant KEEPALIVE_TAG (line 37) | const KEEPALIVE_TAG = 'quick-panel-ai';
constant STORAGE_KEY_SELECTED_SESSION (line 40) | const STORAGE_KEY_SELECTED_SESSION = 'agent-selected-session-id';
constant SSE_CONNECT_TIMEOUT_MS (line 43) | const SSE_CONNECT_TIMEOUT_MS = 3000;
constant REQUEST_TIMEOUT_MS (line 46) | const REQUEST_TIMEOUT_MS = 15 * 60 * 1000;
constant SSE_CONNECTED (line 49) | const SSE_CONNECTED = Symbol('SSE_CONNECTED');
constant SSE_TIMEOUT (line 52) | const SSE_TIMEOUT = Symbol('SSE_TIMEOUT');
type ActiveRequest (line 66) | interface ActiveRequest {
function normalizeString (line 94) | function normalizeString(value: unknown): string {
function normalizePort (line 98) | function normalizePort(value: unknown): number | null {
function createRequestId (line 110) | function createRequestId(): string {
function sleep (line 121) | function sleep(ms: number): Promise<void> {
function isTerminalStatus (line 125) | function isTerminalStatus(status: string): boolean {
function createErrorEvent (line 133) | function createErrorEvent(sessionId: string, requestId: string, error: s...
function createCancelledStatusEvent (line 141) | function createCancelledStatusEvent(
function forwardEventToQuickPanel (line 165) | function forwardEventToQuickPanel(request: ActiveRequest, event: Realtim...
function cleanupRequest (line 203) | function cleanupRequest(requestId: string, reason: string): void {
function validateSession (line 241) | async function validateSession(port: number, sessionId: string): Promise...
function shouldForwardEvent (line 261) | function shouldForwardEvent(event: RealtimeEvent, requestId: string): bo...
type SseSubscription (line 284) | interface SseSubscription {
function createSseSubscription (line 307) | function createSseSubscription(request: ActiveRequest): SseSubscription {
function postActRequest (line 417) | async function postActRequest(request: ActiveRequest): Promise<void> {
function cancelRequestOnServer (line 449) | async function cancelRequestOnServer(
function isRequestStillActive (line 470) | function isRequestStillActive(request: ActiveRequest): boolean {
function startRequest (line 488) | async function startRequest(request: ActiveRequest): Promise<void> {
function handleSendToAI (line 577) | async function handleSendToAI(
function handleCancelAI (line 660) | async function handleCancelAI(
function initQuickPanelAgentHandler (line 738) | function initQuickPanelAgentHandler(): void {
FILE: app/chrome-extension/entrypoints/background/quick-panel/commands.ts
constant COMMAND_KEY (line 13) | const COMMAND_KEY = 'toggle_quick_panel';
constant LOG_PREFIX (line 14) | const LOG_PREFIX = '[QuickPanelCommands]';
function getActiveTabId (line 23) | async function getActiveTabId(): Promise<number | null> {
function isValidTabUrl (line 36) | function isValidTabUrl(url?: string): boolean {
function toggleQuickPanelInActiveTab (line 62) | async function toggleQuickPanelInActiveTab(): Promise<void> {
function initQuickPanelCommands (line 105) | function initQuickPanelCommands(): void {
FILE: app/chrome-extension/entrypoints/background/quick-panel/tabs-handler.ts
constant LOG_PREFIX (line 27) | const LOG_PREFIX = '[QuickPanelTabs]';
function isValidTabId (line 33) | function isValidTabId(value: unknown): value is number {
function isValidWindowId (line 37) | function isValidWindowId(value: unknown): value is number {
function normalizeBoolean (line 41) | function normalizeBoolean(value: unknown): boolean {
function getLastAccessed (line 45) | function getLastAccessed(tab: chrome.tabs.Tab): number | undefined {
function safeErrorMessage (line 51) | function safeErrorMessage(err: unknown): string {
function toTabSummary (line 62) | function toTabSummary(tab: chrome.tabs.Tab): QuickPanelTabSummary | null {
function handleTabsQuery (line 87) | async function handleTabsQuery(
function handleActivateTab (line 138) | async function handleActivateTab(
function handleCloseTab (line 171) | async function handleCloseTab(
function initQuickPanelTabsHandler (line 203) | function initQuickPanelTabsHandler(): void {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/bootstrap.ts
type Logger (line 56) | type Logger = Pick<Console, 'debug' | 'info' | 'warn' | 'error'>;
type V3Runtime (line 61) | interface V3Runtime {
function errorMessage (line 81) | function errorMessage(err: unknown): string {
function isFiniteNumber (line 88) | function isFiniteNumber(v: unknown): v is number {
function tabExists (line 92) | async function tabExists(tabId: number): Promise<boolean> {
function createEphemeralTab (line 101) | async function createEphemeralTab(logger: Logger): Promise<number> {
function safeRemoveTab (line 110) | async function safeRemoveTab(tabId: number, logger: Logger): Promise<voi...
function resolveRunTab (line 122) | async function resolveRunTab(input: {
function failRun (line 146) | async function failRun(
function createDefaultRunExecutor (line 191) | function createDefaultRunExecutor(deps: {
function bootstrapV3 (line 280) | async function bootstrapV3(): Promise<V3Runtime> {
function getV3Runtime (line 460) | function getV3Runtime(): V3Runtime | null {
function isV3Running (line 467) | function isV3Running(): boolean {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/debug.ts
type Breakpoint (line 13) | interface Breakpoint {
type DebuggerState (line 24) | interface DebuggerState {
type DebuggerCommand (line 45) | type DebuggerCommand =
type DebuggerCommandType (line 68) | type DebuggerCommandType = DebuggerCommand['type'];
type DebuggerResponse (line 73) | type DebuggerResponse =
function createInitialDebuggerState (line 80) | function createInitialDebuggerState(runId: RunId): DebuggerState {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/errors.ts
constant RR_ERROR_CODES (line 9) | const RR_ERROR_CODES = {
type RRErrorCode (line 58) | type RRErrorCode = (typeof RR_ERROR_CODES)[keyof typeof RR_ERROR_CODES];
type RRError (line 64) | interface RRError {
function createRRError (line 80) | function createRRError(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/events.ts
type Unsubscribe (line 12) | type Unsubscribe = () => void;
type RunStatus (line 15) | type RunStatus = 'queued' | 'running' | 'paused' | 'succeeded' | 'failed...
type EventBase (line 21) | interface EventBase {
type PauseReason (line 34) | type PauseReason =
type RecoveryReason (line 41) | type RecoveryReason = 'sw_restart' | 'lease_expired';
type RunEvent (line 47) | type RunEvent =
type RunEventType (line 100) | type RunEventType = RunEvent['type'];
type DistributiveOmit (line 105) | type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T...
type RunEventInput (line 112) | type RunEventInput = DistributiveOmit<RunEvent, 'seq' | 'ts'> & {
constant RUN_SCHEMA_VERSION (line 117) | const RUN_SCHEMA_VERSION = 3 as const;
type RunRecordV3 (line 123) | interface RunRecordV3 {
function isTerminalStatus (line 176) | function isTerminalStatus(status: RunStatus): boolean {
function isActiveStatus (line 183) | function isActiveStatus(status: RunStatus): boolean {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/flow.ts
constant FLOW_SCHEMA_VERSION (line 12) | const FLOW_SCHEMA_VERSION = 3 as const;
type EdgeV3 (line 18) | interface EdgeV3 {
type NodeKind (line 30) | type NodeKind = string;
type NodeV3 (line 36) | interface NodeV3 {
type FlowBinding (line 57) | interface FlowBinding {
type FlowV3 (line 66) | interface FlowV3 {
function findNodeById (line 103) | function findNodeById(flow: FlowV3, nodeId: NodeId): NodeV3 | undefined {
function findEdgesFrom (line 110) | function findEdgesFrom(flow: FlowV3, nodeId: NodeId): EdgeV3[] {
function findEdgesTo (line 117) | function findEdgesTo(flow: FlowV3, nodeId: NodeId): EdgeV3[] {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/ids.ts
type FlowId (line 7) | type FlowId = string;
type NodeId (line 10) | type NodeId = string;
type EdgeId (line 13) | type EdgeId = string;
type RunId (line 16) | type RunId = string;
type TriggerId (line 19) | type TriggerId = string;
type EdgeLabel (line 22) | type EdgeLabel = string;
constant EDGE_LABELS (line 25) | const EDGE_LABELS = {
type EdgeLabelValue (line 37) | type EdgeLabelValue = (typeof EDGE_LABELS)[keyof typeof EDGE_LABELS];
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/json.ts
type JsonPrimitive (line 7) | type JsonPrimitive = string | number | boolean | null;
type JsonObject (line 10) | interface JsonObject {
type JsonArray (line 15) | type JsonArray = JsonValue[];
type JsonValue (line 18) | type JsonValue = JsonPrimitive | JsonObject | JsonArray;
type ISODateTimeString (line 21) | type ISODateTimeString = string;
type UnixMillis (line 24) | type UnixMillis = number;
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/policy.ts
type TimeoutPolicy (line 14) | interface TimeoutPolicy {
type RetryPolicy (line 25) | interface RetryPolicy {
type OnErrorPolicy (line 44) | type OnErrorPolicy =
type ArtifactPolicy (line 57) | interface ArtifactPolicy {
type NodePolicy (line 72) | interface NodePolicy {
type FlowPolicy (line 87) | interface FlowPolicy {
function mergeNodePolicy (line 100) | function mergeNodePolicy(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/triggers.ts
type TriggerKind (line 10) | type TriggerKind =
type TriggerSpecBase (line 23) | interface TriggerSpecBase {
type UrlMatchRule (line 39) | interface UrlMatchRule {
type TriggerSpec (line 47) | type TriggerSpec =
type TriggerFireContext (line 104) | interface TriggerFireContext {
type TriggerSpecByKind (line 120) | type TriggerSpecByKind<K extends TriggerKind> = Extract<TriggerSpec, { k...
function isTriggerEnabled (line 125) | function isTriggerEnabled(trigger: TriggerSpec): boolean {
function createTriggerFireContext (line 132) | function createTriggerFireContext(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/domain/variables.ts
type VariableName (line 9) | type VariableName = string;
type PersistentVariableName (line 12) | type PersistentVariableName = `$${string}`;
type VariableScope (line 15) | type VariableScope = 'run' | 'flow' | 'persistent';
type VariablePointer (line 21) | interface VariablePointer {
type VariableDefinition (line 34) | interface VariableDefinition {
type PersistentVarRecord (line 55) | interface PersistentVarRecord {
function isPersistentVariable (line 69) | function isPersistentVariable(name: string): name is PersistentVariableN...
function parseVariablePointer (line 77) | function parseVariablePointer(ref: string): VariablePointer | null {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/keepalive/offscreen-keepalive.ts
constant KEEPALIVE_CONTROL_MESSAGE_TYPE (line 23) | const KEEPALIVE_CONTROL_MESSAGE_TYPE = 'rr_v3_keepalive.control' as const;
type KeepaliveControlCommand (line 25) | type KeepaliveControlCommand = 'start' | 'stop';
type KeepaliveControlMessage (line 27) | interface KeepaliveControlMessage {
type KeepaliveController (line 38) | interface KeepaliveController {
type OffscreenKeepaliveOptions (line 59) | interface OffscreenKeepaliveOptions {
function createOffscreenKeepaliveController (line 70) | function createOffscreenKeepaliveController(
function createNotImplementedKeepaliveController (line 80) | function createNotImplementedKeepaliveController(): KeepaliveController {
class OffscreenKeepaliveControllerImpl (line 97) | class OffscreenKeepaliveControllerImpl implements KeepaliveController {
method constructor (line 109) | constructor(options: OffscreenKeepaliveOptions) {
method acquire (line 116) | acquire(tag: string): () => void {
method isActive (line 154) | isActive(): boolean {
method getRefCount (line 158) | getRefCount(): number {
method releaseAll (line 162) | releaseAll(): void {
method getRefsByTag (line 175) | getRefsByTag(): Record<string, number> {
method scheduleSync (line 185) | private scheduleSync(): void {
method syncOnce (line 199) | private async syncOnce(): Promise<void> {
method teardown (line 229) | private async teardown(): Promise<void> {
method ensureConnectionListener (line 238) | private ensureConnectionListener(): void {
method disconnectPort (line 296) | private disconnectPort(): void {
method sendStartCommand (line 314) | private sendStartCommand(): void {
method sendStopCommand (line 333) | private sendStopCommand(): void {
method sendPong (line 352) | private sendPong(): void {
method sendRuntimeControl (line 371) | private async sendRuntimeControl(command: KeepaliveControlCommand): Pr...
class InMemoryKeepaliveController (line 407) | class InMemoryKeepaliveController implements KeepaliveController {
method acquire (line 410) | acquire(tag: string): () => void {
method isActive (line 428) | isActive(): boolean {
method getRefCount (line 432) | getRefCount(): number {
method releaseAll (line 440) | releaseAll(): void {
method getRefsByTag (line 448) | getRefsByTag(): Record<string, number> {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/artifacts.ts
type ScreenshotResult (line 13) | type ScreenshotResult = { ok: true; base64: string } | { ok: false; erro...
type ArtifactService (line 19) | interface ArtifactService {
function createNotImplementedArtifactService (line 52) | function createNotImplementedArtifactService(): ArtifactService {
function createChromeArtifactService (line 71) | function createChromeArtifactService(): ArtifactService {
type ArtifactPolicyExecutor (line 139) | interface ArtifactPolicyExecutor {
function createArtifactPolicyExecutor (line 160) | function createArtifactPolicyExecutor(service: ArtifactService): Artifac...
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/breakpoints.ts
class BreakpointManager (line 13) | class BreakpointManager {
method constructor (line 17) | constructor(initialBreakpoints?: NodeId[]) {
method add (line 28) | add(nodeId: NodeId): void {
method remove (line 35) | remove(nodeId: NodeId): void {
method setAll (line 42) | setAll(nodeIds: NodeId[]): void {
method enable (line 52) | enable(nodeId: NodeId): void {
method disable (line 62) | disable(nodeId: NodeId): void {
method hasBreakpoint (line 72) | hasBreakpoint(nodeId: NodeId): boolean {
method shouldPauseAt (line 81) | shouldPauseAt(nodeId: NodeId): boolean {
method getAll (line 93) | getAll(): Breakpoint[] {
method getEnabled (line 100) | getEnabled(): Breakpoint[] {
method setStepMode (line 107) | setStepMode(mode: 'none' | 'stepOver'): void {
method getStepMode (line 114) | getStepMode(): 'none' | 'stepOver' {
method clear (line 121) | clear(): void {
class BreakpointRegistry (line 131) | class BreakpointRegistry {
method getOrCreate (line 137) | getOrCreate(runId: RunId, initialBreakpoints?: NodeId[]): BreakpointMa...
method get (line 149) | get(runId: RunId): BreakpointManager | undefined {
method remove (line 156) | remove(runId: RunId): void {
method clear (line 163) | clear(): void {
function getBreakpointRegistry (line 174) | function getBreakpointRegistry(): BreakpointRegistry {
function resetBreakpointRegistry (line 185) | function resetBreakpointRegistry(): void {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/debug-controller.ts
type RunnerRegistry (line 25) | interface RunnerRegistry {
function createRunnerRegistry (line 35) | function createRunnerRegistry(): RunnerRegistry {
type DebugSession (line 48) | interface DebugSession {
type DebugStateListener (line 59) | type DebugStateListener = (state: DebuggerState) => void;
type DebugControllerConfig (line 64) | interface DebugControllerConfig {
class DebugController (line 74) | class DebugController {
method constructor (line 83) | constructor(config: DebugControllerConfig) {
method start (line 92) | start(): void {
method stop (line 102) | stop(): void {
method handle (line 114) | async handle(cmd: DebuggerCommand): Promise<DebuggerResponse> {
method subscribe (line 162) | subscribe(listener: DebugStateListener, filter?: { runId?: RunId }): U...
method getState (line 182) | async getState(runId: RunId): Promise<DebuggerState> {
method handleAttach (line 202) | private async handleAttach(runId: RunId): Promise<DebuggerResponse> {
method handleDetach (line 230) | private async handleDetach(runId: RunId): Promise<DebuggerResponse> {
method handlePause (line 241) | private async handlePause(runId: RunId): Promise<DebuggerResponse> {
method handleResume (line 252) | private async handleResume(runId: RunId): Promise<DebuggerResponse> {
method handleStepOver (line 263) | private async handleStepOver(runId: RunId): Promise<DebuggerResponse> {
method handleSetBreakpoints (line 280) | private async handleSetBreakpoints(runId: RunId, nodeIds: NodeId[]): P...
method handleAddBreakpoint (line 292) | private async handleAddBreakpoint(runId: RunId, nodeId: NodeId): Promi...
method handleRemoveBreakpoint (line 303) | private async handleRemoveBreakpoint(runId: RunId, nodeId: NodeId): Pr...
method handleGetState (line 314) | private async handleGetState(runId: RunId): Promise<DebuggerResponse> {
method handleGetVar (line 319) | private async handleGetVar(runId: RunId, name: string): Promise<Debugg...
method handleSetVar (line 332) | private async handleSetVar(
method handleEvent (line 351) | private handleEvent(event: RunEvent): void {
method persistBreakpoints (line 407) | private async persistBreakpoints(runId: RunId, bpManager: BreakpointMa...
method reconstructVar (line 418) | private async reconstructVar(runId: RunId, name: string): Promise<Json...
method notifyStateChange (line 451) | private notifyStateChange(runId: RunId, state: DebuggerState): void {
function createDebugController (line 481) | function createDebugController(config: DebugControllerConfig): DebugCont...
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/kernel.ts
type RunStartRequest (line 16) | interface RunStartRequest {
type RunResult (line 36) | interface RunResult {
type RunStatusInfo (line 52) | interface RunStatusInfo {
type ExecutionKernel (line 69) | interface ExecutionKernel {
function createNotImplementedKernel (line 131) | function createNotImplementedKernel(): ExecutionKernel {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/recovery-kernel.ts
type RecoveryEnabledKernelDeps (line 25) | interface RecoveryEnabledKernelDeps {
function createRecoveryEnabledKernel (line 46) | function createRecoveryEnabledKernel(deps: RecoveryEnabledKernelDeps): E...
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/runner.ts
type RunnerRuntimeState (line 40) | interface RunnerRuntimeState {
type RunnerConfig (line 58) | interface RunnerConfig {
type RunRunner (line 74) | interface RunRunner {
type RunRunnerFactory (line 98) | interface RunRunnerFactory {
type RunRunnerFactoryDeps (line 105) | interface RunRunnerFactoryDeps {
type Deferred (line 115) | interface Deferred<T> {
function createDeferred (line 121) | function createDeferred<T>(): Deferred<T> {
function sleep (line 131) | function sleep(ms: number): Promise<void> {
function errorMessage (line 135) | function errorMessage(err: unknown): string {
function withTimeout (line 142) | async function withTimeout<T>(
function computeRetryDelayMs (line 166) | function computeRetryDelayMs(policy: RetryPolicy, attempt: number): numb...
function applyVarsPatch (line 188) | function applyVarsPatch(vars: Record<string, JsonValue>, patch: VarsPatc...
function toRRError (line 198) | function toRRError(err: unknown, fallback: { code: string; message: stri...
class SerialQueue (line 212) | class SerialQueue {
method run (line 215) | run<T>(fn: () => Promise<T>): Promise<T> {
function createNotImplementedRunnerFactory (line 230) | function createNotImplementedRunnerFactory(): RunRunnerFactory {
function createRunRunnerFactory (line 241) | function createRunRunnerFactory(deps: RunRunnerFactoryDeps): RunRunnerFa...
type RunnerEnv (line 260) | interface RunnerEnv {
type OnErrorDecision (line 268) | type OnErrorDecision =
type NodeRunResult (line 277) | type NodeRunResult =
class StorageBackedRunRunner (line 285) | class StorageBackedRunRunner implements RunRunner {
method constructor (line 299) | constructor(runId: RunId, config: RunnerConfig, env: RunnerEnv) {
method onEvent (line 316) | onEvent(listener: (event: RunEvent) => void): Unsubscribe {
method start (line 320) | start(): Promise<RunResult> {
method pause (line 327) | pause(): void {
method resume (line 331) | resume(): void {
method cancel (line 347) | cancel(reason?: string): void {
method getVar (line 359) | getVar(name: string): JsonValue | undefined {
method setVar (line 363) | setVar(name: string, value: JsonValue): void {
method buildInitialVars (line 380) | private buildInitialVars(): Record<string, JsonValue> {
method requestPause (line 390) | private requestPause(reason: PauseReason): void {
method waitIfPaused (line 418) | private async waitIfPaused(): Promise<void> {
method ensureRunRecord (line 427) | private async ensureRunRecord(startNodeId: NodeId, startedAt: number):...
method run (line 472) | private async run(): Promise<RunResult> {
method runNode (line 594) | private async runNode(flow: FlowV3, node: NodeV3, nodeStartAt: number)...
method resolveNodePolicy (line 708) | private resolveNodePolicy(flow: FlowV3, node: NodeV3): NodePolicy {
method decideOnError (line 716) | private decideOnError(
method executeNodeAttempt (line 752) | private async executeNodeAttempt(flow: FlowV3, node: NodeV3): Promise<...
method finishSucceeded (line 831) | private async finishSucceeded(startedAt: number): Promise<RunResult> {
method finishFailed (line 851) | private async finishFailed(
method finishCanceled (line 876) | private async finishCanceled(startedAt: number): Promise<RunResult> {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/traversal.ts
type ValidateFlowDAGResult (line 14) | type ValidateFlowDAGResult = { ok: true } | { ok: false; errors: RRError...
function validateFlowDAG (line 21) | function validateFlowDAG(flow: FlowV3): ValidateFlowDAGResult {
function detectCycle (line 71) | function detectCycle(flow: FlowV3): NodeId[] | null {
function findNextNode (line 120) | function findNextNode(
function findEdgeByLabel (line 158) | function findEdgeByLabel(
function getOutEdges (line 169) | function getOutEdges(flow: FlowV3, nodeId: NodeId): EdgeV3[] {
function getInEdges (line 176) | function getInEdges(flow: FlowV3, nodeId: NodeId): EdgeV3[] {
function buildAdjacencyMap (line 183) | function buildAdjacencyMap(flow: FlowV3): Map<NodeId, NodeId[]> {
function getReachableNodes (line 203) | function getReachableNodes(flow: FlowV3): Set<NodeId> {
function isNodeReachable (line 224) | function isNodeReachable(flow: FlowV3, nodeId: NodeId): boolean {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/register-v2-replay-nodes.ts
type RegisterV2ReplayNodesOptions (line 20) | interface RegisterV2ReplayNodesOptions extends V2ActionNodeAdapterOptions {
function registerV2ReplayNodesAsV3Nodes (line 49) | function registerV2ReplayNodesAsV3Nodes(
function listV2ActionTypes (line 81) | function listV2ActionTypes(): string[] {
constant DEFAULT_V2_EXCLUDE_LIST (line 90) | const DEFAULT_V2_EXCLUDE_LIST = ['foreach', 'while'] as const;
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/registry.ts
class PluginRegistry (line 20) | class PluginRegistry implements PluginRegistrationContext {
method registerNode (line 28) | registerNode(def: NodeDefinition): void {
method registerTrigger (line 36) | registerTrigger(def: TriggerDefinition): void {
method getNode (line 44) | getNode(kind: NodeKind): NodeDefinition | undefined {
method getNodeOrThrow (line 52) | getNodeOrThrow(kind: NodeKind): NodeDefinition {
method getTrigger (line 64) | getTrigger(kind: TriggerKind): TriggerDefinition | undefined {
method getTriggerOrThrow (line 72) | getTriggerOrThrow(kind: TriggerKind): TriggerDefinition {
method hasNode (line 86) | hasNode(kind: NodeKind): boolean {
method hasTrigger (line 93) | hasTrigger(kind: TriggerKind): boolean {
method listNodeKinds (line 100) | listNodeKinds(): NodeKind[] {
method listTriggerKinds (line 107) | listTriggerKinds(): TriggerKind[] {
method registerPlugin (line 115) | registerPlugin(plugin: RRPlugin): void {
method registerPlugins (line 122) | registerPlugins(plugins: RRPlugin[]): void {
method clear (line 132) | clear(): void {
function getPluginRegistry (line 144) | function getPluginRegistry(): PluginRegistry {
function resetPluginRegistry (line 155) | function resetPluginRegistry(): void {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/types.ts
type Schema (line 20) | type Schema<T> = z.ZodType<T, z.ZodTypeDef, unknown>;
type NodeExecutionContext (line 26) | interface NodeExecutionContext {
type VarsPatchOp (line 77) | interface VarsPatchOp {
type NodeExecutionResult (line 86) | type NodeExecutionResult =
type NodeDefinition (line 102) | interface NodeDefinition<
type TriggerInstallContext (line 126) | interface TriggerInstallContext<
type TriggerDefinition (line 148) | interface TriggerDefinition<
type PluginRegistrationContext (line 165) | interface PluginRegistrationContext {
type RRPlugin (line 176) | interface RRPlugin {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/v2-action-adapter.ts
constant DEFAULT_TAB_ID_VAR (line 40) | const DEFAULT_TAB_ID_VAR = '__rr_v2__tabId';
constant DEFAULT_FRAME_ID_VAR (line 41) | const DEFAULT_FRAME_ID_VAR = '__rr_v2__frameId';
type V2ActionNodeAdapterOptions (line 43) | interface V2ActionNodeAdapterOptions {
function toErrorMessage (line 68) | function toErrorMessage(e: unknown): string {
function deepClone (line 75) | function deepClone<T>(value: T): T {
function safeJsonValue (line 81) | function safeJsonValue(value: unknown): JsonValue {
function mapLogLevel (line 92) | function mapLogLevel(level: 'info' | 'warn' | 'error' | undefined): 'inf...
function mapV2ErrorCode (line 96) | function mapV2ErrorCode(code: ActionErrorCode): RRErrorCode {
function toRRErrorFromV2 (line 128) | function toRRErrorFromV2(error: ActionError): RRError {
function isRecord (line 137) | function isRecord(value: unknown): value is Record<string, unknown> {
function jsonEquals (line 141) | function jsonEquals(a: JsonValue, b: JsonValue): boolean {
function diffVars (line 173) | function diffVars(
function readNumberVar (line 204) | function readNumberVar(vars: Record<string, JsonValue>, key: string): nu...
function toV2ActionPolicy (line 209) | function toV2ActionPolicy(policy: NodePolicy | undefined): ActionPolicy ...
function toJsonRecord (line 263) | function toJsonRecord(value: unknown): Record<string, JsonValue> {
function adaptV2ActionHandlerToV3NodeDefinition (line 281) | function adaptV2ActionHandlerToV3NodeDefinition<T extends ExecutableActi...
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/enqueue-run.ts
type EnqueueRunDeps (line 25) | interface EnqueueRunDeps {
type EnqueueRunInput (line 41) | interface EnqueueRunInput {
type EnqueueRunResult (line 64) | interface EnqueueRunResult {
function defaultGenerateRunId (line 76) | function defaultGenerateRunId(): RunId {
function validateInt (line 83) | function validateInt(
function computeQueuePosition (line 113) | async function computeQueuePosition(
function enqueueRun (line 141) | async function enqueueRun(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/leasing.ts
type LeaseManager (line 14) | interface LeaseManager {
function createLeaseManager (line 53) | function createLeaseManager(queue: RunQueue, config: RunQueueConfig): Le...
function generateOwnerId (line 111) | function generateOwnerId(): string {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/queue.ts
type RunQueueConfig (line 13) | interface RunQueueConfig {
constant DEFAULT_QUEUE_CONFIG (line 25) | const DEFAULT_QUEUE_CONFIG: RunQueueConfig = {
type QueueItemStatus (line 34) | type QueueItemStatus = 'queued' | 'running' | 'paused';
type Lease (line 39) | interface Lease {
type RunQueueItem (line 49) | interface RunQueueItem {
type EnqueueInput (line 83) | type EnqueueInput = Omit<
type RunQueue (line 98) | interface RunQueue {
function createNotImplementedQueue (line 181) | function createNotImplementedQueue(): RunQueue {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/scheduler.ts
type RunExecutor (line 30) | type RunExecutor = (item: RunQueueItem) => Promise<void>;
type RunSchedulerTuning (line 35) | interface RunSchedulerTuning {
type RunSchedulerDeps (line 52) | interface RunSchedulerDeps {
type RunSchedulerState (line 67) | interface RunSchedulerState {
type RunScheduler (line 77) | interface RunScheduler {
constant DEFAULT_POLL_INTERVAL_MS (line 95) | const DEFAULT_POLL_INTERVAL_MS = 500;
function clampNonNegativeInt (line 99) | function clampNonNegativeInt(value: unknown, fallback: number): number {
function defaultReclaimIntervalMs (line 104) | function defaultReclaimIntervalMs(leaseTtlMs: number): number {
function createRunScheduler (line 123) | function createRunScheduler(deps: RunSchedulerDeps): RunScheduler {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/recovery/recovery-coordinator.ts
type RecoveryResult (line 28) | interface RecoveryResult {
type RecoveryCoordinatorDeps (line 40) | interface RecoveryCoordinatorDeps {
function recoverFromCrash (line 66) | async function recoverFromCrash(deps: RecoveryCoordinatorDeps): Promise<...
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/storage/storage-port.ts
type FlowsStore (line 16) | interface FlowsStore {
type RunsStore (line 30) | interface RunsStore {
type EventsStore (line 45) | interface EventsStore {
type PersistentVarsStore (line 65) | interface PersistentVarsStore {
type TriggersStore (line 82) | interface TriggersStore {
type StoragePort (line 97) | interface StoragePort {
function createNotImplementedStore (line 116) | function createNotImplementedStore<T extends object>(name: string): T {
function createNotImplementedStoragePort (line 135) | function createNotImplementedStoragePort(): StoragePort {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/events-bus.ts
type EventsQuery (line 13) | interface EventsQuery {
type EventsFilter (line 25) | interface EventsFilter {
type EventsBus (line 34) | interface EventsBus {
function createNotImplementedEventsBus (line 63) | function createNotImplementedEventsBus(): EventsBus {
type ListenerEntry (line 81) | interface ListenerEntry {
class StorageBackedEventsBus (line 92) | class StorageBackedEventsBus implements EventsBus {
method constructor (line 95) | constructor(private readonly store: EventsStore) {}
method subscribe (line 97) | subscribe(listener: (event: RunEvent) => void, filter?: EventsFilter):...
method append (line 105) | async append(input: RunEventInput): Promise<RunEvent> {
method list (line 115) | async list(query: EventsQuery): Promise<RunEvent[]> {
method broadcast (line 125) | private broadcast(event: RunEvent): void {
class InMemoryEventsBus (line 144) | class InMemoryEventsBus implements EventsBus {
method subscribe (line 149) | subscribe(listener: (event: RunEvent) => void, filter?: EventsFilter):...
method append (line 157) | async append(input: RunEventInput): Promise<RunEvent> {
method list (line 191) | async list(query: EventsQuery): Promise<RunEvent[]> {
method clear (line 210) | clear(): void {
method getSeq (line 219) | getSeq(runId: RunId): number {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/rpc-server.ts
type RpcServerConfig (line 33) | interface RpcServerConfig {
type PortConnection (line 49) | interface PortConnection {
function defaultGenerateRunId (line 57) | function defaultGenerateRunId(): RunId {
class RpcServer (line 65) | class RpcServer {
method constructor (line 77) | constructor(config: RpcServerConfig) {
method start (line 91) | start(): void {
method stop (line 103) | stop(): void {
method broadcastEvent (line 164) | private broadcastEvent(event: RunEvent): void {
method handleEnqueueRun (line 187) | private async handleEnqueueRun(params: JsonObject | undefined): Promis...
method handleListQueue (line 213) | private async handleListQueue(params: JsonObject | undefined): Promise...
method handleCancelQueueItem (line 243) | private async handleCancelQueueItem(params: JsonObject | undefined): P...
method handleRequest (line 286) | private async handleRequest(request: RpcRequest, conn: PortConnection)...
method handleSaveFlow (line 421) | private async handleSaveFlow(params: JsonObject | undefined): Promise<...
method handleDeleteFlow (line 447) | private async handleDeleteFlow(params: JsonObject | undefined): Promis...
method normalizeFlowSpec (line 491) | private normalizeFlowSpec(value: unknown, existingFlow: FlowV3 | null ...
method normalizeNode (line 647) | private normalizeNode(value: unknown, index: number): NodeV3 {
method normalizeEdge (line 710) | private normalizeEdge(value: unknown, index: number): EdgeV3 {
method requireTriggerManager (line 758) | private requireTriggerManager(): TriggerManager {
method handleCreateTrigger (line 765) | private async handleCreateTrigger(params: JsonObject | undefined): Pro...
method handleUpdateTrigger (line 783) | private async handleUpdateTrigger(params: JsonObject | undefined): Pro...
method handleDeleteTrigger (line 801) | private async handleDeleteTrigger(params: JsonObject | undefined): Pro...
method handleGetTrigger (line 810) | private async handleGetTrigger(params: JsonObject | undefined): Promis...
method handleListTriggers (line 817) | private async handleListTriggers(params: JsonObject | undefined): Prom...
method handleEnableTrigger (line 832) | private async handleEnableTrigger(params: JsonObject | undefined): Pro...
method handleDisableTrigger (line 847) | private async handleDisableTrigger(params: JsonObject | undefined): Pr...
method handleFireTrigger (line 862) | private async handleFireTrigger(params: JsonObject | undefined): Promi...
method normalizeTriggerSpec (line 903) | private normalizeTriggerSpec(value: unknown, opts: { requireId: boolea...
method handlePauseRun (line 1067) | private async handlePauseRun(params: JsonObject | undefined): Promise<...
method handleResumeRun (line 1100) | private async handleResumeRun(params: JsonObject | undefined): Promise...
method handleCancelRun (line 1133) | private async handleCancelRun(params: JsonObject | undefined): Promise...
function createRpcServer (line 1164) | function createRpcServer(config: RpcServerConfig): RpcServer {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/rpc.ts
constant RR_V3_PORT_NAME (line 11) | const RR_V3_PORT_NAME = 'rr_v3' as const;
type RpcMethod (line 16) | type RpcMethod =
type RpcRequest (line 53) | interface RpcRequest {
type RpcResponseOk (line 66) | interface RpcResponseOk {
type RpcResponseErr (line 78) | interface RpcResponseErr {
type RpcResponse (line 90) | type RpcResponse = RpcResponseOk | RpcResponseErr;
type RpcEventMessage (line 95) | interface RpcEventMessage {
type RpcSubscribeAck (line 104) | interface RpcSubscribeAck {
type RpcMessage (line 113) | type RpcMessage =
function generateRequestId (line 123) | function generateRequestId(): string {
function isRpcRequest (line 130) | function isRpcRequest(msg: unknown): msg is RpcRequest {
function isRpcResponse (line 137) | function isRpcResponse(msg: unknown): msg is RpcResponse {
function isRpcEvent (line 144) | function isRpcEvent(msg: unknown): msg is RpcEventMessage {
function createRpcRequest (line 151) | function createRpcRequest(method: RpcMethod, params?: JsonObject): RpcRe...
function createRpcResponseOk (line 163) | function createRpcResponseOk(requestId: string, result: JsonValue): RpcR...
function createRpcResponseErr (line 175) | function createRpcResponseErr(requestId: string, error: string): RpcResp...
function createRpcEventMessage (line 187) | function createRpcEventMessage(event: RunEvent): RpcEventMessage {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/command-trigger.ts
type CommandTriggerHandlerDeps (line 21) | interface CommandTriggerHandlerDeps {
type CommandTriggerSpec (line 25) | type CommandTriggerSpec = TriggerSpecByKind<'command'>;
type InstalledCommandTrigger (line 27) | interface InstalledCommandTrigger {
function createCommandTriggerHandlerFactory (line 36) | function createCommandTriggerHandlerFactory(
function createCommandTriggerHandler (line 45) | function createCommandTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/context-menu-trigger.ts
type ContextMenuTriggerHandlerDeps (line 19) | interface ContextMenuTriggerHandlerDeps {
type ContextMenuTriggerSpec (line 23) | type ContextMenuTriggerSpec = TriggerSpecByKind<'contextMenu'>;
type InstalledContextMenuTrigger (line 25) | interface InstalledContextMenuTrigger {
constant MENU_ITEM_PREFIX (line 32) | const MENU_ITEM_PREFIX = 'rr_v3_';
constant DEFAULT_CONTEXTS (line 35) | const DEFAULT_CONTEXTS: chrome.contextMenus.ContextType[] = ['page'];
function createContextMenuTriggerHandlerFactory (line 42) | function createContextMenuTriggerHandlerFactory(
function createContextMenuTriggerHandler (line 51) | function createContextMenuTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/cron-trigger.ts
type CronTriggerSpec (line 26) | type CronTriggerSpec = TriggerSpecByKind<'cron'>;
type ComputeNextFireAtMs (line 31) | type ComputeNextFireAtMs = (input: {
type CronTriggerHandlerDeps (line 37) | interface CronTriggerHandlerDeps {
type InstalledCronTrigger (line 43) | interface InstalledCronTrigger {
constant ALARM_PREFIX (line 51) | const ALARM_PREFIX = 'rr_v3_cron_';
function normalizeCronExpression (line 58) | function normalizeCronExpression(value: unknown): string {
function normalizeTimezone (line 70) | function normalizeTimezone(value: unknown): string | undefined {
function alarmNameForTrigger (line 91) | function alarmNameForTrigger(triggerId: TriggerId): string {
function parseTriggerIdFromAlarmName (line 98) | function parseTriggerIdFromAlarmName(name: string): TriggerId | null {
function parseSimpleCron (line 111) | function parseSimpleCron(cron: string): {
type ZonedTimeParts (line 179) | interface ZonedTimeParts {
function getDateTimeFormat (line 194) | function getDateTimeFormat(timezone: string): Intl.DateTimeFormat {
constant WEEKDAY_MAP (line 213) | const WEEKDAY_MAP: Record<string, number> = {
function getZonedTimeParts (line 226) | function getZonedTimeParts(utcMs: UnixMillis, timezone: string): ZonedTi...
function getTimezoneOffsetMs (line 251) | function getTimezoneOffsetMs(utcMs: UnixMillis, timezone: string): number {
function zonedToUtcMs (line 261) | function zonedToUtcMs(
function computeNextFireAtMsLocal (line 284) | function computeNextFireAtMsLocal(
function computeNextFireAtMsZoned (line 318) | function computeNextFireAtMsZoned(
function computeNextFireAtMsSimple (line 369) | function computeNextFireAtMsSimple(input: {
function defaultComputeNextFireAtMs (line 387) | function defaultComputeNextFireAtMs(input: {
function createCronTriggerHandlerFactory (line 400) | function createCronTriggerHandlerFactory(
function createCronTriggerHandler (line 409) | function createCronTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/dom-trigger.ts
type DomTriggerHandlerDeps (line 25) | interface DomTriggerHandlerDeps {
type DomTriggerSpec (line 29) | type DomTriggerSpec = TriggerSpecByKind<'dom'>;
type DomObserverTriggerPayload (line 34) | interface DomObserverTriggerPayload {
type DomTriggerFiredMessage (line 45) | interface DomTriggerFiredMessage {
constant DOM_OBSERVER_SCRIPT_FILE (line 53) | const DOM_OBSERVER_SCRIPT_FILE = 'inject-scripts/dom-observer.js';
constant DEFAULT_DEBOUNCE_MS (line 54) | const DEFAULT_DEBOUNCE_MS = 800;
function normalizeDebounceMs (line 58) | function normalizeDebounceMs(value: unknown): number {
function buildDomObserverPayload (line 67) | function buildDomObserverPayload(
function isInjectableUrl (line 93) | function isInjectableUrl(url: string): boolean {
function isDomTriggerFiredMessage (line 100) | function isDomTriggerFiredMessage(msg: unknown): msg is DomTriggerFiredM...
function createDomTriggerHandlerFactory (line 113) | function createDomTriggerHandlerFactory(
function createDomTriggerHandler (line 122) | function createDomTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/interval-trigger.ts
type IntervalTriggerSpec (line 17) | type IntervalTriggerSpec = TriggerSpecByKind<'interval'>;
type IntervalTriggerHandlerDeps (line 19) | interface IntervalTriggerHandlerDeps {
type InstalledIntervalTrigger (line 23) | interface InstalledIntervalTrigger {
constant ALARM_PREFIX (line 31) | const ALARM_PREFIX = 'rr_v3_interval_';
function normalizePeriodMinutes (line 38) | function normalizePeriodMinutes(value: unknown): number {
function alarmNameForTrigger (line 51) | function alarmNameForTrigger(triggerId: TriggerId): string {
function parseTriggerIdFromAlarmName (line 58) | function parseTriggerIdFromAlarmName(name: string): TriggerId | null {
function createIntervalTriggerHandlerFactory (line 69) | function createIntervalTriggerHandlerFactory(
function createIntervalTriggerHandler (line 78) | function createIntervalTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/manual-trigger.ts
type ManualTriggerHandlerDeps (line 17) | interface ManualTriggerHandlerDeps {
type ManualTriggerSpec (line 21) | type ManualTriggerSpec = TriggerSpecByKind<'manual'>;
function createManualTriggerHandlerFactory (line 28) | function createManualTriggerHandlerFactory(
function createManualTriggerHandler (line 40) | function createManualTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/once-trigger.ts
type OnceTriggerSpec (line 19) | type OnceTriggerSpec = TriggerSpecByKind<'once'>;
type OnceTriggerHandlerDeps (line 21) | interface OnceTriggerHandlerDeps {
type InstalledOnceTrigger (line 30) | interface InstalledOnceTrigger {
constant ALARM_PREFIX (line 38) | const ALARM_PREFIX = 'rr_v3_once_';
function normalizeWhenMs (line 45) | function normalizeWhenMs(value: unknown): UnixMillis {
function alarmNameForTrigger (line 55) | function alarmNameForTrigger(triggerId: TriggerId): string {
function parseTriggerIdFromAlarmName (line 62) | function parseTriggerIdFromAlarmName(name: string): TriggerId | null {
function createOnceTriggerHandlerFactory (line 73) | function createOnceTriggerHandlerFactory(
function createOnceTriggerHandler (line 82) | function createOnceTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/trigger-handler.ts
type TriggerHandler (line 12) | interface TriggerHandler<K extends TriggerKind = TriggerKind> {
type TriggerFireCallback (line 46) | interface TriggerFireCallback {
type TriggerHandlerFactory (line 64) | type TriggerHandlerFactory<K extends TriggerKind> = (
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/trigger-manager.ts
type TriggerHandlerFactories (line 29) | type TriggerHandlerFactories = Partial<{
type TriggerManagerStormControl (line 36) | interface TriggerManagerStormControl {
type TriggerManagerDeps (line 55) | interface TriggerManagerDeps {
type TriggerManagerState (line 77) | interface TriggerManagerState {
type TriggerManager (line 87) | interface TriggerManager {
function normalizeNonNegativeInt (line 113) | function normalizeNonNegativeInt(value: unknown, fallback: number, field...
function normalizePositiveInt (line 124) | function normalizePositiveInt(value: unknown, fieldName: string): number {
function createTriggerManager (line 140) | function createTriggerManager(deps: TriggerManagerDeps): TriggerManager {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/url-trigger.ts
type UrlTriggerHandlerDeps (line 23) | interface UrlTriggerHandlerDeps {
type UrlTriggerSpec (line 27) | type UrlTriggerSpec = TriggerSpecByKind<'url'>;
type CompiledUrlRules (line 32) | interface CompiledUrlRules {
type InstalledUrlTrigger (line 41) | interface InstalledUrlTrigger {
function normalizeDomain (line 54) | function normalizeDomain(value: string): string | null {
function normalizePathPrefix (line 64) | function normalizePathPrefix(value: string): string | null {
function normalizeUrlPrefix (line 74) | function normalizeUrlPrefix(value: string): string | null {
function compileUrlMatchRules (line 82) | function compileUrlMatchRules(match: UrlMatchRule[] | undefined): Compil...
function hostnameMatchesDomain (line 118) | function hostnameMatchesDomain(hostname: string, domain: string): boolean {
function matchesRules (line 126) | function matchesRules(compiled: CompiledUrlRules, urlString: string, par...
function createUrlTriggerHandlerFactory (line 152) | function createUrlTriggerHandlerFactory(
function createUrlTriggerHandler (line 161) | function createUrlTriggerHandler(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/index.ts
function createStoragePort (line 28) | function createStoragePort(): StoragePort {
constant RR_V3_VERSION (line 42) | const RR_V3_VERSION = '3.0.0' as const;
constant IS_RR_V3 (line 45) | const IS_RR_V3 = true as const;
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/db.ts
constant RR_V3_DB_NAME (line 7) | const RR_V3_DB_NAME = 'rr_v3';
constant RR_V3_DB_VERSION (line 10) | const RR_V3_DB_VERSION = 1;
constant RR_V3_STORES (line 15) | const RR_V3_STORES = {
type StoreConfig (line 27) | interface StoreConfig {
constant RR_V3_STORE_SCHEMAS (line 41) | const RR_V3_STORE_SCHEMAS: Record<string, StoreConfig> = {
function handleUpgrade (line 101) | function handleUpgrade(db: IDBDatabase, oldVersion: number, _newVersion:...
function openRrV3Db (line 128) | async function openRrV3Db(): Promise<IDBDatabase> {
function closeRrV3Db (line 173) | function closeRrV3Db(): void {
function deleteRrV3Db (line 185) | async function deleteRrV3Db(): Promise<void> {
function withTransaction (line 201) | async function withTransaction<T>(
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/events.ts
function idbRequest (line 15) | function idbRequest<T>(request: IDBRequest<T>, context: string): Promise...
function createEventsStore (line 36) | function createEventsStore(): EventsStore {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/flows.ts
function validateFlow (line 16) | function validateFlow(flow: FlowV3): void {
function createFlowsStore (line 65) | function createFlowsStore(): FlowsStore {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/import/v2-reader.ts
type V2Reader (line 10) | interface V2Reader {
function createNotImplementedV2Reader (line 24) | function createNotImplementedV2Reader(): V2Reader {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/import/v2-to-v3.ts
type V2Node (line 16) | interface V2Node {
type V2Edge (line 26) | interface V2Edge {
type V2VariableDef (line 34) | interface V2VariableDef {
type V2Binding (line 44) | interface V2Binding {
type V2Flow (line 50) | interface V2Flow {
type ConversionResult (line 72) | interface ConversionResult<T> {
function convertFlowV2ToV3 (line 86) | function convertFlowV2ToV3(v2Flow: V2Flow): ConversionResult<FlowV3> {
function convertNodeV2ToV3 (line 192) | function convertNodeV2ToV3(v2Node: V2Node): NodeV3 | null {
function convertEdgeV2ToV3 (line 220) | function convertEdgeV2ToV3(v2Edge: V2Edge): EdgeV3 | null {
type EntryNodeResult (line 240) | interface EntryNodeResult {
function findEntryNodeId (line 256) | function findEntryNodeId(nodes: NodeV3[], edges: EdgeV3[]): EntryNodeRes...
type StableSelectionResult (line 318) | interface StableSelectionResult {
function selectStableRootNode (line 327) | function selectStableRootNode(nodes: NodeV3[]): StableSelectionResult {
function convertVariablesV2ToV3 (line 356) | function convertVariablesV2ToV3(v2Variables: V2VariableDef[]): VariableD...
function convertMetaV2ToV3 (line 384) | function convertMetaV2ToV3(v2Meta: V2Flow['meta']): FlowV3['meta'] | und...
function convertFlowV3ToV2 (line 415) | function convertFlowV3ToV2(v3Flow: FlowV3): ConversionResult<V2Flow> {
type V2Trigger (line 481) | interface V2Trigger {
function convertTriggerV2ToV3 (line 506) | function convertTriggerV2ToV3(v2Trigger: V2Trigger): ConversionResult<Tr...
function convertScheduleToCron (line 594) | function convertScheduleToCron(schedule: V2Trigger['schedule']): string ...
type V2ToV3Converter (line 635) | interface V2ToV3Converter {
function createV2ToV3Converter (line 645) | function createV2ToV3Converter(): V2ToV3Converter {
function createNotImplementedV2ToV3Converter (line 669) | function createNotImplementedV2ToV3Converter(): V2ToV3Converter {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/persistent-vars.ts
function createPersistentVarsStore (line 14) | function createPersistentVarsStore(): PersistentVarsStore {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/queue.ts
constant DEFAULT_LEASE_TTL_MS (line 17) | const DEFAULT_LEASE_TTL_MS = DEFAULT_QUEUE_CONFIG.leaseTtlMs;
constant IDB_NUMBER_MIN (line 23) | const IDB_NUMBER_MIN = -Number.MAX_VALUE;
constant IDB_NUMBER_MAX (line 24) | const IDB_NUMBER_MAX = Number.MAX_VALUE;
function createQueueStore (line 30) | function createQueueStore(): RunQueue {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/runs.ts
function validateRunRecord (line 16) | function validateRunRecord(record: RunRecordV3): void {
function createRunsStore (line 40) | function createRunsStore(): RunsStore {
FILE: app/chrome-extension/entrypoints/background/record-replay-v3/storage/triggers.ts
function createTriggersStore (line 14) | function createTriggersStore(): TriggersStore {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/adapter.ts
constant STEP_TYPE_TO_ACTION_TYPE (line 35) | const STEP_TYPE_TO_ACTION_TYPE: Record<string, ExecutableActionType> = {
function execCtxToActionCtx (line 84) | function execCtxToActionCtx(
function stepToAction (line 124) | function stepToAction(step: Step): ExecutableAction | null {
type LegacySelectorCandidate (line 167) | interface LegacySelectorCandidate {
type LegacyTargetLocator (line 173) | interface LegacyTargetLocator {
function parseAriaValue (line 187) | function parseAriaValue(value: string): { role?: string; name: string } {
function convertSelectorCandidate (line 207) | function convertSelectorCandidate(legacy: LegacySelectorCandidate): Reco...
function convertTargetLocator (line 248) | function convertTargetLocator(target: LegacyTargetLocator): Record<strin...
function isLegacyTargetLocator (line 281) | function isLegacyTargetLocator(value: unknown): value is LegacyTargetLoc...
function extractParams (line 318) | function extractParams(step: Step): Record<string, unknown> {
function actionResultToExecResult (line 351) | function actionResultToExecResult(result: ActionExecutionResult): ExecRe...
type StepExecutionAttempt (line 379) | type StepExecutionAttempt =
type StepExecutorOptions (line 386) | interface StepExecutorOptions {
function createStepExecutor (line 418) | function createStepExecutor(registry: ActionRegistry) {
function isActionSupported (line 504) | function isActionSupported(stepType: string): boolean {
function getActionType (line 511) | function getActionType(stepType: string): ExecutableActionType | undefin...
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/assert.ts
constant DEFAULT_ASSERT_TIMEOUT_MS (line 15) | const DEFAULT_ASSERT_TIMEOUT_MS = 5000;
constant POLL_INTERVAL_MS (line 18) | const POLL_INTERVAL_MS = 200;
constant MAX_ATTR_NAME_LENGTH (line 21) | const MAX_ATTR_NAME_LENGTH = 256;
function validateAssertion (line 26) | function validateAssertion(assert: Assertion): { ok: true } | { ok: fals...
function resolveAssertionParams (line 63) | function resolveAssertionParams(
type ResolvedAssertion (line 137) | type ResolvedAssertion =
function checkAssertionInPage (line 146) | async function checkAssertionInPage(
function pollAssertion (line 253) | async function pollAssertion(
function truncate (line 353) | function truncate(str: string, maxLen: number): string {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/click.ts
function executeClick (line 38) | async function executeClick<T extends 'click' | 'dblclick'>(
function validateClickTarget (line 144) | function validateClickTarget(target: {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/common.ts
function interpolateBraces (line 35) | function interpolateBraces(template: string, vars: VariableStore): string {
function resolveString (line 45) | function resolveString(
function resolveOptionalString (line 57) | function resolveOptionalString(
function clampInt (line 75) | function clampInt(value: number, min: number, max: number): number {
type ConvertedSelectorTarget (line 85) | interface ConvertedSelectorTarget {
function toSelectorTarget (line 101) | function toSelectorTarget(
type SendMessageResult (line 240) | type SendMessageResult<T = unknown> = { ok: true; value: T } | { ok: fal...
function sendMessageToTab (line 246) | async function sendMessageToTab<T = unknown>(
function ensureElementVisible (line 271) | async function ensureElementVisible(
function readTabUrl (line 289) | async function readTabUrl(tabId: number): Promise<string> {
type FallbackLogEntry (line 302) | interface FallbackLogEntry {
function logSelectorFallback (line 314) | function logSelectorFallback(
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/control-flow.ts
constant DEFAULT_MAX_ITERATIONS (line 31) | const DEFAULT_MAX_ITERATIONS = 1000;
function evaluateCondition (line 40) | function evaluateCondition(condition: Condition, vars: VariableStore): b...
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/delay.ts
constant MAX_DELAY_MS (line 12) | const MAX_DELAY_MS = 2_147_483_647;
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/dom.ts
type ResolveRefResponse (line 38) | interface ResolveRefResponse {
type DomScriptResult (line 44) | interface DomScriptResult {
type ResolvedTarget (line 49) | interface ResolvedTarget {
function hasValidTarget (line 64) | function hasValidTarget(target: unknown): boolean {
function stripCompositePrefix (line 75) | function stripCompositePrefix(selector: string): string {
function resolveTargetSelector (line 94) | async function resolveTargetSelector(
function maybeLogFallback (line 157) | function maybeLogFallback(
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/drag.ts
type Coordinates (line 24) | interface Coordinates {
function hasTargetSpec (line 30) | function hasTargetSpec(target: unknown): boolean {
function isFiniteNumber (line 39) | function isFiniteNumber(v: unknown): v is number {
function getPathEndpoints (line 44) | function getPathEndpoints(
function extractToolError (line 63) | function extractToolError(result: unknown, fallback: string): string {
function locateTarget (line 69) | async function locateTarget(
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/extract.ts
constant DEFAULT_EXTRACT_ATTR (line 13) | const DEFAULT_EXTRACT_ATTR = 'textContent';
function executeExtraction (line 18) | async function executeExtraction(
function resolveExtractParams (line 147) | function resolveExtractParams(
type ResolvedParams (line 194) | type ResolvedParams =
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/http.ts
constant DEFAULT_HTTP_TIMEOUT_MS (line 30) | const DEFAULT_HTTP_TIMEOUT_MS = 30000;
constant MAX_URL_LENGTH (line 33) | const MAX_URL_LENGTH = 8192;
function resolveHeaders (line 38) | async function resolveHeaders(
function resolveFormData (line 59) | async function resolveFormData(
function resolveBody (line 80) | async function resolveBody(
function isStatusOk (line 128) | function isStatusOk(status: number, okStatus: HttpOkStatus | undefined):...
function getValueByPath (line 148) | function getValueByPath(obj: unknown, path: string): JsonValue | undefin...
function applyAssignments (line 178) | function applyAssignments(
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/index.ts
constant ALL_HANDLERS (line 64) | const ALL_HANDLERS = [
function registerReplayHandlers (line 102) | function registerReplayHandlers(registry: ActionRegistry): void {
function createReplayActionRegistry (line 146) | function createReplayActionRegistry(): ActionRegistry {
function getSupportedActionTypes (line 155) | function getSupportedActionTypes(): ReadonlyArray<string> {
function isActionTypeSupported (line 162) | function isActionTypeSupported(type: string): boolean {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/key.ts
function extractToolError (line 25) | function extractToolError(result: unknown, fallback: string): string {
function hasTargetSpec (line 31) | function hasTargetSpec(target: unknown): boolean {
function stripCompositeSelector (line 40) | function stripCompositeSelector(selector: string): string {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/screenshot.ts
function extractToolText (line 15) | function extractToolText(result: unknown): string | undefined {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/script.ts
constant MAX_CODE_LENGTH (line 23) | const MAX_CODE_LENGTH = 100000;
function resolveArgs (line 28) | function resolveArgs(
function getValueByPath (line 49) | function getValueByPath(obj: unknown, path: string): JsonValue | undefin...
function applyAssignments (line 80) | function applyAssignments(result: JsonValue, assignments: Assignments, v...
function executeScript (line 92) | async function executeScript(
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/scroll.ts
function hasTargetSpec (line 18) | function hasTargetSpec(target: unknown): boolean {
function stripCompositeSelector (line 27) | function stripCompositeSelector(selector: string): string {
function describeOffset (line 38) | function describeOffset(v: unknown): string {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/handlers/tabs.ts
constant DEFAULT_TAB_TIMEOUT_MS (line 15) | const DEFAULT_TAB_TIMEOUT_MS = 10000;
constant DEFAULT_DOWNLOAD_TIMEOUT_MS (line 18) | const DEFAULT_DOWNLOAD_TIMEOUT_MS = 60000;
function waitForTabComplete (line 393) | async function waitForTabComplete(tabId: number, timeoutMs: number): Pro...
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/registry.ts
type AnyExecutableAction (line 39) | type AnyExecutableAction = {
type AnyExecutableHandler (line 42) | type AnyExecutableHandler = { [T in ExecutableActionType]: ActionHandler...
type BeforeExecuteArgs (line 44) | interface BeforeExecuteArgs<T extends ExecutableActionType> {
type BeforeExecuteHook (line 51) | type BeforeExecuteHook = <T extends ExecutableActionType>(
type AfterExecuteArgs (line 55) | interface AfterExecuteArgs<T extends ExecutableActionType> {
type AfterExecuteHook (line 63) | type AfterExecuteHook = <T extends ExecutableActionType>(
type ActionRegistryHooks (line 67) | interface ActionRegistryHooks {
function isRecord (line 76) | function isRecord(value: unknown): value is Record<string, unknown> {
function toNonEmptyArray (line 80) | function toNonEmptyArray(value: string[], fallback: string): NonEmptyArr...
constant ACTION_ERROR_CODES (line 84) | const ACTION_ERROR_CODES: ReadonlyArray<ActionErrorCode> = [
function isActionErrorCode (line 99) | function isActionErrorCode(value: unknown): value is ActionErrorCode {
function toErrorMessage (line 103) | function toErrorMessage(e: unknown): string {
function toActionError (line 110) | function toActionError(e: unknown, fallbackCode: ActionErrorCode = 'UNKN...
function ok (line 117) | function ok(): ValidationResult {
function invalid (line 121) | function invalid(...errors: string[]): ValidationResult {
function failed (line 125) | function failed<T extends ExecutableActionType>(
function sleep (line 132) | function sleep(ms: number): Promise<void> {
function isVariablePointer (line 141) | function isVariablePointer(value: unknown): value is VariablePointer {
function isVarValue (line 149) | function isVarValue(
function isExprValue (line 157) | function isExprValue(value: unknown): value is { kind: 'expr'; default?:...
function isStringTemplate (line 163) | function isStringTemplate(value: unknown): value is { kind: 'template'; ...
function readByPath (line 169) | function readByPath(
function tryResolveJson (line 192) | function tryResolveJson(
function formatInserted (line 211) | function formatInserted(value: JsonValue, format?: 'text' | 'json' | 'ur...
function tryResolveString (line 218) | function tryResolveString(
function tryResolveNumber (line 267) | function tryResolveNumber(
function shouldRetry (line 304) | function shouldRetry(policy: RetryPolicy | undefined, error: ActionError...
function computeRetryDelayMs (line 313) | function computeRetryDelayMs(policy: RetryPolicy, retryIndex: number): n...
function runWithTimeout (line 327) | async function runWithTimeout<T>(
class ActionRegistry (line 363) | class ActionRegistry {
method register (line 371) | register<T extends ExecutableActionType>(
method unregister (line 388) | unregister<T extends ExecutableActionType>(type: T): boolean {
method get (line 397) | get<T extends ExecutableActionType>(type: T): ActionHandler<T> | undef...
method has (line 404) | has(type: ExecutableActionType): boolean {
method list (line 411) | list(): ReadonlyArray<AnyExecutableHandler> {
method onBeforeExecute (line 421) | onBeforeExecute(hook: BeforeExecuteHook): () => void {
method onAfterExecute (line 432) | onAfterExecute(hook: AfterExecuteHook): () => void {
method use (line 443) | use(hooks: ActionRegistryHooks): () => void {
method validate (line 455) | validate<T extends ExecutableActionType>(action: ExecutableAction<T>):...
method execute (line 465) | async execute<T extends ExecutableActionType>(
function createActionRegistry (line 638) | function createActionRegistry(): ActionRegistry {
FILE: app/chrome-extension/entrypoints/background/record-replay/actions/types.ts
type Milliseconds (line 18) | type Milliseconds = number;
type ISODateTimeString (line 19) | type ISODateTimeString = string;
type NonEmptyArray (line 20) | type NonEmptyArray<T> = [T, ...T[]];
type JsonPrimitive (line 23) | type JsonPrimitive = string | number | boolean | null;
type JsonValue (line 24) | type JsonValue = JsonPrimitive | JsonObject | JsonArray;
type JsonObject (line 25) | interface JsonObject {
type JsonArray (line 28) | type JsonArray = JsonValue[];
type FlowId (line 31) | type FlowId = string;
type ActionId (line 32) | type ActionId = string;
type SubflowId (line 33) | type SubflowId = string;
type EdgeId (line 34) | type EdgeId = string;
type VariableName (line 35) | type VariableName = string;
constant EDGE_LABELS (line 41) | const EDGE_LABELS = {
type BuiltinEdgeLabel (line 48) | type BuiltinEdgeLabel = (typeof EDGE_LABELS)[keyof typeof EDGE_LABELS];
type EdgeLabel (line 49) | type EdgeLabel = string;
type ActionErrorCode (line 55) | type ActionErrorCode =
type ActionError (line 69) | interface ActionError {
type TimeoutPolicy (line 79) | interface TimeoutPolicy {
type BackoffKind (line 85) | type BackoffKind = 'none' | 'exp' | 'linear';
type RetryPolicy (line 87) | interface RetryPolicy {
type ErrorHandlingStrategy (line 102) | type ErrorHandlingStrategy =
type ArtifactCapturePolicy (line 107) | interface ArtifactCapturePolicy {
type ActionPolicy (line 114) | interface ActionPolicy {
type VariableDefinitionBase (line 125) | interface VariableDefinitionBase {
type VariableStringRules (line 133) | interface VariableStringRules {
type VariableNumberRules (line 139) | interface VariableNumberRules {
type VariableDefinition (line 145) | type VariableDefinition =
type VariableStore (line 175) | type VariableStore = Record<VariableName, JsonValue>;
type VariableScope (line 177) | type VariableScope = 'flow' | 'run' | 'env' | 'secret';
type VariablePathSegment (line 178) | type VariablePathSegment = string | number;
type VariablePointer (line 180) | interface VariablePointer {
type ExpressionLanguage (line 190) | type ExpressionLanguage = 'js' | 'rr';
type Expression (line 192) | interface Expression<_T = JsonValue> {
type VariableValue (line 197) | interface VariableValue<T> {
type ExpressionValue (line 203) | interface ExpressionValue<T> {
type TemplateFormat (line 209) | type TemplateFormat = 'text' | 'json' | 'urlEncoded';
type TemplatePart (line 211) | type TemplatePart =
type StringTemplate (line 215) | interface StringTemplate {
type Resolvable (line 220) | type Resolvable<T> =
type DataPath (line 226) | type DataPath = string;
type Assignments (line 227) | type Assignments = Record<VariableName, DataPath>;
type CompareOp (line 233) | type CompareOp =
type Condition (line 249) | type Condition =
type SelectorCandidateSource (line 267) | type SelectorCandidateSource = 'recorded' | 'user' | 'generated';
type SelectorStability (line 269) | interface SelectorStability {
type SelectorCandidateBase (line 283) | interface SelectorCandidateBase {
type SelectorCandidate (line 289) | type SelectorCandidate =
type FrameTarget (line 305) | type FrameTarget =
type TargetHint (line 310) | interface TargetHint {
type ElementTargetBase (line 317) | interface ElementTargetBase {
type ElementTarget (line 322) | type ElementTarget =
type BrowserWorld (line 337) | type BrowserWorld = 'MAIN' | 'ISOLATED';
type ClickParams (line 341) | interface ClickParams {
type FillParams (line 348) | interface FillParams {
type KeyParams (line 355) | interface KeyParams {
type ScrollMode (line 360) | type ScrollMode = 'element' | 'offset' | 'container';
type ScrollOffset (line 362) | interface ScrollOffset {
type ScrollParams (line 367) | interface ScrollParams {
type Point (line 373) | interface Point {
type DragParams (line 378) | interface DragParams {
type NavigateParams (line 386) | interface NavigateParams {
type WaitCondition (line 393) | type WaitCondition =
type WaitParams (line 400) | interface WaitParams {
type Assertion (line 404) | type Assertion =
type AssertFailStrategy (line 416) | type AssertFailStrategy = 'stop' | 'warn' | 'retry';
type AssertParams (line 418) | interface AssertParams {
type ExtractParams (line 425) | type ExtractParams =
type ScriptTiming (line 439) | type ScriptTiming = 'before' | 'after';
type ScriptParams (line 441) | interface ScriptParams {
type ScreenshotParams (line 450) | interface ScreenshotParams {
type HttpMethod (line 458) | type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
type HttpHeaders (line 459) | type HttpHeaders = Record<string, Resolvable<string>>;
type HttpFormData (line 460) | type HttpFormData = Record<string, Resolvable<string>>;
type HttpBody (line 462) | type HttpBody =
type HttpOkStatus (line 467) | type HttpOkStatus =
type HttpParams (line 471) | interface HttpParams {
type TriggerEventParams (line 484) | interface TriggerEventParams {
type SetAttributeParams (line 491) | interface SetAttributeParams {
type SwitchFrameParams (line 498) | interface SwitchFrameParams {
type LoopElementsParams (line 502) | interface LoopElementsParams {
type OpenTabParams (line 511) | interface OpenTabParams {
type SwitchTabParams (line 516) | interface SwitchTabParams {
type CloseTabParams (line 522) | interface CloseTabParams {
type HandleDownloadParams (line 527) | interface HandleDownloadParams {
type ExecuteFlowParams (line 535) | interface ExecuteFlowParams {
type ForeachParams (line 541) | interface ForeachParams {
type WhileParams (line 548) | interface WhileParams {
type IfBranch (line 554) | interface IfBranch {
type IfParams (line 560) | type IfParams =
type DelayParams (line 573) | interface DelayParams {
type TriggerUrlRuleKind (line 579) | type TriggerUrlRuleKind = 'url' | 'domain' | 'path';
type TriggerUrlRule (line 581) | interface TriggerUrlRule {
type TriggerUrlConfig (line 586) | interface TriggerUrlConfig {
type TriggerModeConfig (line 590) | interface TriggerModeConfig {
type TriggerContextMenuConfig (line 599) | interface TriggerContextMenuConfig {
type TriggerCommandConfig (line 604) | interface TriggerCommandConfig {
type TriggerDomConfig (line 609) | interface TriggerDomConfig {
type TriggerScheduleType (line 617) | type TriggerScheduleType = 'once' | 'interval' | 'daily';
type TriggerSchedule (line 619) | interface TriggerSchedule {
type TriggerParams (line 626) | interface TriggerParams {
type ActionParamsByType (line 645) | interface ActionParamsByType {
type ActionType (line 690) | type ActionType = keyof ActionParamsByType;
type ActionBase (line 692) | interface ActionBase<T extends ActionType> {
type Action (line 702) | type Action<T extends ActionType = ActionType> = ActionBase<T> & {
type AnyAction (line 706) | type AnyAction = { [T in ActionType]: Action<T> }[ActionType];
type ExecutableActionType (line 708) | type ExecutableActionType = Exclude<ActionType, 'trigger'>;
type ExecutableAction (line 709) | type ExecutableAction<T extends ExecutableActionType = ExecutableActionT...
type HttpResponse (line 715) | interface HttpResponse {
type DownloadState (line 722) | type DownloadState = 'in_progress' | 'complete' | 'interrupted' | 'cance...
type DownloadInfo (line 724) | interface DownloadInfo {
type ActionOutputsByType (line 735) | interface ActionOutputsByType {
type ActionOutput (line 744) | type ActionOutput<T extends ActionType> = T extends keyof ActionOutputsB...
type ValidationResult (line 752) | type ValidationResult = { ok: true } | { ok: false; errors: NonEmptyArra...
type ExecutionFlags (line 758) | interface ExecutionFlags {
type ActionExecutionContext (line 766) | interface ActionExecutionContext {
type ControlDirective (line 787) | type ControlDirective =
type ActionExecutionResult (line 802) | interface ActionExecutionResult<T extends ActionType = ActionType> {
type ActionHandler (line 822) | interface ActionHandler<T extends ExecutableActionType = ExecutableActio...
type ActionEdge (line 836) | interface ActionEdge {
type FlowBinding (line 843) | interface FlowBinding {
type FlowMeta (line 848) | interface FlowMeta {
type Flow (line 858) | interface Flow {
type ActionCategory (line 881) | type ActionCategory = 'Flow' | 'Actions' | 'Logic' | 'Tools' | 'Tabs' | ...
type ActionSpecDisplay (line 883) | interface ActionSpecDisplay {
type ActionSpecPorts (line 891) | interface ActionSpecPorts {
type ActionSpec (line 898) | interface ActionSpec<T extends ActionType = ActionType> {
constant ACTION_TYPES (line 912) | const ACTION_TYPES: ReadonlyArray<ActionType> = [
constant EXECUTABLE_ACTION_TYPES (line 942) | const EXECUTABLE_ACTION_TYPES: ReadonlyArray<ExecutableActionType> = ACT...
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/constants.ts
constant ENGINE_CONSTANTS (line 4) | const ENGINE_CONSTANTS = {
type EdgeLabel (line 13) | type EdgeLabel =
constant LOG_STEP_IDS (line 17) | const LOG_STEP_IDS = {
type LogStepId (line 31) | type LogStepId = (typeof LOG_STEP_IDS)[keyof typeof LOG_STEP_IDS];
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/execution-mode.ts
type ExecutionMode (line 18) | type ExecutionMode = 'legacy' | 'actions' | 'hybrid';
type ExecutionModeConfig (line 23) | interface ExecutionModeConfig {
constant DEFAULT_EXECUTION_MODE_CONFIG (line 71) | const DEFAULT_EXECUTION_MODE_CONFIG: ExecutionModeConfig = {
constant MINIMAL_HYBRID_ACTION_TYPES (line 90) | const MINIMAL_HYBRID_ACTION_TYPES = new Set<string>([
constant MIGRATED_ACTION_TYPES (line 113) | const MIGRATED_ACTION_TYPES = new Set<string>([
constant NEEDS_VALIDATION_TYPES (line 136) | const NEEDS_VALIDATION_TYPES = new Set<string>([
constant LEGACY_ONLY_TYPES (line 159) | const LEGACY_ONLY_TYPES = new Set<string>([
function shouldUseActions (line 170) | function shouldUseActions(step: Step, config: ExecutionModeConfig): bool...
function createHybridConfig (line 208) | function createHybridConfig(overrides?: Partial<ExecutionModeConfig>): E...
function createActionsOnlyConfig (line 226) | function createActionsOnlyConfig(
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/logging/run-logger.ts
class RunLogger (line 7) | class RunLogger {
method constructor (line 9) | constructor(private runId: string) {}
method push (line 11) | push(e: RunLogEntry) {
method getLogs (line 15) | getLogs() {
method overlayInit (line 19) | async overlayInit() {
method overlayAppend (line 27) | async overlayAppend(text: string) {
method overlayDone (line 39) | async overlayDone() {
method screenshotOnFailure (line 47) | async screenshotOnFailure() {
method persist (line 58) | async persist(flow: Flow, startedAt: number, success: boolean) {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/plugins/breakpoint.ts
function breakpointPlugin (line 4) | function breakpointPlugin(): RunPlugin {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/plugins/manager.ts
class PluginManager (line 13) | class PluginManager {
method constructor (line 14) | constructor(private plugins: RunPlugin[]) {}
method runStart (line 16) | async runStart(ctx: RunContext) {
method beforeStep (line 20) | async beforeStep(ctx: StepContext): Promise<HookControl | undefined> {
method afterStep (line 28) | async afterStep(ctx: StepAfterContext) {
method onError (line 32) | async onError(ctx: StepErrorContext): Promise<HookControl | undefined> {
method onRetry (line 40) | async onRetry(ctx: StepRetryContext) {
method onChooseNextLabel (line 44) | async onChooseNextLabel(ctx: StepContext & { suggested?: string }): Pr...
method subflowStart (line 52) | async subflowStart(ctx: SubflowContext) {
method subflowEnd (line 56) | async subflowEnd(ctx: SubflowContext) {
method runEnd (line 60) | async runEnd(ctx: RunEndContext) {
function safeCall (line 65) | async function safeCall<T extends keyof RunPlugin>(plugin: RunPlugin, ke...
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/plugins/types.ts
type RunContext (line 7) | interface RunContext {
type StepContext (line 13) | interface StepContext extends RunContext {
type StepErrorContext (line 17) | interface StepErrorContext extends StepContext {
type StepRetryContext (line 21) | interface StepRetryContext extends StepErrorContext {
type StepAfterContext (line 25) | interface StepAfterContext extends StepContext {
type SubflowContext (line 29) | interface SubflowContext extends RunContext {
type RunEndContext (line 33) | interface RunEndContext extends RunContext {
type HookControl (line 38) | interface HookControl {
type RunPlugin (line 43) | interface RunPlugin {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/policies/retry.ts
type BackoffKind (line 3) | type BackoffKind = 'none' | 'exp';
type RetryOptions (line 5) | interface RetryOptions {
function withRetry (line 11) | async function withRetry<T>(
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/policies/wait.ts
function waitForNavigationDone (line 8) | async function waitForNavigationDone(prevUrl: string, timeoutMs?: number) {
function ensureReadPageIfWeb (line 12) | async function ensureReadPageIfWeb() {
function maybeQuickWaitForNav (line 22) | async function maybeQuickWaitForNav(prevUrl: string, timeoutMs?: number) {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/runners/after-script-queue.ts
class AfterScriptQueue (line 12) | class AfterScriptQueue {
method constructor (line 15) | constructor(private logger: RunLogger) {}
method enqueue (line 17) | enqueue(script: StepScript) {
method size (line 21) | size() {
method flush (line 25) | async flush(ctx: ExecCtx, vars: Record<string, any>) {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/runners/control-flow-runner.ts
type ControlFlowEnv (line 6) | interface ControlFlowEnv {
class ControlFlowRunner (line 14) | class ControlFlowRunner {
method constructor (line 15) | constructor(private env: ControlFlowEnv) {}
method run (line 17) | async run(control: any, ctx: ExecCtx): Promise<'ok' | 'paused'> {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/runners/step-executor.ts
type StepExecutionResult (line 29) | interface StepExecutionResult {
type StepExecutionOptions (line 43) | interface StepExecutionOptions {
type StepExecutorInterface (line 57) | interface StepExecutorInterface {
class LegacyStepExecutor (line 76) | class LegacyStepExecutor implements StepExecutorInterface {
method execute (line 77) | async execute(
method supports (line 92) | supports(_stepType: string): boolean {
class ActionsStepExecutor (line 108) | class ActionsStepExecutor implements StepExecutorInterface {
method constructor (line 111) | constructor(
method execute (line 118) | async execute(
method supports (line 146) | supports(stepType: string): boolean {
class HybridStepExecutor (line 161) | class HybridStepExecutor implements StepExecutorInterface {
method constructor (line 164) | constructor(
method execute (line 171) | async execute(
method supports (line 221) | supports(stepType: string): boolean {
function createExecutor (line 230) | function createExecutor(
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/runners/step-runner.ts
type ErrorLike (line 27) | interface ErrorLike {
function errorMessage (line 31) | function errorMessage(e: unknown): string {
type StepRunEnv (line 41) | interface StepRunEnv {
class StepRunner (line 64) | class StepRunner {
method constructor (line 65) | constructor(private env: StepRunEnv) {}
method getActiveTabInfo (line 67) | private async getActiveTabInfo(): Promise<{ url: string; status: strin...
method run (line 73) | async run(
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/runners/subflow-runner.ts
type SubflowEnv (line 12) | interface SubflowEnv {
class SubflowRunner (line 21) | class SubflowRunner {
method constructor (line 22) | constructor(private env: SubflowEnv) {}
method runSubflowById (line 24) | async runSubflowById(subflowId: string, ctx: ExecCtx, pausedRef: () =>...
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/scheduler.ts
type RunOptions (line 34) | interface RunOptions {
function isExecutionMode (line 71) | function isExecutionMode(value: unknown): value is ExecutionMode {
function toStringSet (line 78) | function toStringSet(value: unknown): Set<string> {
function buildExecutionModeConfig (line 98) | function buildExecutionModeConfig(options: RunOptions): ExecutionModeCon...
class ExecutionOrchestrator (line 133) | class ExecutionOrchestrator {
method constructor (line 160) | constructor(
method ensureWithinDeadline (line 203) | private ensureWithinDeadline() {
method run (line 215) | async run(): Promise<RunResult> {
method prepareExecution (line 225) | private async prepareExecution() {
method hasCycle (line 476) | private hasCycle(
method traverseDag (line 500) | private async traverseDag(): Promise<RunResult> {
method advanceToNext (line 685) | private async advanceToNext(
method chooseNextLabel (line 714) | private async chooseNextLabel(step: Step, suggested: string): Promise<...
method findNextNodeId (line 735) | private findNextNodeId(
method evalCondition (line 747) | private evalCondition(cond: any): boolean {
method cleanup (line 763) | private async cleanup() {
function runFlow (line 843) | async function runFlow(flow: Flow, options: RunOptions = {}): Promise<Ru...
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/state-manager.ts
type Listener (line 3) | type Listener<T> = (payload: T) => void;
type RunState (line 5) | interface RunState {
class StateManager (line 14) | class StateManager<T extends { id: string }> {
method constructor (line 19) | constructor(storageKey: string) {
method on (line 23) | on<E = any>(name: string, listener: Listener<E>) {
method off (line 27) | off<E = any>(name: string, listener: Listener<E>) {
method emit (line 34) | private emit<E = any>(name: string, payload: E) {
method getAll (line 42) | getAll(): Map<string, T> {
method get (line 46) | get(id: string): T | undefined {
method add (line 50) | async add(id: string, data: T): Promise<void> {
method update (line 56) | async update(id: string, patch: Partial<T>): Promise<void> {
method delete (line 65) | async delete(id: string): Promise<void> {
method persist (line 71) | private async persist(): Promise<void> {
method restore (line 78) | async restore(): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/record-replay/engine/utils/expression.ts
type Token (line 8) | type Token = { type: string; value?: any };
function tokenize (line 10) | function tokenize(input: string): Token[] {
function evalExpression (line 92) | function evalExpression(expr: string, scope: { vars: Record<string, any>...
FILE: app/chrome-extension/entrypoints/background/record-replay/flow-store.ts
constant VALID_NODE_TYPES (line 11) | const VALID_NODE_TYPES = new Set<string>(Object.values(NODE_TYPES));
function isValidNodeType (line 12) | function isValidNodeType(type: string): boolean {
function toNodeBase (line 17) | function toNodeBase(node: RRNode): NodeBase {
function toEdge (line 26) | function toEdge(edge: RREdge): Edge {
function filterValidEdges (line 39) | function filterValidEdges(edges: Edge[], nodeIds: Set<string>): Edge[] {
function notifyFlowsChanged (line 57) | function notifyFlowsChanged(): void {
function stripStepsForSave (line 86) | function stripStepsForSave(flow: Flow): Flow {
function normalizeFlowForSave (line 103) | function normalizeFlowForSave(flow: Flow): Flow {
type PublishedFlowInfo (line 148) | interface PublishedFlowInfo {
function needsNormalization (line 159) | function needsNormalization(flow: Flow): boolean {
function lazyNormalize (line 170) | async function lazyNormalize(flow: Flow): Promise<Flow> {
function listFlows (line 186) | async function listFlows(): Promise<Flow[]> {
function getFlow (line 208) | async function getFlow(flowId: string): Promise<Flow | undefined> {
function saveFlow (line 220) | async function saveFlow(flow: Flow, options?: { notify?: boolean }): Pro...
function deleteFlow (line 233) | async function deleteFlow(flowId: string): Promise<void> {
function listRuns (line 239) | async function listRuns(): Promise<RunRecord[]> {
function appendRun (line 244) | async function appendRun(record: RunRecord): Promise<void> {
function listPublished (line 269) | async function listPublished(): Promise<PublishedFlowInfo[]> {
function publishFlow (line 274) | async function publishFlow(flow: Flow, slug?: string): Promise<Published...
function unpublishFlow (line 287) | async function unpublishFlow(flowId: string): Promise<void> {
function toSlug (line 292) | function toSlug(name: string): string {
function exportFlow (line 300) | async function exportFlow(flowId: string): Promise<string> {
function exportAllFlows (line 306) | async function exportAllFlows(): Promise<string> {
function importFlowFromJson (line 322) | async function importFlowFromJson(json: string): Promise<Flow[]> {
type ScheduleType (line 397) | type ScheduleType = 'once' | 'interval' | 'daily';
type FlowSchedule (line 398) | interface FlowSchedule {
function listSchedules (line 409) | async function listSchedules(): Promise<FlowSchedule[]> {
function saveSchedule (line 414) | async function saveSchedule(s: FlowSchedule): Promise<void> {
function removeSchedule (line 419) | async function removeSchedule(scheduleId: string): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/record-replay/index.ts
function rescheduleAlarms (line 29) | async function rescheduleAlarms() {
function startRecording (line 63) | async function startRecording(meta?: Partial<Flow>): Promise<{ success: ...
function stopRecording (line 67) | async function stopRecording(): Promise<{ success: boolean; flow?: Flow;...
function initRecordReplayListeners (line 71) | function initRecordReplayListeners() {
function matchUrl (line 368) | function matchUrl(
function refreshContextMenus (line 387) | async function refreshContextMenus(triggers: FlowTrigger[]) {
function removeRecordReplayMenus (line 412) | async function removeRecordReplayMenus() {
function refreshTriggers (line 426) | async function refreshTriggers() {
function initTriggerEngine (line 459) | async function initTriggerEngine() {
function ensureCoreInjected (line 464) | async function ensureCoreInjected(tabId?: number) {
function pingTab (line 479) | async function pingTab(tabId: number, action: string): Promise<boolean> {
FILE: app/chrome-extension/entrypoints/background/record-replay/legacy-types.ts
type SelectorType (line 23) | type SelectorType = 'css' | 'xpath' | 'attr' | 'aria' | 'text';
type SelectorCandidate (line 25) | interface SelectorCandidate {
type TargetLocator (line 31) | interface TargetLocator {
type StepType (line 40) | type StepType = (typeof STEP_TYPES)[keyof typeof STEP_TYPES];
type StepBase (line 42) | interface StepBase {
type StepClick (line 50) | interface StepClick extends StepBase {
type StepFill (line 57) | interface StepFill extends StepBase {
type StepTriggerEvent (line 63) | interface StepTriggerEvent extends StepBase {
type StepSetAttribute (line 71) | interface StepSetAttribute extends StepBase {
type StepScreenshot (line 79) | interface StepScreenshot extends StepBase {
type StepSwitchFrame (line 86) | interface StepSwitchFrame extends StepBase {
type StepLoopElements (line 91) | interface StepLoopElements extends StepBase {
type StepKey (line 99) | interface StepKey extends StepBase {
type StepScroll (line 105) | interface StepScroll extends StepBase {
type StepDrag (line 112) | interface StepDrag extends StepBase {
type StepWait (line 119) | interface StepWait extends StepBase {
type StepAssert (line 129) | interface StepAssert extends StepBase {
type StepScript (line 140) | interface StepScript extends StepBase {
type StepIf (line 147) | interface StepIf extends StepBase {
type StepForeach (line 153) | interface StepForeach extends StepBase {
type StepWhile (line 160) | interface StepWhile extends StepBase {
type StepHttp (line 167) | interface StepHttp extends StepBase {
type StepExtract (line 178) | interface StepExtract extends StepBase {
type StepOpenTab (line 186) | interface StepOpenTab extends StepBase {
type StepSwitchTab (line 192) | interface StepSwitchTab extends StepBase {
type StepCloseTab (line 199) | interface StepCloseTab extends StepBase {
type StepNavigate (line 205) | interface StepNavigate extends StepBase {
type StepHandleDownload (line 210) | interface StepHandleDownload extends StepBase {
type StepExecuteFlow (line 217) | interface StepExecuteFlow extends StepBase {
type Step (line 228) | type Step =
FILE: app/chrome-extension/entrypoints/background/record-replay/nodes/index.ts
function executeStep (line 56) | async function executeStep(ctx: ExecCtx, step: Step): Promise<ExecResult> {
FILE: app/chrome-extension/entrypoints/background/record-replay/nodes/types.ts
type ExecCtx (line 7) | interface ExecCtx {
type ExecResult (line 24) | interface ExecResult {
type NodeRuntime (line 33) | interface NodeRuntime<S extends Step = Step> {
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/browser-event-listener.ts
function initBrowserEventListeners (line 7) | function initBrowserEventListeners(session: RecordingSessionManager): vo...
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/content-injection.ts
type RecorderCmd (line 4) | type RecorderCmd = 'start' | 'stop' | 'pause' | 'resume';
constant REC_CMD (line 5) | const REC_CMD = {
constant RECORDER_JS_SCRIPT (line 12) | const RECORDER_JS_SCRIPT = 'inject-scripts/recorder.js';
function ensureRecorderInjected (line 14) | async function ensureRecorderInjected(tabId: number): Promise<void> {
function broadcastControlToTab (line 68) | async function broadcastControlToTab(
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/content-message-handler.ts
function initContentMessageHandler (line 13) | function initContentMessageHandler(session: RecordingSessionManager): vo...
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/flow-builder.ts
constant WORKFLOW_VERSION (line 6) | const WORKFLOW_VERSION = 1;
function createInitialFlow (line 12) | function createInitialFlow(meta?: Partial<Flow>): Flow {
function generateStepId (line 30) | function generateStepId(): string {
function addNavigationStep (line 39) | function addNavigationStep(flow: Flow, url: string): void {
function appendNodeToFlow (line 62) | function appendNodeToFlow(flow: Flow, step: Step): void {
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/recorder-manager.ts
constant STOP_BARRIER_TOP_TIMEOUT_MS (line 10) | const STOP_BARRIER_TOP_TIMEOUT_MS = 5000;
constant STOP_BARRIER_SUBFRAME_TIMEOUT_MS (line 13) | const STOP_BARRIER_SUBFRAME_TIMEOUT_MS = 1500;
constant STOP_BARRIER_GRACE_MS (line 16) | const STOP_BARRIER_GRACE_MS = 150;
type StopAckStats (line 19) | interface StopAckStats {
type StopFrameAck (line 25) | interface StopFrameAck {
type StopTabBarrierResult (line 33) | interface StopTabBarrierResult {
function listFrameIds (line 45) | async function listFrameIds(tabId: number): Promise<number[]> {
function sendStopToFrameWithAck (line 61) | async function sendStopToFrameWithAck(
function stopTabWithBarrier (line 100) | async function stopTabWithBarrier(tabId: number, sessionId: string): Pro...
class RecorderManagerImpl (line 129) | class RecorderManagerImpl {
method init (line 132) | async init(): Promise<void> {
method start (line 139) | async start(meta?: Partial<Flow>): Promise<{ success: boolean; error?:...
method stop (line 190) | async stop(): Promise<{ success: boolean; error?: string; flow?: Flow ...
method pause (line 267) | async pause(): Promise<{ success: boolean; error?: string }> {
method resume (line 288) | async resume(): Promise<{ success: boolean; error?: string }> {
FILE: app/chrome-extension/entrypoints/background/record-replay/recording/session-manager.ts
type RecordingStatus (line 13) | type RecordingStatus = 'idle' | 'recording' | 'paused' | 'stopping';
type RecordingSessionState (line 15) | interface RecordingSessionState {
constant VALID_NODE_TYPES (line 27) | const VALID_NODE_TYPES = new Set<string>(Object.values(NODE_TYPES));
class RecordingSessionManager (line 29) | class RecordingSessionManager {
method getStatus (line 45) | getStatus(): RecordingStatus {
method getSession (line 49) | getSession(): Readonly<RecordingSessionState> {
method getFlow (line 53) | getFlow(): Flow | null {
method getOriginTabId (line 57) | getOriginTabId(): number | null {
method addActiveTab (line 61) | addActiveTab(tabId: number): void {
method removeActiveTab (line 65) | removeActiveTab(tabId: number): void {
method getActiveTabs (line 69) | getActiveTabs(): number[] {
method startSession (line 73) | async startSession(flow: Flow, originTabId: number): Promise<void> {
method beginStopping (line 95) | beginStopping(): string {
method markTabStopped (line 106) | markTabStopped(tabId: number): boolean {
method isStopping (line 120) | isStopping(): boolean {
method canAcceptSteps (line 127) | canAcceptSteps(): boolean {
method pause (line 134) | pause(): void {
method resume (line 143) | resume(): void {
method stopSession (line 152) | async stopSession(): Promise<Flow | null> {
method updateFlow (line 165) | updateFlow(mutator: (f: Flow) => void): void {
method appendSteps (line 188) | appendSteps(steps: Step[]): void {
method toNodeType (line 281) | private toNodeType(stepType: string): NodeBase['type'] {
method checkDagInvariant (line 294) | private checkDagInvariant(nodes: NodeBase[], edges: Edge[]): boolean {
method rebuildCaches (line 319) | private rebuildCaches(): void {
method rebuildDagFromSteps (line 340) | private rebuildDagFromSteps(): void {
method rechainEdges (line 377) | private rechainEdges(): void {
method appendVariables (line 404) | appendVariables(variables: VariableDef[]): void {
method getTimelineSteps (line 442) | private getTimelineSteps(): Step[] {
method broadcastTimelineUpdate (line 468) | broadcastTimelineUpdate(): void {
FILE: app/chrome-extension/entrypoints/background/record-replay/rr-utils.ts
function applyAssign (line 13) | function applyAssign(
function expandTemplatesDeep (line 39) | function expandTemplatesDeep<T = any>(value: T, scope: Record<string, an...
function ensureTab (line 56) | async function ensureTab(options: {
function waitForNetworkIdle (line 102) | async function waitForNetworkIdle(totalTimeoutMs: number, idleThresholdM...
function waitForNavigation (line 144) | async function waitForNavigation(timeoutMs?: number, prevUrl?: string): ...
function topoOrder (line 234) | function topoOrder(nodes: DagNode[], edges: DagEdge[]): DagNode[] {
function defaultEdgesOnly (line 239) | function defaultEdgesOnly(edges: DagEdge[] = []): DagEdge[] {
function mapDagNodeToStep (line 243) | function mapDagNodeToStep(n: DagNode): Step {
FILE: app/chrome-extension/entrypoints/background/record-replay/selector-engine.ts
type LocatedElement (line 6) | interface LocatedElement {
function isCompositeSelector (line 14) | function isCompositeSelector(sel: string): boolean {
function sendToTab (line 19) | async function sendToTab(tabId: number, message: any, frameId?: number):...
function ensureRefForSelector (line 27) | async function ensureRefForSelector(
function locateElement (line 66) | async function locateElement(
FILE: app/chrome-extension/entrypoints/background/record-replay/storage/indexeddb-manager.ts
type StoreName (line 11) | type StoreName = 'flows' | 'runs' | 'published' | 'schedules' | 'triggers';
constant DB_NAME (line 13) | const DB_NAME = 'rr_storage';
constant DB_VERSION (line 18) | const DB_VERSION = 3;
constant REQUIRED_STORES (line 20) | const REQUIRED_STORES = ['flows', 'runs', 'published', 'schedules', 'tri...
function getAll (line 38) | async function getAll<T>(store: StoreName): Promise<T[]> {
function getOne (line 42) | async function getOne<T>(store: StoreName, key: string): Promise<T | und...
function putOne (line 46) | async function putOne<T>(store: StoreName, value: T): Promise<void> {
function deleteOne (line 50) | async function deleteOne(store: StoreName, key: string): Promise<void> {
function clearStore (line 54) | async function clearStore(store: StoreName): Promise<void> {
function putMany (line 58) | async function putMany<T>(storeName: StoreName, values: T[]): Promise<vo...
method list (line 64) | async list(): Promise<Flow[]> {
method get (line 67) | async get(id: string): Promise<Flow | undefined> {
method save (line 70) | async save(flow: Flow): Promise<void> {
method delete (line 73) | async delete(id: string): Promise<void> {
method list (line 78) | async list(): Promise<RunRecord[]> {
method save (line 81) | async save(record: RunRecord): Promise<void> {
method replaceAll (line 84) | async replaceAll(records: RunRecord[]): Promise<void> {
method list (line 93) | async list(): Promise<PublishedFlowInfo[]> {
method save (line 96) | async save(info: PublishedFlowInfo): Promise<void> {
method delete (line 99) | async delete(id: string): Promise<void> {
method list (line 104) | async list(): Promise<FlowSchedule[]> {
method save (line 107) | async save(s: FlowSchedule): Promise<void> {
method delete (line 110) | async delete(id: string): Promise<void> {
method list (line 115) | async list(): Promise<FlowTrigger[]> {
method save (line 118) | async save(t: FlowTrigger): Promise<void> {
method delete (line 121) | async delete(id: string): Promise<void> {
function ensureMigratedFromLocal (line 131) | async function ensureMigratedFromLocal(): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/record-replay/trigger-store.ts
type TriggerType (line 3) | type TriggerType = 'url' | 'contextMenu' | 'command' | 'dom';
type BaseTrigger (line 5) | interface BaseTrigger {
type UrlTrigger (line 13) | interface UrlTrigger extends BaseTrigger {
type ContextMenuTrigger (line 18) | interface ContextMenuTrigger extends BaseTrigger {
type CommandTrigger (line 24) | interface CommandTrigger extends BaseTrigger {
type DomTrigger (line 29) | interface DomTrigger extends BaseTrigger {
type FlowTrigger (line 37) | type FlowTrigger = UrlTrigger | ContextMenuTrigger | CommandTrigger | Do...
function listTriggers (line 39) | async function listTriggers(): Promise<FlowTrigger[]> {
function saveTrigger (line 44) | async function saveTrigger(t: FlowTrigger): Promise<void> {
function deleteTrigger (line 49) | async function deleteTrigger(id: string): Promise<void> {
function toId (line 54) | function toId(prefix = 'trg') {
FILE: app/chrome-extension/entrypoints/background/record-replay/types.ts
type VariableType (line 62) | type VariableType = 'string' | 'number' | 'boolean' | 'enum' | 'array';
type VariableDef (line 64) | interface VariableDef {
type NodeType (line 78) | type NodeType = (typeof NODE_TYPES)[keyof typeof NODE_TYPES];
type NodeBase (line 80) | interface NodeBase {
type Edge (line 89) | interface Edge {
type Flow (line 102) | interface Flow {
type RunLogEntry (line 146) | interface RunLogEntry {
type RunRecord (line 160) | interface RunRecord {
type RunResult (line 169) | interface RunResult {
FILE: app/chrome-extension/entrypoints/background/semantic-similarity.ts
type ModelConfig (line 10) | interface ModelConfig {
function initializeSemanticEngineIfCached (line 22) | async function initializeSemanticEngineIfCached(): Promise<boolean> {
function initializeDefaultSemanticEngine (line 44) | async function initializeDefaultSemanticEngine(): Promise<void> {
function needsModelSwitch (line 113) | function needsModelSwitch(
function handleModelSwitch (line 141) | async function handleModelSwitch(
function handleGetModelStatus (line 215) | async function handleGetModelStatus(): Promise<{
function updateModelStatus (line 262) | async function updateModelStatus(
function handleUpdateModelStatus (line 292) | async function handleUpdateModelStatus(
function analyzeErrorType (line 313) | function analyzeErrorType(errorMessage: string): 'network' | 'file' | 'u...
FILE: app/chrome-extension/entrypoints/background/storage-manager.ts
function handleGetStorageStats (line 6) | async function handleGetStorageStats(): Promise<{
function handleClearAllData (line 55) | async function handleClearAllData(): Promise<{ success: boolean; error?:...
FILE: app/chrome-extension/entrypoints/background/tools/base-browser.ts
constant PING_TIMEOUT_MS (line 5) | const PING_TIMEOUT_MS = 300;
method injectContentScript (line 17) | protected async injectContentScript(
method sendMessageToTab (line 89) | protected async sendMessageToTab(tabId: number, message: any, frameId?: ...
method tryGetTab (line 117) | protected async tryGetTab(tabId?: number): Promise<chrome.tabs.Tab | nul...
method getActiveTabOrThrow (line 129) | protected async getActiveTabOrThrow(): Promise<chrome.tabs.Tab> {
method ensureFocus (line 139) | protected async ensureFocus(
method getActiveTabInWindow (line 156) | protected async getActiveTabInWindow(windowId?: number): Promise<chrome....
method getActiveTabOrThrowInWindow (line 168) | protected async getActiveTabOrThrowInWindow(windowId?: number): Promise<...
FILE: app/chrome-extension/entrypoints/background/tools/browser/bookmark.ts
type BookmarkSearchToolParams (line 9) | interface BookmarkSearchToolParams {
type BookmarkAddToolParams (line 18) | interface BookmarkAddToolParams {
type BookmarkDeleteToolParams (line 28) | interface BookmarkDeleteToolParams {
function getBookmarkFolderPath (line 41) | async function getBookmarkFolderPath(bookmarkNodeId: string): Promise<st...
function findFolderByPathOrId (line 80) | async function findFolderByPathOrId(
function createFolderPath (line 135) | async function createFolderPath(
function flattenBookmarkNodesToBookmarks (line 201) | function flattenBookmarkNodesToBookmarks(
function findBookmarksByUrl (line 233) | async function findBookmarksByUrl(
class BookmarkSearchTool (line 255) | class BookmarkSearchTool extends BaseBrowserToolExecutor {
method execute (line 261) | async execute(args: BookmarkSearchToolParams): Promise<ToolResult> {
class BookmarkAddTool (line 362) | class BookmarkAddTool extends BaseBrowserToolExecutor {
method execute (line 368) | async execute(args: BookmarkAddToolParams): Promise<ToolResult> {
class BookmarkDeleteTool (line 500) | class BookmarkDeleteTool extends BaseBrowserToolExecutor {
method execute (line 506) | async execute(args: BookmarkDeleteToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/common.ts
constant DEFAULT_WINDOW_WIDTH (line 7) | const DEFAULT_WINDOW_WIDTH = 1280;
constant DEFAULT_WINDOW_HEIGHT (line 8) | const DEFAULT_WINDOW_HEIGHT = 720;
type NavigateToolParams (line 10) | interface NavigateToolParams {
class NavigateTool (line 24) | class NavigateTool extends BaseBrowserToolExecutor {
method triggerAutoCapture (line 30) | private async triggerAutoCapture(tabId: number, url?: string): Promise...
method execute (line 41) | async execute(args: NavigateToolParams): Promise<ToolResult> {
type CloseTabsToolParams (line 444) | interface CloseTabsToolParams {
class CloseTabsTool (line 452) | class CloseTabsTool extends BaseBrowserToolExecutor {
method execute (line 455) | async execute(args: CloseTabsToolParams): Promise<ToolResult> {
type SwitchTabToolParams (line 626) | interface SwitchTabToolParams {
class SwitchTabTool (line 634) | class SwitchTabTool extends BaseBrowserToolExecutor {
method execute (line 637) | async execute(args: SwitchTabToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/computer.ts
type MouseButton (line 18) | type MouseButton = 'left' | 'right' | 'middle';
type Coordinates (line 20) | interface Coordinates {
type ZoomRegion (line 25) | interface ZoomRegion {
type Modifiers (line 32) | interface Modifiers {
type ComputerParams (line 39) | interface ComputerParams {
class CDPHelper (line 81) | class CDPHelper {
method attach (line 82) | static async attach(tabId: number): Promise<void> {
method detach (line 86) | static async detach(tabId: number): Promise<void> {
method send (line 90) | static async send(tabId: number, method: string, params?: object): Pro...
method dispatchMouseEvent (line 94) | static async dispatchMouseEvent(tabId: number, opts: any) {
method insertText (line 120) | static async insertText(tabId: number, text: string) {
method modifierMask (line 124) | static modifierMask(mods: string[]): number {
method resolveKeyDef (line 161) | private static resolveKeyDef(token: string): { key: string; code?: str...
method dispatchSimpleKey (line 174) | static async dispatchSimpleKey(tabId: number, token: string) {
method dispatchKeyChord (line 192) | static async dispatchKeyChord(tabId: number, chord: string) {
class ComputerTool (line 222) | class ComputerTool extends BaseBrowserToolExecutor {
method execute (line 225) | async execute(args: ComputerParams): Promise<ToolResult> {
method mapActionToCapture (line 275) | private mapActionToCapture(action: string): ActionType | null {
method executeAction (line 295) | private async executeAction(params: ComputerParams, tab: chrome.tabs.T...
method domHoverFallback (line 1298) | private async domHoverFallback(
method triggerAutoCapture (line 1406) | private async triggerAutoCapture(
FILE: app/chrome-extension/entrypoints/background/tools/browser/console-buffer.ts
constant DEFAULT_MAX_BUFFER_MESSAGES (line 10) | const DEFAULT_MAX_BUFFER_MESSAGES = 2000;
constant DEFAULT_MAX_BUFFER_EXCEPTIONS (line 11) | const DEFAULT_MAX_BUFFER_EXCEPTIONS = 500;
type BufferedConsoleMessage (line 13) | interface BufferedConsoleMessage {
type BufferedConsoleException (line 24) | interface BufferedConsoleException {
type TabConsoleBufferState (line 33) | interface TabConsoleBufferState {
type ConsoleBufferReadOptions (line 45) | interface ConsoleBufferReadOptions {
type ConsoleBufferReadResult (line 52) | interface ConsoleBufferReadResult {
function extractHostname (line 70) | function extractHostname(url?: string): string {
function isErrorLevel (line 79) | function isErrorLevel(level?: string): boolean {
function matchesPattern (line 84) | function matchesPattern(pattern: RegExp, text: string): boolean {
function formatConsoleArgs (line 89) | function formatConsoleArgs(args: unknown[]): string {
function extractArgPreview (line 109) | function extractArgPreview(arg: unknown): unknown {
function safeTimestamp (line 127) | function safeTimestamp(value: unknown): number {
function safeString (line 134) | function safeString(value: unknown): string {
function safeNumber (line 138) | function safeNumber(value: unknown): number | undefined {
class ConsoleBuffer (line 142) | class ConsoleBuffer {
method constructor (line 147) | constructor() {
method isCapturing (line 162) | isCapturing(tabId: number): boolean {
method ensureStarted (line 169) | async ensureStarted(tabId: number): Promise<void> {
method clear (line 185) | clear(
method read (line 212) | read(tabId: number, options: ConsoleBufferReadOptions = {}): ConsoleBu...
method startCapture (line 274) | private async startCapture(tabId: number): Promise<void> {
method handleTabRemoved (line 305) | private handleTabRemoved(tabId: number): void {
method handleTabUpdated (line 310) | private handleTabUpdated(
method handleDebuggerDetach (line 336) | private handleDebuggerDetach(source: chrome.debugger.Debuggee, reason:...
method handleDebuggerEvent (line 349) | private handleDebuggerEvent(
method trimMessages (line 415) | private trimMessages(state: TabConsoleBufferState): void {
method trimExceptions (line 422) | private trimExceptions(state: TabConsoleBufferState): void {
method stopCapture (line 429) | private async stopCapture(tabId: number, reason: string): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/console.ts
constant DEFAULT_MAX_MESSAGES (line 7) | const DEFAULT_MAX_MESSAGES = 100;
type ConsoleMode (line 9) | type ConsoleMode = 'snapshot' | 'buffer';
type ConsoleToolParams (line 11) | interface ConsoleToolParams {
type ConsoleMessage (line 28) | interface ConsoleMessage {
type ConsoleException (line 40) | interface ConsoleException {
type ConsoleResult (line 49) | interface ConsoleResult {
function normalizeLimit (line 69) | function normalizeLimit(value: unknown, fallback: number): number {
function parseRegexPattern (line 74) | function parseRegexPattern(pattern?: string): RegExp | undefined {
function matchesPattern (line 88) | function matchesPattern(pattern: RegExp, text: string): boolean {
function isErrorLevel (line 93) | function isErrorLevel(level?: string): boolean {
function applyResultFilters (line 98) | function applyResultFilters(
function isDebuggerConflictError (line 126) | function isDebuggerConflictError(error: unknown): boolean {
function formatDebuggerConflictMessage (line 131) | function formatDebuggerConflictMessage(tabId: number, originalMessage: s...
class ConsoleTool (line 142) | class ConsoleTool extends BaseBrowserToolExecutor {
method execute (line 145) | async execute(args: ConsoleToolParams): Promise<ToolResult> {
method navigateToUrl (line 311) | private async navigateToUrl(
method waitForTabReady (line 338) | private async waitForTabReady(tabId: number): Promise<void> {
method formatConsoleArgs (line 357) | private formatConsoleArgs(args: any[]): string {
method captureConsoleMessages (line 381) | private async captureConsoleMessages(
FILE: app/chrome-extension/entrypoints/background/tools/browser/dialog.ts
type HandleDialogParams (line 6) | interface HandleDialogParams {
class HandleDialogTool (line 14) | class HandleDialogTool extends BaseBrowserToolExecutor {
method execute (line 17) | async execute(args: HandleDialogParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/download.ts
type HandleDownloadParams (line 5) | interface HandleDownloadParams {
class HandleDownloadTool (line 14) | class HandleDownloadTool extends BaseBrowserToolExecutor {
method execute (line 17) | async execute(args: HandleDownloadParams): Promise<ToolResult> {
function waitForDownload (line 34) | async function waitForDownload(opts: {
FILE: app/chrome-extension/entrypoints/background/tools/browser/element-picker.ts
type NormalizedRequest (line 24) | interface NormalizedRequest {
type ElementPickerToolParams (line 30) | interface ElementPickerToolParams {
type PickerUiEvent (line 37) | interface PickerUiEvent {
type PickerFrameEvent (line 44) | interface PickerFrameEvent {
constant DEFAULT_TIMEOUT_MS (line 56) | const DEFAULT_TIMEOUT_MS = 3 * 60 * 1000;
constant MAX_TIMEOUT_MS (line 57) | const MAX_TIMEOUT_MS = 10 * 60 * 1000;
constant MIN_TIMEOUT_MS (line 58) | const MIN_TIMEOUT_MS = 10 * 1000;
function toTrimmedString (line 64) | function toTrimmedString(value: unknown): string {
function normalizeTimeoutMs (line 68) | function normalizeTimeoutMs(value: unknown): number {
function normalizeRequests (line 75) | function normalizeRequests(requests: ElementPickerRequest[]): Normalized...
function buildResultItems (line 100) | function buildResultItems(
function listMissingRequestIds (line 111) | function listMissingRequestIds(
class ElementPickerTool (line 126) | class ElementPickerTool extends BaseBrowserToolExecutor {
method injectPickerScripts (line 132) | private async injectPickerScripts(tabId: number): Promise<void> {
method callPickerApi (line 144) | private async callPickerApi(
method execute (line 172) | async execute(args: ElementPickerToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/file-upload.ts
type FileUploadToolParams (line 6) | interface FileUploadToolParams {
class FileUploadTool (line 21) | class FileUploadTool extends BaseBrowserToolExecutor {
method constructor (line 23) | constructor() {
method execute (line 30) | async execute(args: FileUploadToolParams): Promise<ToolResult> {
method prepareFileFromRemote (line 169) | private async prepareFileFromRemote(options: {
FILE: app/chrome-extension/entrypoints/background/tools/browser/gif-auto-capture.ts
constant CDP_SESSION_KEY (line 42) | const CDP_SESSION_KEY = 'gif-auto-capture';
constant DEFAULT_CAPTURE_DELAY_MS (line 43) | const DEFAULT_CAPTURE_DELAY_MS = 150;
constant DEFAULT_WIDTH (line 44) | const DEFAULT_WIDTH = 800;
constant DEFAULT_HEIGHT (line 45) | const DEFAULT_HEIGHT = 600;
constant DEFAULT_FRAME_DELAY_CS (line 46) | const DEFAULT_FRAME_DELAY_CS = 20;
constant DEFAULT_MAX_COLORS (line 47) | const DEFAULT_MAX_COLORS = 256;
type AutoCaptureConfig (line 53) | interface AutoCaptureConfig {
type TabCaptureState (line 63) | interface TabCaptureState {
function sleep (line 88) | function sleep(ms: number): Promise<void> {
function normalizeActionMetadata (line 92) | function normalizeActionMetadata(action: ActionMetadata, atMs: number): ...
function sendToOffscreen (line 115) | async function sendToOffscreen<T extends { success: boolean; error?: str...
function captureFrameData (line 141) | async function captureFrameData(tabId: number, state: TabCaptureState): ...
function startAutoCapture (line 207) | async function startAutoCapture(
function stopAutoCapture (line 280) | async function stopAutoCapture(tabId: number): Promise<{
function isAutoCaptureActive (line 357) | function isAutoCaptureActive(tabId: number): boolean {
function getAutoCaptureStatus (line 364) | function getAutoCaptureStatus(tabId: number): {
function captureFrameOnAction (line 393) | async function captureFrameOnAction(
function captureInitialFrame (line 501) | async function captureInitialFrame(
function clearAllAutoCapture (line 510) | async function clearAllAutoCapture(): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/gif-enhanced-renderer.ts
type ActionType (line 16) | type ActionType =
type CoordinateSpace (line 31) | type CoordinateSpace = 'viewport' | 'screenshot';
type Point (line 33) | interface Point {
type ActionMetadata (line 38) | interface ActionMetadata {
type GifEnhancedRenderingConfig (line 53) | interface GifEnhancedRenderingConfig {
type ResolvedClickIndicatorConfig (line 101) | interface ResolvedClickIndicatorConfig {
type ResolvedDragPathConfig (line 113) | interface ResolvedDragPathConfig {
type ResolvedLabelsConfig (line 124) | interface ResolvedLabelsConfig {
type ResolvedGifEnhancedRenderingConfig (line 140) | interface ResolvedGifEnhancedRenderingConfig {
type ActionEvent (line 147) | interface ActionEvent {
type CapturePlan (line 152) | interface CapturePlan {
type RenderGifEnhancedOverlaysParams (line 158) | interface RenderGifEnhancedOverlaysParams {
constant CLICK_ACTIONS (line 173) | const CLICK_ACTIONS: readonly ActionType[] = [
function clamp (line 184) | function clamp(value: number, min: number, max: number): number {
function normalizePositiveNumber (line 188) | function normalizePositiveNumber(
function normalizePositiveInt (line 198) | function normalizePositiveInt(value: unknown, fallback: number, min: num...
function normalizeDash (line 203) | function normalizeDash(value: unknown, fallback: number[]): number[] {
function easeOutCubic (line 209) | function easeOutCubic(t: number): number {
function projectPoint (line 214) | function projectPoint(
function buildRoundedRectPath (line 239) | function buildRoundedRectPath(
function truncate (line 258) | function truncate(text: string, maxLength: number): string {
function resolveActionLabel (line 268) | function resolveActionLabel(action: ActionMetadata, cfg: ResolvedLabelsC...
function resolveAnchorPoint (line 322) | function resolveAnchorPoint(action: ActionMetadata): Point | null {
function drawClickIndicator (line 333) | function drawClickIndicator(
function drawArrowHead (line 380) | function drawArrowHead(
function drawDragPath (line 412) | function drawDragPath(
function drawLabelPill (line 456) | function drawLabelPill(
type SchemaEnhancedRenderingInput (line 520) | interface SchemaEnhancedRenderingInput {
function normalizeSchemaInput (line 562) | function normalizeSchemaInput(raw: unknown): GifEnhancedRenderingConfig ...
function resolveGifEnhancedRenderingConfig (line 646) | function resolveGifEnhancedRenderingConfig(
function resolveCapturePlanForAction (line 713) | function resolveCapturePlanForAction(
function renderGifEnhancedOverlays (line 739) | function renderGifEnhancedOverlays(params: RenderGifEnhancedOverlaysPara...
function pruneActionEventsInPlace (line 799) | function pruneActionEventsInPlace(
FILE: app/chrome-extension/entrypoints/background/tools/browser/gif-recorder.ts
constant DEFAULT_FPS (line 43) | const DEFAULT_FPS = 5;
constant DEFAULT_DURATION_MS (line 44) | const DEFAULT_DURATION_MS = 5000;
constant DEFAULT_MAX_FRAMES (line 45) | const DEFAULT_MAX_FRAMES = 50;
constant DEFAULT_WIDTH (line 46) | const DEFAULT_WIDTH = 800;
constant DEFAULT_HEIGHT (line 47) | const DEFAULT_HEIGHT = 600;
constant DEFAULT_MAX_COLORS (line 48) | const DEFAULT_MAX_COLORS = 256;
constant CDP_SESSION_KEY (line 49) | const CDP_SESSION_KEY = 'gif-recorder';
type GifRecorderAction (line 55) | type GifRecorderAction =
type GifRecorderParams (line 64) | interface GifRecorderParams {
type RecordingState (line 87) | interface RecordingState {
type GifResult (line 108) | interface GifResult {
type AutoCaptureMetadata (line 143) | interface AutoCaptureMetadata {
type ExportableGif (line 150) | interface ExportableGif {
constant EXPORT_CACHE_LIFETIME_MS (line 165) | const EXPORT_CACHE_LIFETIME_MS = 5 * 60 * 1000;
type OffscreenResponseBase (line 171) | type OffscreenResponseBase = { success: boolean; error?: string };
function sendToOffscreen (line 173) | async function sendToOffscreen<TResponse extends OffscreenResponseBase>(
function captureFrame (line 213) | async function captureFrame(
function captureAndEncodeFrame (line 253) | async function captureAndEncodeFrame(state: RecordingState): Promise<voi...
function captureTick (line 269) | async function captureTick(state: RecordingState): Promise<void> {
function startRecording (line 315) | async function startRecording(
function stopRecording (line 412) | async function stopRecording(): Promise<GifResult> {
function getRecordingStatus (line 572) | function getRecordingStatus(): GifResult {
function blobToDataUrl (line 595) | function blobToDataUrl(blob: Blob): Promise<string> {
function normalizePositiveInt (line 604) | function normalizePositiveInt(value: unknown, fallback: number, max?: nu...
class GifRecorderTool (line 616) | class GifRecorderTool extends BaseBrowserToolExecutor {
method execute (line 619) | async execute(args: GifRecorderParams): Promise<ToolResult> {
method isRestrictedUrl (line 1204) | private isRestrictedUrl(url?: string): boolean {
method resolveTargetTab (line 1214) | private async resolveTargetTab(tabId?: number): Promise<chrome.tabs.Ta...
method buildResponse (line 1225) | private buildResponse(result: GifResult): ToolResult {
FILE: app/chrome-extension/entrypoints/background/tools/browser/history.ts
type HistoryToolParams (line 16) | interface HistoryToolParams {
type HistoryItem (line 24) | interface HistoryItem {
type HistoryResult (line 33) | interface HistoryResult {
class HistoryTool (line 45) | class HistoryTool extends BaseBrowserToolExecutor {
method parseDateString (line 57) | private parseDateString(dateStr: string | undefined | null): number | ...
method formatDate (line 108) | private formatDate(timestamp: number): string {
method execute (line 113) | async execute(args: HistoryToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/inject-script.ts
type InjectScriptParam (line 6) | interface InjectScriptParam {
type ScriptConfig (line 12) | interface ScriptConfig {
type SendCommandToInjectScriptToolParam (line 17) | interface SendCommandToInjectScriptToolParam {
class InjectScriptTool (line 24) | class InjectScriptTool extends BaseBrowserToolExecutor {
method execute (line 26) | async execute(args: InjectScriptParam & ScriptConfig): Promise<ToolRes...
class SendCommandToInjectScriptTool (line 109) | class SendCommandToInjectScriptTool extends BaseBrowserToolExecutor {
method execute (line 111) | async execute(args: SendCommandToInjectScriptToolParam): Promise<ToolR...
function isTabExists (line 168) | async function isTabExists(tabId: number) {
function handleInject (line 183) | async function handleInject(tabId: number, scriptConfig: ScriptConfig) {
function handleCleanup (line 222) | async function handleCleanup(tabId: number) {
FILE: app/chrome-extension/entrypoints/background/tools/browser/interaction.ts
type Coordinates (line 7) | interface Coordinates {
type ClickToolParams (line 12) | interface ClickToolParams {
class ClickTool (line 32) | class ClickTool extends BaseBrowserToolExecutor {
method execute (line 38) | async execute(args: ClickToolParams): Promise<ToolResult> {
type FillToolParams (line 158) | interface FillToolParams {
class FillTool (line 172) | class FillTool extends BaseBrowserToolExecutor {
method execute (line 178) | async execute(args: FillToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/javascript.ts
constant DEFAULT_TIMEOUT_MS (line 30) | const DEFAULT_TIMEOUT_MS = 15_000;
constant CDP_SESSION_KEY (line 31) | const CDP_SESSION_KEY = 'javascript';
type ExecutionEngine (line 37) | type ExecutionEngine = 'cdp' | 'scripting';
type ErrorKind (line 39) | type ErrorKind =
type JavaScriptToolParams (line 47) | interface JavaScriptToolParams {
type ExecutionError (line 54) | interface ExecutionError {
type ExecutionMetrics (line 64) | interface ExecutionMetrics {
type JavaScriptToolResult (line 68) | interface JavaScriptToolResult {
type ExecutionOptions (line 80) | interface ExecutionOptions {
type ExecutionSuccess (line 86) | type ExecutionSuccess = {
type ExecutionFailure (line 94) | type ExecutionFailure = {
type ExecutionResult (line 100) | type ExecutionResult = ExecutionSuccess | ExecutionFailure;
class TimeoutError (line 106) | class TimeoutError extends Error {
method constructor (line 107) | constructor(timeoutMs: number) {
function normalizePositiveInt (line 117) | function normalizePositiveInt(value: unknown, fallback: number): number {
function withTimeout (line 124) | function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise...
function isTimeoutError (line 137) | function isTimeoutError(error: unknown): error is TimeoutError {
function isDebuggerConflictError (line 141) | function isDebuggerConflictError(error: unknown): boolean {
function wrapUserCode (line 151) | function wrapUserCode(code: string): string {
type CDPRemoteObject (line 159) | interface CDPRemoteObject {
type CDPExceptionDetails (line 167) | interface CDPExceptionDetails {
type CDPEvaluateResult (line 179) | interface CDPEvaluateResult {
function extractReturnValue (line 184) | function extractReturnValue(remoteObject?: CDPRemoteObject): unknown {
function parseExceptionDetails (line 194) | function parseExceptionDetails(details: CDPExceptionDetails): ExecutionE...
function executeViaCdp (line 221) | async function executeViaCdp(
type ScriptingExecutionResult (line 294) | interface ScriptingExecutionResult {
function executeViaScripting (line 304) | async function executeViaScripting(
class JavaScriptTool (line 403) | class JavaScriptTool extends BaseBrowserToolExecutor {
method execute (line 406) | async execute(args: JavaScriptToolParams): Promise<ToolResult> {
method resolveTargetTab (line 469) | private async resolveTargetTab(tabId?: number): Promise<chrome.tabs.Ta...
method buildSuccessResponse (line 480) | private buildSuccessResponse(
method buildErrorResponse (line 503) | private buildErrorResponse(
FILE: app/chrome-extension/entrypoints/background/tools/browser/keyboard.ts
type KeyboardToolParams (line 7) | interface KeyboardToolParams {
class KeyboardTool (line 20) | class KeyboardTool extends BaseBrowserToolExecutor {
method execute (line 26) | async execute(args: KeyboardToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/network-capture-debugger.ts
type NetworkDebuggerStartToolParams (line 7) | interface NetworkDebuggerStartToolParams {
type NetworkRequestInfo (line 15) | interface NetworkRequestInfo {
constant DEBUGGER_PROTOCOL_VERSION (line 39) | const DEBUGGER_PROTOCOL_VERSION = '1.3';
constant MAX_RESPONSE_BODY_SIZE_BYTES (line 40) | const MAX_RESPONSE_BODY_SIZE_BYTES = 1 * 1024 * 1024;
constant DEFAULT_MAX_CAPTURE_TIME_MS (line 41) | const DEFAULT_MAX_CAPTURE_TIME_MS = 3 * 60 * 1000;
constant DEFAULT_INACTIVITY_TIMEOUT_MS (line 42) | const DEFAULT_INACTIVITY_TIMEOUT_MS = 60 * 1000;
class NetworkDebuggerStartTool (line 47) | class NetworkDebuggerStartTool extends BaseBrowserToolExecutor {
method constructor (line 58) | constructor() {
method handleTabRemoved (line 71) | private handleTabRemoved(tabId: number) {
method handleTabCreated (line 82) | private async handleTabCreated(tab: chrome.tabs.Tab) {
method startCaptureForTab (line 127) | private async startCaptureForTab(
method handleDebuggerEvent (line 211) | private handleDebuggerEvent(source: chrome.debugger.Debuggee, method: ...
method handleDebuggerDetach (line 238) | private handleDebuggerDetach(source: chrome.debugger.Debuggee, reason:...
method updateLastActivityTime (line 248) | private updateLastActivityTime(tabId: number) {
method checkInactivity (line 263) | private checkInactivity(tabId: number) {
method stopCaptureByInactivity (line 286) | private async stopCaptureByInactivity(tabId: number) {
method shouldFilterRequestByUrl (line 300) | private shouldFilterRequestByUrl(url: string): boolean {
method shouldFilterRequestByExtension (line 306) | private shouldFilterRequestByExtension(url: string, includeStatic: boo...
method shouldFilterByMimeType (line 318) | private shouldFilterByMimeType(mimeType: string, includeStatic: boolea...
method handleRequestWillBeSent (line 336) | private handleRequestWillBeSent(tabId: number, params: any) {
method handleResponseReceived (line 390) | private handleResponseReceived(tabId: number, params: any) {
method handleLoadingFinished (line 428) | private async handleLoadingFinished(tabId: number, params: any) {
method shouldCaptureResponseBody (line 472) | private shouldCaptureResponseBody(requestInfo: NetworkRequestInfo): bo...
method handleLoadingFailed (line 504) | private handleLoadingFailed(tabId: number, params: any) {
method getResponseBody (line 524) | private async getResponseBody(
method cleanupCapture (line 549) | private cleanupCapture(tabId: number) {
method stopCapture (line 579) | async stopCapture(tabId: number, isAutoStop: boolean = false): Promise...
method analyzeCommonHeaders (line 684) | private analyzeCommonHeaders(
method filterOutCommonHeaders (line 740) | private filterOutCommonHeaders(
method execute (line 767) | async execute(args: NetworkDebuggerStartToolParams): Promise<ToolResul...
class NetworkDebuggerStopTool (line 862) | class NetworkDebuggerStopTool extends BaseBrowserToolExecutor {
method constructor (line 866) | constructor() {
method execute (line 874) | async execute(): Promise<ToolResult> {
method performStop (line 943) | private async performStop(
FILE: app/chrome-extension/entrypoints/background/tools/browser/network-capture-web-request.ts
constant STATIC_RESOURCE_EXTENSIONS (line 7) | const STATIC_RESOURCE_EXTENSIONS = [
constant AD_ANALYTICS_DOMAINS (line 46) | const AD_ANALYTICS_DOMAINS = NETWORK_FILTERS.EXCLUDED_DOMAINS;
type NetworkCaptureStartToolParams (line 48) | interface NetworkCaptureStartToolParams {
type NetworkRequestInfo (line 55) | interface NetworkRequestInfo {
type CaptureInfo (line 76) | interface CaptureInfo {
class NetworkCaptureStartTool (line 92) | class NetworkCaptureStartTool extends BaseBrowserToolExecutor {
method constructor (line 136) | constructor() {
method handleTabRemoved (line 152) | private handleTabRemoved(tabId: number) {
method handleTabCreated (line 163) | private async handleTabCreated(tab: chrome.tabs.Tab) {
method shouldFilterRequest (line 207) | private shouldFilterRequest(url: string, includeStatic: boolean): bool...
method shouldFilterByMimeType (line 235) | private shouldFilterByMimeType(mimeType: string, includeStatic: boolea...
method updateLastActivityTime (line 268) | private updateLastActivityTime(tabId: number): void {
method checkInactivity (line 290) | private checkInactivity(tabId: number): void {
method stopCaptureByInactivity (line 316) | private async stopCaptureByInactivity(tabId: number): Promise<void> {
method cleanupCapture (line 327) | private cleanupCapture(tabId: number): void {
method setupListeners (line 350) | private setupListeners(): void {
method removeListeners (line 510) | private removeListeners(): void {
method processRequestBody (line 548) | private processRequestBody(requestBody: chrome.webRequest.WebRequestBo...
method startCaptureForTab (line 562) | private async startCaptureForTab(
method stopCapture (line 638) | public async stopCapture(
method analyzeCommonHeaders (line 733) | private analyzeCommonHeaders(
method filterOutCommonHeaders (line 772) | private filterOutCommonHeaders(
method execute (line 789) | async execute(args: NetworkCaptureStartToolParams): Promise<ToolResult> {
class NetworkCaptureStopTool (line 875) | class NetworkCaptureStopTool extends BaseBrowserToolExecutor {
method constructor (line 879) | constructor() {
method execute (line 887) | async execute(): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/network-capture.ts
type NetworkCaptureBackend (line 7) | type NetworkCaptureBackend = 'webRequest' | 'debugger';
type NetworkCaptureToolParams (line 9) | interface NetworkCaptureToolParams {
function getFirstText (line 21) | function getFirstText(result: ToolResult): string | undefined {
function decorateJsonResult (line 29) | function decorateJsonResult(result: ToolResult, extra: Record<string, un...
function isDebuggerCaptureActive (line 50) | function isDebuggerCaptureActive(): boolean {
function isWebRequestCaptureActive (line 60) | function isWebRequestCaptureActive(): boolean {
class NetworkCaptureTool (line 72) | class NetworkCaptureTool extends BaseBrowserToolExecutor {
method execute (line 75) | async execute(args: NetworkCaptureToolParams): Promise<ToolResult> {
method handleStart (line 92) | private async handleStart(
method handleStop (line 119) | private async handleStop(
FILE: app/chrome-extension/entrypoints/background/tools/browser/network-request.ts
constant DEFAULT_NETWORK_REQUEST_TIMEOUT (line 6) | const DEFAULT_NETWORK_REQUEST_TIMEOUT = 30000;
type NetworkRequestToolParams (line 8) | interface NetworkRequestToolParams {
class NetworkRequestTool (line 23) | class NetworkRequestTool extends BaseBrowserToolExecutor {
method execute (line 26) | async execute(args: NetworkRequestToolParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/performance.ts
type OwnerTag (line 6) | type OwnerTag = 'performance';
type StartTraceParams (line 8) | interface StartTraceParams {
type StopTraceParams (line 14) | interface StopTraceParams {
type AnalyzeInsightParams (line 19) | interface AnalyzeInsightParams {
type DebuggeeEvent (line 23) | type DebuggeeEvent = (source: chrome.debugger.Debuggee, method: string, ...
type TraceSessionState (line 25) | interface TraceSessionState {
constant LAST_RESULTS (line 36) | const LAST_RESULTS = new Map<
function tracingCategories (line 48) | function tracingCategories(): string[] {
function enablePerformanceMetrics (line 70) | async function enablePerformanceMetrics(tabId: number): Promise<Record<s...
function saveTraceToDownloads (line 85) | async function saveTraceToDownloads(
function saveTraceToNativeTemp (line 107) | async function saveTraceToNativeTemp(
function cleanupNativeTempFile (line 164) | async function cleanupNativeTempFile(filePath: string): Promise<void> {
function getOrCreateStopPromise (line 209) | function getOrCreateStopPromise(session: TraceSessionState): Promise<{ c...
class PerformanceStartTraceTool (line 220) | class PerformanceStartTraceTool extends BaseBrowserToolExecutor {
method execute (line 223) | async execute(args: StartTraceParams): Promise<ToolResult> {
class PerformanceStopTraceTool (line 316) | class PerformanceStopTraceTool extends BaseBrowserToolExecutor {
method execute (line 319) | async execute(args: StopTraceParams): Promise<ToolResult> {
class PerformanceAnalyzeInsightTool (line 417) | class PerformanceAnalyzeInsightTool extends BaseBrowserToolExecutor {
method execute (line 420) | async execute(args: AnalyzeInsightParams & { timeoutMs?: number }): Pr...
FILE: app/chrome-extension/entrypoints/background/tools/browser/read-page.ts
type ReadPageStats (line 8) | interface ReadPageStats {
type ReadPageParams (line 14) | interface ReadPageParams {
class ReadPageTool (line 22) | class ReadPageTool extends BaseBrowserToolExecutor {
method execute (line 26) | async execute(args: ReadPageParams): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/screenshot.ts
constant SCREENSHOT_CONSTANTS (line 15) | const SCREENSHOT_CONSTANTS = {
type ScreenshotToolParams (line 46) | interface ScreenshotToolParams {
type ScreenshotPageDetails (line 61) | interface ScreenshotPageDetails {
constant PAGE_DETAILS_REQUIRED_FIELDS (line 71) | const PAGE_DETAILS_REQUIRED_FIELDS: Array<keyof ScreenshotPageDetails> = [
function assertValidPageDetails (line 84) | function assertValidPageDetails(details: unknown): ScreenshotPageDetails {
class ScreenshotTool (line 108) | class ScreenshotTool extends BaseBrowserToolExecutor {
method execute (line 114) | async execute(args: ScreenshotToolParams): Promise<ToolResult> {
method logInfo (line 391) | private logInfo(message: string) {
method _captureElement (line 398) | async _captureElement(
method _captureFullPage (line 440) | async _captureFullPage(
FILE: app/chrome-extension/entrypoints/background/tools/browser/userscript.ts
type UserscriptAction (line 7) | type UserscriptAction =
type UserscriptArgsBase (line 18) | interface UserscriptArgsBase {
type CreateArgs (line 23) | interface CreateArgs {
type UpdateArgs (line 38) | type UpdateArgs = Partial<Omit<CreateArgs, 'script'>> & { id: string; sc...
type UserscriptRecord (line 40) | interface UserscriptRecord {
type ActiveInjection (line 66) | type ActiveInjection = { kind: 'css' | 'js'; world?: 'ISOLATED' | 'MAIN' };
function loadAllRecords (line 69) | async function loadAllRecords(): Promise<Record<string, UserscriptRecord...
function saveAllRecords (line 74) | async function saveAllRecords(records: Record<string, UserscriptRecord>)...
function fnv1a (line 79) | function fnv1a(str: string): string {
function now (line 89) | function now(): number {
function computeSHA256 (line 93) | async function computeSHA256(input: string): Promise<string> {
function probeUnsafeEvalInMain (line 100) | async function probeUnsafeEvalInMain(tabId: number): Promise<boolean> {
function parseUserscriptMeta (line 121) | function parseUserscriptMeta(source: string): {
function pick (line 144) | function pick<T>(arr: T[] | undefined): T | undefined {
function deriveName (line 148) | function deriveName(meta: Record<string, string[]>, fallback?: string): ...
function toBoolean (line 152) | function toBoolean(val: any, d: boolean): boolean {
function isLikelyCSS (line 157) | function isLikelyCSS(source: string): boolean {
function normalizeMatches (line 174) | function normalizeMatches(matches?: string[], currentUrl?: string): stri...
function matchUrl (line 188) | function matchUrl(patterns: string[], url?: string): boolean {
function getActiveTab (line 223) | async function getActiveTab(): Promise<chrome.tabs.Tab | null> {
function insertCssToTab (line 228) | async function insertCssToTab(tabId: number, css: string, allFrames: boo...
function removeCssFromTab (line 232) | async function removeCssFromTab(tabId: number, css: string, allFrames: b...
function injectJsPersistent (line 240) | async function injectJsPersistent(
function setActiveInjection (line 324) | function setActiveInjection(tabId: number, id: string, inj: ActiveInject...
function clearActiveInjection (line 333) | function clearActiveInjection(tabId: number, id: string) {
function reinjectForTab (line 338) | async function reinjectForTab(tabId: number, url?: string) {
class UserscriptTool (line 422) | class UserscriptTool extends BaseBrowserToolExecutor {
method execute (line 425) | async execute(params: UserscriptArgsBase): Promise<ToolResult> {
method create (line 460) | private async create(args: CreateArgs): Promise<ToolResult> {
method list (line 598) | private async list(args: any): Promise<ToolResult> {
method get (line 632) | private async get(args: any): Promise<ToolResult> {
method enable (line 644) | private async enable(args: any, enabled: boolean): Promise<ToolResult> {
method update (line 656) | private async update(args: UpdateArgs): Promise<ToolResult> {
method remove (line 679) | private async remove(args: any): Promise<ToolResult> {
method sendCommand (line 707) | private async sendCommand(args: any): Promise<ToolResult> {
method exportAll (line 749) | private async exportAll(): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/vector-search.ts
type VectorSearchResult (line 13) | interface VectorSearchResult {
class VectorSearchTabsContentTool (line 26) | class VectorSearchTabsContentTool extends BaseBrowserToolExecutor {
method constructor (line 31) | constructor() {
method initializeIndexer (line 40) | private async initializeIndexer(): Promise<void> {
method execute (line 51) | async execute(args: { query: string }): Promise<ToolResult> {
method ensureTabsIndexed (line 145) | private async ensureTabsIndexed(tabs: chrome.tabs.Tab[]): Promise<void> {
method convertSearchResults (line 162) | private convertSearchResults(searchResults: SearchResult[]): VectorSea...
method deduplicateByTab (line 177) | private deduplicateByTab(results: VectorSearchResult[]): VectorSearchR...
method extractSnippet (line 195) | private extractSnippet(text: string, maxLength: number = 200): string {
method getIndexStats (line 227) | public async getIndexStats() {
method rebuildIndex (line 246) | public async rebuildIndex(): Promise<void> {
method indexTab (line 287) | public async indexTab(tabId: number): Promise<void> {
method removeTabIndex (line 298) | public async removeTabIndex(tabId: number): Promise<void> {
FILE: app/chrome-extension/entrypoints/background/tools/browser/web-fetcher.ts
type WebFetcherToolParams (line 6) | interface WebFetcherToolParams {
class WebFetcherTool (line 16) | class WebFetcherTool extends BaseBrowserToolExecutor {
method execute (line 22) | async execute(args: WebFetcherToolParams): Promise<ToolResult> {
type GetInteractiveElementsToolParams (line 170) | interface GetInteractiveElementsToolParams {
class GetInteractiveElementsTool (line 177) | class GetInteractiveElementsTool extends BaseBrowserToolExecutor {
method execute (line 183) | async execute(args: GetInteractiveElementsToolParams): Promise<ToolRes...
FILE: app/chrome-extension/entrypoints/background/tools/browser/window.ts
class WindowTool (line 5) | class WindowTool extends BaseBrowserToolExecutor {
method execute (line 7) | async execute(): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/tools/index.ts
type ToolCallParam (line 12) | interface ToolCallParam {
FILE: app/chrome-extension/entrypoints/background/tools/record-replay.ts
class FlowRunTool (line 7) | class FlowRunTool {
method execute (line 9) | async execute(args: any): Promise<ToolResult> {
class ListPublishedTool (line 44) | class ListPublishedTool {
method execute (line 46) | async execute(): Promise<ToolResult> {
FILE: app/chrome-extension/entrypoints/background/utils/sidepanel.ts
function openAgentChatSidepanel (line 19) | async function openAgentChatSidepanel(
FILE: app/chrome-extension/entrypoints/background/web-editor/index.ts
constant CONTEXT_MENU_ID (line 15) | const CONTEXT_MENU_ID = 'web_editor_toggle';
constant COMMAND_KEY (line 16) | const COMMAND_KEY = 'toggle_web_editor';
constant DEFAULT_NATIVE_SERVER_PORT (line 17) | const DEFAULT_NATIVE_SERVER_PORT = 12306;
constant WEB_EDITOR_TX_CHANGED_SESSION_KEY_PREFIX (line 20) | const WEB_EDITOR_TX_CHANGED_SESSION_KEY_PREFIX = 'web-editor-v2-tx-chang...
constant WEB_EDITOR_SELECTION_SESSION_KEY_PREFIX (line 21) | const WEB_EDITOR_SELECTION_SESSION_KEY_PREFIX = 'web-editor-v2-selection-';
constant WEB_EDITOR_EXCLUDED_KEYS_SESSION_KEY_PREFIX (line 24) | const WEB_EDITOR_EXCLUDED_KEYS_SESSION_KEY_PREFIX = 'web-editor-v2-exclu...
constant STORAGE_KEY_SELECTED_SESSION (line 27) | const STORAGE_KEY_SELECTED_SESSION = 'agent-selected-session-id';
type ExecutionStatusEntry (line 30) | interface ExecutionStatusEntry {
constant STATUS_CACHE_TTL (line 37) | const STATUS_CACHE_TTL = 5 * 60 * 1000;
function cleanupExpiredStatuses (line 39) | function cleanupExpiredStatuses(): void {
function setExecutionStatus (line 48) | function setExecutionStatus(
function getExecutionStatus (line 66) | function getExecutionStatus(requestId: string): ExecutionStatusEntry | u...
function subscribeToSessionStatus (line 76) | async function subscribeToSessionStatus(
function handleSseEvent (line 153) | function handleSseEvent(requestId: string, event: unknown): void {
constant USE_WEB_EDITOR_V2 (line 209) | const USE_WEB_EDITOR_V2 = true;
constant V1_SCRIPT_PATH (line 212) | const V1_SCRIPT_PATH = 'inject-scripts/web-editor.js';
constant V2_SCRIPT_PATH (line 215) | const V2_SCRIPT_PATH = 'web-editor-v2.js';
constant PROPS_AGENT_SCRIPT_PATH (line 218) | const PROPS_AGENT_SCRIPT_PATH = 'inject-scripts/props-agent.js';
type WebEditorInstructionType (line 220) | type WebEditorInstructionType = 'update_text' | 'update_style';
type WebEditorFingerprint (line 222) | interface WebEditorFingerprint {
type DebugSource (line 230) | interface DebugSource {
type StyleOperation (line 238) | interface StyleOperation {
type WebEditorApplyPayload (line 245) | interface WebEditorApplyPayload {
function normalizeString (line 263) | function normalizeString(value: unknown): string {
function normalizeStringArray (line 267) | function normalizeStringArray(value: unknown): string[] {
function normalizeStyleMap (line 272) | function normalizeStyleMap(value: unknown): Record<string, string> | und...
function normalizeStyleMapAllowEmpty (line 284) | function normalizeStyleMapAllowEmpty(value: unknown): Record<string, str...
function normalizeDebugSource (line 296) | function normalizeDebugSource(value: unknown): DebugSource | undefined {
function normalizeOperation (line 313) | function normalizeOperation(value: unknown): StyleOperation | undefined {
function normalizeApplyPayload (line 332) | function normalizeApplyPayload(raw: unknown): WebEditorApplyPayload {
function normalizeApplyBatchPayload (line 391) | function normalizeApplyBatchPayload(raw: unknown): WebEditorApplyBatchPa...
function buildAgentPromptBatch (line 412) | function buildAgentPromptBatch(elements: readonly ElementChangeSummary[]...
function buildAgentPrompt (line 533) | function buildAgentPrompt(payload: WebEditorApplyPayload): string {
function ensureContextMenu (line 672) | async function ensureContextMenu(): Promise<void> {
function getActions (line 691) | function getActions() {
function ensureEditorInjected (line 703) | async function ensureEditorInjected(tabId: number): Promise<void> {
function ensurePropsAgentInjected (line 741) | async function ensurePropsAgentInjected(tabId: number): Promise<void> {
function sendPropsAgentCleanup (line 759) | async function sendPropsAgentCleanup(tabId: number): Promise<void> {
constant PROPS_AGENT_EARLY_INJECTION_ID_PREFIX (line 790) | const PROPS_AGENT_EARLY_INJECTION_ID_PREFIX = 'mcp_we_props_early';
type EarlyInjectionResult (line 795) | interface EarlyInjectionResult {
function sanitizeContentScriptId (line 806) | function sanitizeContentScriptId(input: string): string {
function buildEarlyInjectionPatterns (line 818) | function buildEarlyInjectionPatterns(tabUrl: string): { host: string; ma...
function registerPropsAgentEarlyInjection (line 845) | async function registerPropsAgentEarlyInjection(tabUrl: string): Promise...
function toggleEditorInTab (line 877) | async function toggleEditorInTab(tabId: number): Promise<{ active?: bool...
function getActiveTabId (line 904) | async function getActiveTabId(): Promise<number | null> {
function initWebEditorListeners (line 914) | function initWebEditorListeners(): void {
FILE: app/chrome-extension/entrypoints/content.ts
method main (line 3) | main() {}
FILE: app/chrome-extension/entrypoints/element-picker.content.ts
type UiShowMessage (line 25) | interface UiShowMessage {
type UiUpdateMessage (line 33) | interface UiUpdateMessage {
type UiHideMessage (line 42) | interface UiHideMessage {
type UiPingMessage (line 47) | interface UiPingMessage {
type PickerMessage (line 51) | type PickerMessage = UiPingMessage | UiShowMessage | UiUpdateMessage | U...
method main (line 61) | main() {
FILE: app/chrome-extension/entrypoints/offscreen/gif-encoder.ts
type GifEncoderState (line 15) | interface GifEncoderState {
type GifAddFrameMessage (line 23) | interface GifAddFrameMessage {
type GifFinishMessage (line 33) | interface GifFinishMessage {
type GifResetMessage (line 38) | interface GifResetMessage {
type GifMessage (line 43) | type GifMessage = GifAddFrameMessage | GifFinishMessage | GifResetMessage;
type GifMessageResponse (line 45) | interface GifMessageResponse {
function initializeEncoder (line 69) | function initializeEncoder(width: number, height: number): void {
function addFrame (line 77) | function addFrame(
function finishEncoding (line 109) | function finishEncoding(): Uint8Array {
function resetEncoder (line 123) | function resetEncoder(): void {
function isGifMessage (line 138) | function isGifMessage(message: unknown): message is GifMessage {
function handleGifMessage (line 152) | function handleGifMessage(
FILE: app/chrome-extension/entrypoints/offscreen/main.ts
type OffscreenMessage (line 16) | interface OffscreenMessage {
type SimilarityEngineInitMessage (line 21) | interface SimilarityEngineInitMessage extends OffscreenMessage {
type SimilarityEngineComputeBatchMessage (line 26) | interface SimilarityEngineComputeBatchMessage extends OffscreenMessage {
type SimilarityEngineGetEmbeddingMessage (line 32) | interface SimilarityEngineGetEmbeddingMessage extends OffscreenMessage {
type SimilarityEngineGetEmbeddingsBatchMessage (line 38) | interface SimilarityEngineGetEmbeddingsBatchMessage extends OffscreenMes...
type SimilarityEngineStatusMessage (line 44) | interface SimilarityEngineStatusMessage extends OffscreenMessage {
type MessageResponse (line 48) | type MessageResponse = {
function needsReinitialization (line 161) | function needsReinitialization(newConfig: any): boolean {
type ProgressCallback (line 183) | type ProgressCallback = (progress: { status: string; progress: number; m...
function handleSimilarityEngineInit (line 188) | async function handleSimilarityEngineInit(config: any): Promise<void> {
function clearVectorIndexedDB (line 274) | async function clearVectorIndexedDB(): Promise<void> {
function analyzeErrorType (line 308) | function analyzeErrorType(errorMessage: string): 'network' | 'file' | 'u...
function updateModelStatus (line 337) | async function updateModelStatus(
function handleComputeSimilarityBatch (line 377) | async function handleComputeSimilarityBatch(
function handleGetEmbedding (line 395) | async function handleGetEmbedding(
function handleGetEmbeddingsBatch (line 413) | async function handleGetEmbeddingsBatch(
function handleGetEngineStatus (line 431) | async function handleGetEngineStatus(): Promise<{
FILE: app/chrome-extension/entrypoints/offscreen/rr-keepalive.ts
constant KEEPALIVE_CONTROL_MESSAGE_TYPE (line 23) | const KEEPALIVE_CONTROL_MESSAGE_TYPE = 'rr_v3_keepalive.control' as const;
type KeepaliveControlCommand (line 25) | type KeepaliveControlCommand = 'start' | 'stop';
type KeepaliveControlMessage (line 27) | interface KeepaliveControlMessage {
function isKeepaliveControlMessage (line 32) | function isKeepaliveControlMessage(value: unknown): value is KeepaliveCo...
function isKeepaliveMessage (line 53) | function isKeepaliveMessage(value: unknown): value is KeepaliveMessage {
function scheduleReconnect (line 76) | function scheduleReconnect(delayMs = 1000): void {
function connectToBackground (line 95) | function connectToBackground(): chrome.runtime.Port | null {
function sendPing (line 139) | function sendPing(): void {
function startPingLoop (line 164) | function startPingLoop(): void {
function stopPingLoop (line 191) | function stopPingLoop(): void {
function initKeepalive (line 223) | function initKeepalive(): void {
function isKeepaliveActive (line 264) | function isKeepaliveActive(): boolean {
function getActivePortCount (line 272) | function getActivePortCount(): number {
FILE: app/chrome-extension/entrypoints/popup/components/builder/components/nodes/node-util.ts
function iconComp (line 30) | function iconComp(t?: string) {
function getTypeLabel (line 88) | function getTypeLabel(type?: string) {
function nodeSubtitle (line 114) | function nodeSubtitle(node?: NodeBase | null): string {
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/form-widget-registry.ts
constant REG (line 10) | const REG = new Map<string, Component>();
function registerDefaultWidgets (line 12) | function registerDefaultWidgets() {
function getWidget (line 22) | function getWidget(name?: string): Component | null {
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/toast.ts
type ToastLevel (line 4) | type ToastLevel = 'info' | 'warn' | 'error';
function toast (line 6) | function toast(message: string, level: ToastLevel = 'warn') {
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/transforms.ts
function newId (line 14) | function newId(prefix: string) {
type NodeType (line 18) | type NodeType = NodeBase['type'];
function defaultConfigFor (line 20) | function defaultConfigFor(t: NodeType): any {
function stepsToNodes (line 54) | function stepsToNodes(steps: any[]): NodeBase[] {
function topoOrder (line 63) | function topoOrder(nodes: NodeBase[], edges: EdgeV2[]): NodeBase[] {
function nodesToSteps (line 68) | function nodesToSteps(nodes: NodeBase[], edges: EdgeV2[]): any[] {
function autoChainEdges (line 78) | function autoChainEdges(nodes: NodeBase[]): EdgeV2[] {
function summarizeNode (line 90) | function summarizeNode(n?: NodeBase | null): string {
function cloneFlow (line 145) | function cloneFlow(flow: FlowV2): FlowV2 {
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/ui-nodes.ts
type NodeCategory (line 52) | type NodeCategory = 'Flow' | 'Actions' | 'Logic' | 'Tools' | 'Tabs' | 'P...
type NodeUIConfig (line 54) | interface NodeUIConfig {
function specToUi (line 70) | function specToUi(spec: any): NodeUIConfig {
constant NODE_UI_LIST (line 94) | const NODE_UI_LIST: NodeUIConfig[] = listNodeSpecs().map(specToUi);
constant REGISTRY_MAP (line 96) | const REGISTRY_MAP: Record<string, NodeUIConfig> = Object.fromEntries(
constant NODE_UI_REGISTRY (line 99) | const NODE_UI_REGISTRY = REGISTRY_MAP as Record<NodeType, NodeUIConfig>;
constant NODE_CATEGORIES (line 101) | const NODE_CATEGORIES: NodeCategory[] = [
function listByCategory (line 110) | function listByCategory(): Record<NodeCategory, NodeUIConfig[]> {
function canvasTypeKey (line 123) | function canvasTypeKey(t: NodeType): string {
function defaultConfigOf (line 129) | function defaultConfigOf(t: NodeType): any {
function validateNodeWithRegistry (line 139) | function validateNodeWithRegistry(n: NodeBase): string[] {
function registerExtraUiNodes (line 155) | function registerExtraUiNodes(list: NodeUIConfig[]) {
function getIoConstraint (line 163) | function getIoConstraint(t: NodeType): { inputs: number | 'any'; outputs...
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/validation.ts
function validateNode (line 4) | function validateNode(n: NodeBase): string[] {
function validateFlow (line 113) | function validateFlow(nodes: NodeBase[]): {
FILE: app/chrome-extension/entrypoints/popup/components/builder/model/variables.ts
type VariableOrigin (line 2) | type VariableOrigin = 'global' | 'node';
type VariableOption (line 4) | interface VariableOption {
constant VAR_TOKEN_OPEN (line 11) | const VAR_TOKEN_OPEN = '{';
constant VAR_TOKEN_CLOSE (line 12) | const VAR_TOKEN_CLOSE = '}';
constant VAR_PLACEHOLDER (line 13) | const VAR_PLACEHOLDER = '{}';
FILE: app/chrome-extension/entrypoints/popup/components/builder/store/useBuilderStore.ts
function useBuilderStore (line 18) | function useBuilderStore(initial?: FlowV2 | null) {
FILE: app/chrome-extension/entrypoints/quick-panel.content.ts
method main (line 22) | main() {
FILE: app/chrome-extension/entrypoints/shared/composables/useRRV3Rpc.ts
type RpcRequestOptions (line 34) | interface RpcRequestOptions {
type UseRRV3RpcOptions (line 42) | interface UseRRV3RpcOptions {
type EventListener (line 58) | type EventListener = (event: RunEvent) => void;
type PendingRequest (line 61) | interface PendingRequest {
type UseRRV3Rpc (line 73) | interface UseRRV3Rpc {
function toErrorMessage (line 106) | function toErrorMessage(error: unknown): string {
function isRunEvent (line 110) | function isRunEvent(value: unknown): value is RunEvent {
function useRRV3Rpc (line 126) | function useRRV3Rpc(options: UseRRV3RpcOptions = {}): UseRRV3Rpc {
FILE: app/chrome-extension/entrypoints/shared/utils/rr-flow-convert.ts
type FlowConversionResult (line 20) | interface FlowConversionResult<T> {
function flowV2ToV3ForRpc (line 33) | function flowV2ToV3ForRpc(flowV2: FlowV2): FlowConversionResult<FlowV3> {
function flowV3ToV2ForBuilder (line 56) | function flowV3ToV2ForBuilder(flowV3: FlowV3): FlowConversionResult<Flow...
function isFlowV3 (line 77) | function isFlowV3(value: unknown): value is FlowV3 {
function isFlowV2 (line 96) | function isFlowV2(value: unknown): value is FlowV2 {
function extractFlowCandidates (line 119) | function extractFlowCandidates(parsed: unknown): unknown[] {
FILE: app/chrome-extension/entrypoints/sidepanel/components/agent-chat/timeline/markstream-thinking.ts
constant AGENTCHAT_MD_SCOPE (line 16) | const AGENTCHAT_MD_SCOPE = 'agentchat';
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentChat.ts
type RequestState (line 27) | type RequestState = 'idle' | AgentStatusEvent['status'];
type UseAgentChatOptions (line 29) | interface UseAgentChatOptions {
function useAgentChat (line 36) | function useAgentChat(options: UseAgentChatOptions) {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentChatViewRoute.ts
type AgentChatView (line 22) | type AgentChatView = 'sessions' | 'chat';
type AgentChatRouteState (line 25) | interface AgentChatRouteState {
type UseAgentChatViewRouteOptions (line 31) | interface UseAgentChatViewRouteOptions {
constant DEFAULT_VIEW (line 43) | const DEFAULT_VIEW: AgentChatView = 'sessions';
constant URL_PARAM_VIEW (line 44) | const URL_PARAM_VIEW = 'view';
constant URL_PARAM_SESSION_ID (line 45) | const URL_PARAM_SESSION_ID = 'sessionId';
function parseView (line 55) | function parseView(value: string | null): AgentChatView {
function updateUrlParams (line 66) | function updateUrlParams(view: AgentChatView, sessionId: string | null):...
function useAgentChatViewRoute (line 95) | function useAgentChatViewRoute(options: UseAgentChatViewRouteOptions = {...
type UseAgentChatViewRoute (line 234) | type UseAgentChatViewRoute = ReturnType<typeof useAgentChatViewRoute>;
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentInputPreferences.ts
constant STORAGE_KEY_FAKE_CARET (line 11) | const STORAGE_KEY_FAKE_CARET = 'agent-chat-fake-caret-enabled';
type UseAgentInputPreferences (line 17) | interface UseAgentInputPreferences {
function useAgentInputPreferences (line 40) | function useAgentInputPreferences(): UseAgentInputPreferences {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentProjects.ts
constant STORAGE_KEY_SELECTED_PROJECT (line 8) | const STORAGE_KEY_SELECTED_PROJECT = 'agent-selected-project-id';
type PathValidationResult (line 10) | interface PathValidationResult {
function normalizePathForComparison (line 21) | function normalizePathForComparison(path: string): string {
type UseAgentProjectsOptions (line 30) | interface UseAgentProjectsOptions {
function useAgentProjects (line 36) | function useAgentProjects(options: UseAgentProjectsOptions) {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentServer.ts
type ServerStatus (line 10) | interface ServerStatus {
type UseAgentServerOptions (line 16) | interface UseAgentServerOptions {
function useAgentServer (line 26) | function useAgentServer(options: UseAgentServerOptions = {}) {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentSessions.ts
constant STORAGE_KEY_SELECTED_SESSION (line 16) | const STORAGE_KEY_SELECTED_SESSION = 'agent-selected-session-id';
type UseAgentSessionsOptions (line 18) | interface UseAgentSessionsOptions {
function useAgentSessions (line 25) | function useAgentSessions(options: UseAgentSessionsOptions) {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentTheme.ts
type AgentThemeId (line 8) | type AgentThemeId =
constant STORAGE_KEY_THEME (line 17) | const STORAGE_KEY_THEME = 'agentTheme';
constant DEFAULT_THEME (line 20) | const DEFAULT_THEME: AgentThemeId = 'warm-editorial';
constant VALID_THEMES (line 23) | const VALID_THEMES: AgentThemeId[] = [
constant THEME_LABELS (line 33) | const THEME_LABELS: Record<AgentThemeId, string> = {
type UseAgentTheme (line 42) | interface UseAgentTheme {
function isValidTheme (line 60) | function isValidTheme(value: unknown): value is AgentThemeId {
function getThemeFromDocument (line 67) | function getThemeFromDocument(): AgentThemeId {
function useAgentTheme (line 75) | function useAgentTheme(): UseAgentTheme {
function preloadAgentTheme (line 153) | async function preloadAgentTheme(): Promise<AgentThemeId> {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAgentThreads.ts
constant AGENT_SERVER_PORT_KEY (line 17) | const AGENT_SERVER_PORT_KEY: InjectionKey<Ref<number | null>> = Symbol('...
type AgentThreadState (line 20) | type AgentThreadState =
type ToolKind (line 29) | type ToolKind = 'grep' | 'read' | 'edit' | 'run' | 'plan' | 'generic';
type ToolSeverity (line 32) | type ToolSeverity = 'info' | 'success' | 'warning' | 'error';
type DiffStats (line 35) | interface DiffStats {
type ToolPresentation (line 42) | interface ToolPresentation {
type TimelineItem (line 68) | type TimelineItem =
type WebEditorApplyMeta (line 115) | interface WebEditorApplyMeta {
type ThreadHeader (line 123) | interface ThreadHeader {
type AgentThread (line 133) | interface AgentThread {
type UseAgentThreadsOptions (line 147) | interface UseAgentThreadsOptions {
function normalize (line 157) | function normalize(s: string | undefined): string {
function firstString (line 164) | function firstString(...args: unknown[]): string | undefined {
function extractAfterPrefix (line 176) | function extractAfterPrefix(content: string, prefix: string): string | u...
function summarizeOneLine (line 185) | function summarizeOneLine(content: string): string {
function titleCase (line 193) | function titleCase(s: string): string {
function getFileName (line 200) | function getFileName(filePath: string): string {
function buildDiffStats (line 207) | function buildDiffStats(meta: Record<string, unknown>): DiffStats | unde...
function presentTool (line 221) | function presentTool(msg: AgentMessage): ToolPresentation {
function isAttachmentMetadata (line 449) | function isAttachmentMetadata(value: unknown): value is AttachmentMetada...
function getMessageAttachments (line 484) | function getMessageAttachments(msg: AgentMessage): AttachmentMetadata[] {
function mapMessageToTimelineItem (line 494) | function mapMessageToTimelineItem(msg: AgentMessage): TimelineItem | null {
function buildThreads (line 559) | function buildThreads(
function useAgentThreads (line 721) | function useAgentThreads(options: UseAgentThreadsOptions) {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useAttachments.ts
constant MAX_FILE_SIZE (line 8) | const MAX_FILE_SIZE = 10 * 1024 * 1024;
constant MAX_ATTACHMENTS (line 9) | const MAX_ATTACHMENTS = 10;
constant ALLOWED_IMAGE_TYPES (line 12) | const ALLOWED_IMAGE_TYPES = new Set([
type AttachmentWithPreview (line 23) | interface AttachmentWithPreview extends AgentAttachment {
function useAttachments (line 28) | function useAttachments() {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useFakeCaret.ts
type FakeCaretTrailPoint (line 24) | interface FakeCaretTrailPoint {
type UseFakeCaretOptions (line 30) | interface UseFakeCaretOptions {
type UseFakeCaretReturn (line 41) | interface UseFakeCaretReturn {
constant MAX_TRAIL_POINTS (line 60) | const MAX_TRAIL_POINTS = 24;
constant TRAIL_DECAY (line 61) | const TRAIL_DECAY = 0.86;
constant TRAIL_MIN_ALPHA (line 62) | const TRAIL_MIN_ALPHA = 0.06;
constant TRAIL_MIN_DISTANCE_PX (line 63) | const TRAIL_MIN_DISTANCE_PX = 0.35;
constant SMOOTHING (line 64) | const SMOOTHING = 0.35;
constant SNAP_DISTANCE_PX (line 65) | const SNAP_DISTANCE_PX = 0.2;
function isFiniteNumber (line 71) | function isFiniteNumber(v: unknown): v is number {
function clamp (line 75) | function clamp(v: number, min: number, max: number): number {
function useFakeCaret (line 83) | function useFakeCaret(options: UseFakeCaretOptions): UseFakeCaretReturn {
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useFloatingDrag.ts
constant STORAGE_KEY (line 12) | const STORAGE_KEY = 'sidepanel_navigator_position';
type UseFloatingDragOptions (line 14) | interface UseFloatingDragOptions {
type UseFloatingDragReturn (line 27) | interface UseFloatingDragReturn {
function getDefaultBottomRightPosition (line 41) | function getDefaultBottomRightPosition(
function loadPosition (line 54) | async function loadPosition(storageKey: string): Promise<FloatingPositio...
function savePosition (line 76) | async function savePosition(storageKey: string, position: FloatingPositi...
function useFloatingDrag (line 87) | function useFloatingDrag(
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useOpenProjectPreference.ts
constant STORAGE_KEY (line 9) | const STORAGE_KEY = 'agent-open-project-default';
type UseOpenProjectPreferenceOptions (line 11) | interface UseOpenProjectPreferenceOptions {
type UseOpenProjectPreference (line 19) | interface UseOpenProjectPreference {
function useOpenProjectPreference (line 34) | function useOpenProjectPreference(
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useRRV3Debugger.ts
type UseRRV3DebuggerOptions (line 27) | interface UseRRV3DebuggerOptions {
type UseRRV3Debugger (line 45) | interface UseRRV3Debugger {
function toErrorMessage (line 83) | function toErrorMessage(error: unknown): string {
function isValidBreakpoint (line 90) | function isValidBreakpoint(value: unknown): boolean {
function isValidDebuggerState (line 99) | function isValidDebuggerState(value: unknown): value is DebuggerState {
function normalizeResponse (line 114) | function normalizeResponse(raw: JsonValue): DebuggerResponse {
constant STATE_REFRESH_EVENTS (line 147) | const STATE_REFRESH_EVENTS = new Set(['run.paused', 'run.resumed', 'node...
function useRRV3Debugger (line 152) | function useRRV3Debugger(options: UseRRV3DebuggerOptions = {}): UseRRV3D...
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useTextareaAutoResize.ts
type UseTextareaAutoResizeOptions (line 7) | interface UseTextareaAutoResizeOptions {
type UseTextareaAutoResizeReturn (line 18) | interface UseTextareaAutoResizeReturn {
constant DEFAULT_MIN_HEIGHT (line 27) | const DEFAULT_MIN_HEIGHT = 50;
constant DEFAULT_MAX_HEIGHT (line 28) | const DEFAULT_MAX_HEIGHT = 200;
function useTextareaAutoResize (line 39) | function useTextareaAutoResize(
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useWebEditorTxState.ts
constant WEB_EDITOR_TX_CHANGED_SESSION_KEY_PREFIX (line 30) | const WEB_EDITOR_TX_CHANGED_SESSION_KEY_PREFIX = 'web-editor-v2-tx-chang...
constant WEB_EDITOR_EXCLUDED_KEYS_SESSION_KEY_PREFIX (line 31) | const WEB_EDITOR_EXCLUDED_KEYS_SESSION_KEY_PREFIX = 'web-editor-v2-exclu...
constant WEB_EDITOR_SELECTION_SESSION_KEY_PREFIX (line 32) | const WEB_EDITOR_SELECTION_SESSION_KEY_PREFIX = 'web-editor-v2-selection-';
constant VALID_TX_ACTIONS (line 34) | const VALID_TX_ACTIONS = new Set<WebEditorTxChangeAction>([
function isValidTabId (line 47) | function isValidTabId(value: unknown): value is number {
function buildTxSessionKey (line 51) | function buildTxSessionKey(tabId: number): string {
function buildExcludedKeysSessionKey (line 55) | function buildExcludedKeysSessionKey(tabId: number): string {
function buildSelectionSessionKey (line 59) | function buildSelectionSessionKey(tabId: number): string {
function normalizeSelectionPayload (line 67) | function normalizeSelectionPayload(raw: unknown): WebEditorSelectionChan...
function normalizeTxChangedPayload (line 104) | function normalizeTxChangedPayload(raw: unknown): WebEditorTxChangedPayl...
function normalizeExcludedKeys (line 148) | function normalizeExcludedKeys(raw: unknown): WebEditorElementKey[] {
function persistExcludedKeys (line 168) | async function persistExcludedKeys(
function getActiveTabIdDefault (line 186) | async function getActiveTabIdDefault(): Promise<number | null> {
function getCurrentWindowId (line 201) | async function getCurrentWindowId(): Promise<number | null> {
type UseWebEditorTxStateOptions (line 215) | interface UseWebEditorTxStateOptions {
function useWebEditorTxState (line 227) | function useWebEditorTxState(options: UseWebEditorTxStateOptions = {}) {
type WebEditorTxStateReturn (line 664) | type WebEditorTxStateReturn = ReturnType<typeof useWebEditorTxState>;
constant WEB_EDITOR_TX_STATE_INJECTION_KEY (line 678) | const WEB_EDITOR_TX_STATE_INJECTION_KEY: InjectionKey<WebEditorTxStateRe...
FILE: app/chrome-extension/entrypoints/sidepanel/composables/useWorkflowsV3.ts
type FlowLite (line 23) | interface FlowLite {
type RunLite (line 39) | interface RunLite {
type TriggerLite (line 56) | interface TriggerLite {
function mapFlowV3ToLite (line 69) | function mapFlowV3ToLite(flow: FlowV3): FlowLite {
function mapRunV3ToLite (line 86) | function mapRunV3ToLite(run: RunRecordV3): RunLite {
function mapTriggerV3ToLite (line 112) | function mapTriggerV3ToLite(trigger: TriggerSpec): TriggerLite {
type UseWorkflowsV3Options (line 122) | interface UseWorkflowsV3Options {
type UseWorkflowsV3Return (line 129) | interface UseWorkflowsV3Return {
function useWorkflowsV3 (line 158) | function useWorkflowsV3(options: UseWorkflowsV3Options = {}): UseWorkflo...
FILE: app/chrome-extension/entrypoints/sidepanel/main.ts
function init (line 16) | async function init(): Promise<void> {
FILE: app/chrome-extension/entrypoints/sidepanel/utils/loading-texts.ts
function getRandomLoadingText (line 58) | function getRandomLoadingText(): string {
FILE: app/chrome-extension/entrypoints/web-editor-v2/constants.ts
constant WEB_EDITOR_V2_VERSION (line 9) | const WEB_EDITOR_V2_VERSION = 2 as const;
constant WEB_EDITOR_V2_LOG_PREFIX (line 12) | const WEB_EDITOR_V2_LOG_PREFIX = '[WebEditorV2]' as const;
constant WEB_EDITOR_V2_HOST_ID (line 19) | const WEB_EDITOR_V2_HOST_ID = '__mcp_web_editor_v2_host__';
constant WEB_EDITOR_V2_OVERLAY_ID (line 22) | const WEB_EDITOR_V2_OVERLAY_ID = '__mcp_web_editor_v2_overlay__';
constant WEB_EDITOR_V2_UI_ID (line 25) | const WEB_EDITOR_V2_UI_ID = '__mcp_web_editor_v2_ui__';
constant WEB_EDITOR_V2_Z_INDEX (line 32) | const WEB_EDITOR_V2_Z_INDEX = 2147483647;
constant WEB_EDITOR_V2_PANEL_WIDTH (line 35) | const WEB_EDITOR_V2_PANEL_WIDTH = 320;
constant WEB_EDITOR_V2_COLORS (line 41) | const WEB_EDITOR_V2_COLORS = {
constant WEB_EDITOR_V2_DRAG_THRESHOLD_PX (line 67) | const WEB_EDITOR_V2_DRAG_THRESHOLD_PX = 5;
constant WEB_EDITOR_V2_DRAG_HYSTERESIS_PX (line 70) | const WEB_EDITOR_V2_DRAG_HYSTERESIS_PX = 6;
constant WEB_EDITOR_V2_DRAG_MAX_HIT_ELEMENTS (line 73) | const WEB_EDITOR_V2_DRAG_MAX_HIT_ELEMENTS = 8;
constant WEB_EDITOR_V2_INSERTION_LINE_WIDTH (line 76) | const WEB_EDITOR_V2_INSERTION_LINE_WIDTH = 3;
constant WEB_EDITOR_V2_SNAP_THRESHOLD_PX (line 83) | const WEB_EDITOR_V2_SNAP_THRESHOLD_PX = 6;
constant WEB_EDITOR_V2_SNAP_HYSTERESIS_PX (line 86) | const WEB_EDITOR_V2_SNAP_HYSTERESIS_PX = 2;
constant WEB_EDITOR_V2_SNAP_MAX_ANCHOR_ELEMENTS (line 89) | const WEB_EDITOR_V2_SNAP_MAX_ANCHOR_ELEMENTS = 30;
constant WEB_EDITOR_V2_SNAP_MAX_SIBLINGS_SCAN (line 92) | const WEB_EDITOR_V2_SNAP_MAX_SIBLINGS_SCAN = 300;
constant WEB_EDITOR_V2_GUIDE_LINE_WIDTH (line 95) | const WEB_EDITOR_V2_GUIDE_LINE_WIDTH = 1;
constant WEB_EDITOR_V2_DISTANCE_LABEL_MIN_PX (line 102) | const WEB_EDITOR_V2_DISTANCE_LABEL_MIN_PX = 1;
constant WEB_EDITOR_V2_DISTANCE_LINE_WIDTH (line 105) | const WEB_EDITOR_V2_DISTANCE_LINE_WIDTH = 1;
constant WEB_EDITOR_V2_DISTANCE_TICK_SIZE (line 108) | const WEB_EDITOR_V2_DISTANCE_TICK_SIZE = 4;
constant WEB_EDITOR_V2_DISTANCE_LABEL_FONT (line 111) | const WEB_EDITOR_V2_DISTANCE_LABEL_FONT =
constant WEB_EDITOR_V2_DISTANCE_LABEL_PADDING_X (line 115) | const WEB_EDITOR_V2_DISTANCE_LABEL_PADDING_X = 6;
constant WEB_EDITOR_V2_DISTANCE_LABEL_PADDING_Y (line 118) | const WEB_EDITOR_V2_DISTANCE_LABEL_PADDING_Y = 3;
constant WEB_EDITOR_V2_DISTANCE_LABEL_RADIUS (line 121) | const WEB_EDITOR_V2_DISTANCE_LABEL_RADIUS = 4;
constant WEB_EDITOR_V2_DISTANCE_LABEL_OFFSET (line 124) | const WEB_EDITOR_V2_DISTANCE_LABEL_OFFSET = 8;
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/css-compare.ts
type ComputedDiffItem (line 22) | interface ComputedDiffItem {
type CompareComputedResult (line 36) | interface CompareComputedResult {
type CompareComputedOptions (line 44) | interface CompareComputedOptions {
constant DEFAULT_PX_EPSILON (line 61) | const DEFAULT_PX_EPSILON = 0.5;
constant DEFAULT_MATRIX_EPSILON (line 62) | const DEFAULT_MATRIX_EPSILON = 1e-3;
constant PX_VALUE_REGEX (line 65) | const PX_VALUE_REGEX = /(-?\d*\.?\d+(?:e[+-]?\d+)?)px/gi;
constant MATRIX_NUMBER_REGEX (line 66) | const MATRIX_NUMBER_REGEX = /-?\d*\.?\d+(?:e[+-]?\d+)?/gi;
function normalizeText (line 76) | function normalizeText(text: string): string {
function readComputedMap (line 89) | function readComputedMap(
function compareComputed (line 142) | function compareComputed(
function normalizeCssValue (line 174) | function normalizeCssValue(raw: string): string {
function approximatelyEqual (line 186) | function approximatelyEqual(a: number, b: number, epsilon: number): bool...
function isMatrixValue (line 193) | function isMatrixValue(value: string): boolean {
function extractMatrixNumbers (line 202) | function extractMatrixNumbers(value: string): number[] | null {
function extractPxNumbers (line 222) | function extractPxNumbers(value: string): number[] | null {
function pxValueShape (line 242) | function pxValueShape(value: string): string {
function compareMatrixWithEpsilon (line 251) | function compareMatrixWithEpsilon(expected: string, actual: string, epsi...
function comparePxWithEpsilon (line 274) | function comparePxWithEpsilon(expected: string, actual: string, epsilon:...
function compareSingleValue (line 295) | function compareSingleValue(
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/cssom-styles-collector.ts
type Specificity (line 24) | type Specificity = readonly [inline: number, ids: number, classes: numbe...
type DeclStatus (line 26) | type DeclStatus = 'active' | 'overridden';
type CssRuleSource (line 28) | interface CssRuleSource {
type CssDeclView (line 33) | interface CssDeclView {
type CssRuleView (line 42) | interface CssRuleView {
type CssSectionView (line 53) | interface CssSectionView {
type CssPanelSnapshot (line 60) | interface CssPanelSnapshot {
type DeclCandidate (line 79) | interface DeclCandidate {
type FlatStyleRule (line 91) | interface FlatStyleRule {
type RuleIndex (line 99) | interface RuleIndex {
type CollectElementOptions (line 107) | interface CollectElementOptions {
type CollectedElementRules (line 112) | interface CollectedElementRules {
constant ZERO_SPEC (line 128) | const ZERO_SPEC: Specificity = [0, 0, 0, 0] as const;
function compareSpecificity (line 130) | function compareSpecificity(a: Specificity, b: Specificity): number {
function splitSelectorList (line 137) | function splitSelectorList(input: string): string[] {
function maxSpecificity (line 183) | function maxSpecificity(list: readonly Specificity[]): Specificity {
function computeSelectorSpecificity (line 189) | function computeSelectorSpecificity(selector: string): Specificity {
function computeMatchedRuleSpecificity (line 329) | function computeMatchedRuleSpecificity(
constant LEGACY_PSEUDO_ELEMENTS (line 353) | const LEGACY_PSEUDO_ELEMENTS = new Set([
function isIdentStart (line 363) | function isIdentStart(ch: string): boolean {
function consumeIdent (line 367) | function consumeIdent(s: string, start: number): number {
function consumeBracket (line 381) | function consumeBracket(s: string, openIndex: number): number {
function consumeParenFunction (line 412) | function consumeParenFunction(
function isCombinatorOrWhitespace (line 447) | function isCombinatorOrWhitespace(s: string, i: number): boolean {
function consumeWhitespaceAndCombinators (line 452) | function consumeWhitespaceAndCombinators(s: string, i: number): number {
function extractNthOfSelectorList (line 460) | function extractNthOfSelectorList(content: string): string | null {
function isOfTokenAt (line 500) | function isOfTokenAt(s: string, i: number): boolean {
constant INHERITED_PROPERTIES (line 513) | const INHERITED_PROPERTIES = new Set<string>([
function isInheritableProperty (line 631) | function isInheritableProperty(property: string): boolean {
constant SHORTHAND_TO_LONGHANDS (line 642) | const SHORTHAND_TO_LONGHANDS: Record<string, readonly string[]> = {
function expandToLonghands (line 776) | function expandToLonghands(property: string): readonly string[] {
function normalizePropertyName (line 784) | function normalizePropertyName(property: string): string {
function compareSourceOrder (line 795) | function compareSourceOrder(
function compareCascade (line 805) | function compareCascade(a: DeclCandidate, b: DeclCandidate): number {
function computeOverrides (line 812) | function computeOverrides(candidates: readonly DeclCandidate[]): {
constant CONTAINER_RULE (line 836) | const CONTAINER_RULE = (globalThis as unknown as { CSSRule?: { CONTAINER...
constant SCOPE_RULE (line 838) | const SCOPE_RULE = (globalThis as unknown as { CSSRule?: { SCOPE_RULE?: ...
function isSheetApplicable (line 841) | function isSheetApplicable(sheet: CSSStyleSheet): boolean {
function describeStyleSheet (line 853) | function describeStyleSheet(sheet: CSSStyleSheet, fallbackIndex: number)...
function safeReadCssRules (line 871) | function safeReadCssRules(sheet: CSSStyleSheet): CSSRuleList | null {
function evalMediaRule (line 879) | function evalMediaRule(rule: CSSMediaRule, warnings: string[]): boolean {
function evalSupportsRule (line 890) | function evalSupportsRule(rule: CSSSupportsRule, warnings: string[]): bo...
function createRuleIndexForRoot (line 902) | function createRuleIndexForRoot(root: Document | ShadowRoot, rootId: num...
function readStyleDecls (line 1080) | function readStyleDecls(style: CSSStyleDeclaration): Array<{
function canReadInlineStyle (line 1115) | function canReadInlineStyle(element: Element): element is Element & { st...
function formatElementLabel (line 1124) | function formatElementLabel(element: Element, maxClasses = 2): string {
function getElementRoot (line 1137) | function getElementRoot(element: Element): Document | ShadowRoot {
function getParentElementOrHost (line 1146) | function getParentElementOrHost(element: Element): Element | null {
function collectForElement (line 1159) | function collectForElement(
function collectMatchedRules (line 1296) | function collectMatchedRules(element: Element): {
function collectCssPanelSnapshot (line 1327) | function collectCssPanelSnapshot(
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/debug-source.ts
constant MAX_DOM_DEPTH (line 20) | const MAX_DOM_DEPTH = 15;
constant MAX_FIBER_DEPTH (line 23) | const MAX_FIBER_DEPTH = 40;
function asRecord (line 32) | function asRecord(value: unknown): Record<string, unknown> | null {
function readString (line 42) | function readString(value: unknown): string | undefined {
function readNumber (line 53) | function readNumber(value: unknown): number | undefined {
function readComponentName (line 64) | function readComponentName(value: unknown): string | undefined {
function extractReactDebugSource (line 87) | function extractReactDebugSource(fiber: unknown): DebugSource | null {
function findReactDebugSource (line 130) | function findReactDebugSource(element: Element): DebugSource | null {
function parseVInspector (line 163) | function parseVInspector(value: unknown): DebugSource | null {
function findInspectorLocation (line 195) | function findInspectorLocation(element: Element): DebugSource | null {
function findVueDebugSource (line 218) | function findVueDebugSource(element: Element): DebugSource | null {
function findDebugSource (line 275) | function findDebugSource(element: Element): DebugSource | null {
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/design-tokens/design-tokens-service.ts
type GetRootTokensOptions (line 52) | interface GetRootTokensOptions {
type GetContextTokensOptions (line 58) | interface GetContextTokensOptions {
type DesignTokensServiceOptions (line 68) | interface DesignTokensServiceOptions {
type DesignTokensService (line 111) | interface DesignTokensService {
type RootCacheEntry (line 184) | interface RootCacheEntry {
function createDesignTokensService (line 192) | function createDesignTokensService(
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/design-tokens/token-detector.ts
type TokenDetectorOptions (line 33) | interface TokenDetectorOptions {
type TokenDetector (line 49) | interface TokenDetector {
constant DEFAULT_MAX_DECLARATIONS_PER_TOKEN (line 67) | const DEFAULT_MAX_DECLARATIONS_PER_TOKEN = 50;
constant DEFAULT_MAX_INLINE_DEPTH (line 68) | const DEFAULT_MAX_INLINE_DEPTH = 8;
function createTokenDetector (line 77) | function createTokenDetector(options: TokenDetectorOptions = {}): TokenD...
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/design-tokens/token-resolver.ts
type TokenResolverOptions (line 30) | interface TokenResolverOptions {
type ResolveForPropertyOptions (line 39) | interface ResolveForPropertyOptions {
type TokenResolver (line 47) | interface TokenResolver {
function createTokenResolver (line 99) | function createTokenResolver(options: TokenResolverOptions = {}): TokenR...
FILE: app/chrome-extension/entrypoints/web-editor-v2/core/design-tokens/types.ts
type CssVarName (line 19) | type CssVarName = `--${string}`;
type RootCacheKey (line 25) | type RootCacheKey = Document | ShadowRoot;
type RootType (line 28) | type RootType = 'document' | 'shadow';
type TokenKind (line 38) | type TokenKind =
type StyleSheetRef (line 51) | interface StyleSheetRef {
type TokenDeclarationOrigin (line 59) | type TokenDeclarationOrigin = 'rule' | 'inline';
type TokenDeclaration (line 65) | interface TokenDeclaration {
type DesignToken (line 92) | interface DesignToken {
type TokenIndexStats (line 106) | interface TokenIndexStats {
type TokenIndex (line 121) | interface TokenIndex {
type ContextToken (line 136) | interface ContextToken {
type TokenQueryResult (line 144) | interface TokenQueryResult<T> {
type CssVarReference (line 158) | interface CssVarReference {
type TokenAvailability (line 166) | type TokenAvailability = 'available' | 'unset';
type TokenResolutionMethod (line 169) | type TokenResolutionMethod =
type TokenResolution (line 175) | interface TokenResolution {
type TokenResolvedForProperty (line 185) | interface TokenResolvedForProperty {
type TokenInvalidationReason (line 203) | type TokenInvalidationReason =
type TokenInvalidationEvent (line 211) | interface TokenInvalidationEvent {
Condensed preview — 652 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,570K chars).
[
{
"path": ".gitattributes",
"chars": 43,
"preview": "*.onnx filter=lfs diff=lfs merge=lfs -text\n"
},
{
"path": ".github/workflows/build-release.yml",
"chars": 2173,
"preview": "# name: Build and Release Chrome Extension\n\n# on:\n# push:\n# branches: [ master, develop ]\n# paths:\n# - '"
},
{
"path": ".gitignore",
"chars": 531,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\n.output\n"
},
{
"path": ".husky/commit-msg",
"chars": 34,
"preview": "npx --no -- commitlint --edit \"$1\""
},
{
"path": ".husky/pre-commit",
"chars": 15,
"preview": "npx lint-staged"
},
{
"path": ".prettierignore",
"chars": 234,
"preview": "# 构建输出目录\ndist\n.output\n.wxt\n\n# 依赖\nnode_modules\n\n# 日志\nlogs\n*.log\n\n# 缓存\n.cache\n.temp\n\n# 编辑器配置\n.vscode\n!.vscode/extensions.j"
},
{
"path": ".prettierrc.json",
"chars": 171,
"preview": "{\n \"semi\": true,\n \"singleQuote\": true,\n \"tabWidth\": 2,\n \"printWidth\": 100,\n \"endOfLine\": \"auto\",\n \"proseWrap\": \"pr"
},
{
"path": ".vscode/extensions.json",
"chars": 39,
"preview": "{\n \"recommendations\": [\"Vue.volar\"]\n}\n"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2024 hangye\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "README.md",
"chars": 12974,
"preview": "# Chrome MCP Server 🚀\n\n[](https://img.shields.io/github/"
},
{
"path": "README_zh.md",
"chars": 8149,
"preview": "# Chrome MCP Server 🚀\n\n[](https://opensource.org/license"
},
{
"path": "app/chrome-extension/LICENSE",
"chars": 1064,
"preview": "MIT License\n\nCopyright (c) 2025 hangwin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "app/chrome-extension/README.md",
"chars": 232,
"preview": "# WXT + Vue 3\n\nThis template should help get you started developing with Vue 3 in WXT.\n\n## Recommended IDE Setup\n\n- [VS "
},
{
"path": "app/chrome-extension/_locales/de/messages.json",
"chars": 13560,
"preview": "{\n \"extensionName\": {\n \"message\": \"chrome-mcp-server\",\n \"description\": \"Erweiterungsname\"\n },\n \"extensionDescri"
},
{
"path": "app/chrome-extension/_locales/en/messages.json",
"chars": 16425,
"preview": "{\n \"extensionName\": {\n \"message\": \"chrome-mcp-server\",\n \"description\": \"Extension name\"\n },\n \"extensionDescript"
},
{
"path": "app/chrome-extension/_locales/ja/messages.json",
"chars": 6549,
"preview": "{\n \"extensionName\": {\n \"message\": \"Chrome MCPサーバー\"\n },\n \"extensionDescription\": {\n \"message\": \"自身のChromeブラウザの機能"
},
{
"path": "app/chrome-extension/_locales/ko/messages.json",
"chars": 10237,
"preview": "{\n \"extensionName\": {\n \"message\": \"chrome-mcp-server\",\n \"description\": \"확장 프로그램 이름\"\n },\n \"extensionDescription\""
},
{
"path": "app/chrome-extension/_locales/zh_CN/messages.json",
"chars": 12596,
"preview": "{\n \"extensionName\": {\n \"message\": \"chrome-mcp-server\",\n \"description\": \"扩展名称\"\n },\n \"extensionDescription\": {\n "
},
{
"path": "app/chrome-extension/_locales/zh_TW/messages.json",
"chars": 9465,
"preview": "{\n \"extensionName\": {\n \"message\": \"chrome-mcp-server\",\n \"description\": \"擴充功能名稱\"\n },\n \"extensionDescription\": {\n"
},
{
"path": "app/chrome-extension/common/agent-models.ts",
"chars": 7494,
"preview": "/**\n * Agent CLI Model Definitions.\n *\n * Static model definitions for each CLI type.\n * Based on the pattern from Claud"
},
{
"path": "app/chrome-extension/common/constants.ts",
"chars": 5964,
"preview": "/**\n * Chrome Extension Constants\n * Centralized configuration values and magic constants\n */\n\n// Native Host Configurat"
},
{
"path": "app/chrome-extension/common/element-marker-types.ts",
"chars": 2552,
"preview": "// Element marker types shared across background, content scripts, and popup\n\nexport type UrlMatchType = 'exact' | 'pref"
},
{
"path": "app/chrome-extension/common/message-types.ts",
"chars": 13483,
"preview": "/**\n * Consolidated message type constants for Chrome extension communication\n * Note: Native message types are imported"
},
{
"path": "app/chrome-extension/common/node-types.ts",
"chars": 440,
"preview": "// node-types.ts — centralized node type constants for Builder/UI layer\n// Combines all executable Step types with UI-on"
},
{
"path": "app/chrome-extension/common/rr-v3-keepalive-protocol.ts",
"chars": 703,
"preview": "/**\n * @fileoverview RR V3 Keepalive Protocol Constants\n * @description Shared protocol constants for Background-Offscre"
},
{
"path": "app/chrome-extension/common/step-types.ts",
"chars": 265,
"preview": "// step-types.ts — re-export shared constants to keep single source of truth\nexport { STEP_TYPES } from 'chrome-mcp-shar"
},
{
"path": "app/chrome-extension/common/tool-handler.ts",
"chars": 532,
"preview": "import type { CallToolResult, TextContent, ImageContent } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface T"
},
{
"path": "app/chrome-extension/common/web-editor-types.ts",
"chars": 16302,
"preview": "/**\n * Web Editor V2 - Shared Type Definitions\n *\n * This module defines types shared between:\n * - Background script (i"
},
{
"path": "app/chrome-extension/entrypoints/background/element-marker/element-marker-storage.ts",
"chars": 3259,
"preview": "// IndexedDB storage for element markers (URL -> marked selectors)\n// Uses the shared IndexedDbClient for robust transac"
},
{
"path": "app/chrome-extension/entrypoints/background/element-marker/index.ts",
"chars": 16196,
"preview": "import { BACKGROUND_MESSAGE_TYPES } from '@/common/message-types';\nimport type {\n UpsertMarkerRequest,\n ElementMarker,"
},
{
"path": "app/chrome-extension/entrypoints/background/index.ts",
"chars": 2946,
"preview": "import { initNativeHostListener } from './native-host';\nimport {\n initSemanticSimilarityListener,\n initializeSemanticE"
},
{
"path": "app/chrome-extension/entrypoints/background/keepalive-manager.ts",
"chars": 2416,
"preview": "/**\n * @fileoverview Keepalive Manager\n * @description Global singleton service for managing Service Worker keepalive.\n "
},
{
"path": "app/chrome-extension/entrypoints/background/native-host.ts",
"chars": 20241,
"preview": "import { NativeMessageType } from 'chrome-mcp-shared';\nimport { BACKGROUND_MESSAGE_TYPES } from '@/common/message-types'"
},
{
"path": "app/chrome-extension/entrypoints/background/quick-panel/agent-handler.ts",
"chars": 24659,
"preview": "/**\n * Quick Panel Agent Handler\n *\n * Background service that bridges Quick Panel (content script) with the native-serv"
},
{
"path": "app/chrome-extension/entrypoints/background/quick-panel/commands.ts",
"chars": 3636,
"preview": "/**\n * Quick Panel Commands Handler\n *\n * Handles keyboard shortcuts for Quick Panel functionality.\n * Listens for the '"
},
{
"path": "app/chrome-extension/entrypoints/background/quick-panel/tabs-handler.ts",
"chars": 6772,
"preview": "/**\n * Quick Panel Tabs Handler\n *\n * Background service worker bridge for Quick Panel (content script) to:\n * - Enumera"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/adapter.ts",
"chars": 14938,
"preview": "/**\n * Adapter Layer: Step ↔ Action\n *\n * Provides conversion utilities between the legacy Step system and the new Actio"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/assert.ts",
"chars": 11925,
"preview": "/**\n * Assert Action Handler\n *\n * Validates page state against specified conditions:\n * - exists: Selector can be resol"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/click.ts",
"chars": 5776,
"preview": "/**\n * Click and Double-Click Action Handlers\n *\n * Handles click interactions:\n * - Single click\n * - Double click\n * -"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/common.ts",
"chars": 10097,
"preview": "/**\n * Common utilities for Action handlers\n *\n * Shared helpers for:\n * - Variable resolution and template interpolatio"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/control-flow.ts",
"chars": 10677,
"preview": "/**\n * Control Flow Action Handlers\n *\n * Handles flow control operations:\n * - if: Conditional branching\n * - foreach: "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/delay.ts",
"chars": 1135,
"preview": "/**\n * Delay Action Handler\n *\n * Provides a simple pause in execution flow.\n * Supports variable resolution for dynamic"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/dom.ts",
"chars": 13857,
"preview": "/**\n * DOM Tools Action Handlers\n *\n * Handles DOM manipulation actions:\n * - triggerEvent: Dispatch a custom DOM Event "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/drag.ts",
"chars": 8098,
"preview": "/**\n * Drag Action Handler\n *\n * Performs a left-click drag from a start target to an end target.\n *\n * Features:\n * - L"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/extract.ts",
"chars": 8619,
"preview": "/**\n * Extract Action Handler\n *\n * Extracts data from the page and stores in variables:\n * - selector mode: Extract tex"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/fill.ts",
"chars": 6185,
"preview": "/**\n * Fill Action Handler\n *\n * Handles form input actions:\n * - Text input\n * - File upload\n * - Auto-scroll and focus"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/http.ts",
"chars": 10045,
"preview": "/**\n * HTTP Action Handler\n *\n * Makes HTTP requests from the extension context.\n * Supports:\n * - All common HTTP metho"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/index.ts",
"chars": 5548,
"preview": "/**\n * Action Handlers Registry\n *\n * Central registration point for all action handlers.\n * Provides factory function t"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/key.ts",
"chars": 6407,
"preview": "/**\n * Key Action Handler\n *\n * Handles keyboard input:\n * - Resolves key sequences via variables/templates\n * - Optiona"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/navigate.ts",
"chars": 3372,
"preview": "/**\n * Navigate Action Handler\n *\n * Handles page navigation actions:\n * - Navigate to URL\n * - Page refresh\n * - Wait f"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/screenshot.ts",
"chars": 3285,
"preview": "/**\n * Screenshot Action Handler\n *\n * Captures screenshots and optionally stores base64 data in variables.\n * Supports "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/script.ts",
"chars": 7126,
"preview": "/**\n * Script Action Handler\n *\n * Executes custom JavaScript in the page context.\n * Supports:\n * - MAIN or ISOLATED wo"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/scroll.ts",
"chars": 8982,
"preview": "/**\n * Scroll Action Handler\n *\n * Supports three scroll modes:\n * - offset: Scroll the window to absolute coordinates\n "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/tabs.ts",
"chars": 12422,
"preview": "/**\n * Tab Management Action Handlers\n *\n * Handles browser tab operations:\n * - openTab: Open a new tab or window\n * - "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/handlers/wait.ts",
"chars": 6428,
"preview": "/**\n * Wait Action Handler\n *\n * Handles various wait conditions:\n * - Sleep (fixed delay)\n * - Network idle\n * - Naviga"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/index.ts",
"chars": 731,
"preview": "/**\n * Action System - 导出模块\n */\n\n// 类型导出\nexport * from './types';\n\n// 注册表导出\nexport {\n ActionRegistry,\n createActionReg"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/registry.ts",
"chars": 19764,
"preview": "/**\n * Action Registry - Action 执行器注册表和执行管道\n *\n * 特性:\n * - 动态注册/注销 handler\n * - 中间件/钩子机制 (beforeExecute, afterExecute)\n "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/actions/types.ts",
"chars": 21497,
"preview": "/**\n * Action Type System for Record & Replay\n * 商业级录制回放的核心类型定义\n *\n * 设计原则:\n * - 类型安全,无 any\n * - 支持所有操作类型\n * - 支持重试、超时、错"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/constants.ts",
"chars": 994,
"preview": "// constants.ts — centralized engine constants and labels\nimport { EDGE_LABELS } from 'chrome-mcp-shared';\n\nexport const"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/execution-mode.ts",
"chars": 7191,
"preview": "/**\n * Execution Mode Configuration\n *\n * Controls whether step execution uses the legacy node system or the new ActionR"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/logging/run-logger.ts",
"chars": 2008,
"preview": "// engine/logging/run-logger.ts — run logs, overlay and persistence\nimport type { RunLogEntry, RunRecord, Flow } from '."
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/plugins/breakpoint.ts",
"chars": 630,
"preview": "import type { RunPlugin, StepContext } from './types';\nimport { runState } from '../state-manager';\n\nexport function bre"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/plugins/manager.ts",
"chars": 2150,
"preview": "import type {\n RunPlugin,\n HookControl,\n RunContext,\n StepContext,\n StepAfterContext,\n StepErrorContext,\n StepRet"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/plugins/types.ts",
"chars": 1642,
"preview": "// Plugin system for record-replay engine\n// Inspired by webpack-like lifecycle hooks, to avoid touching core for extens"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/policies/retry.ts",
"chars": 943,
"preview": "// engine/policies/retry.ts — unified retry/backoff policy\n\nexport type BackoffKind = 'none' | 'exp';\n\nexport interface "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/policies/wait.ts",
"chars": 3268,
"preview": "// engine/policies/wait.ts — wrappers around rr-utils navigation/network waits\n// Keep logic centralized to avoid duplic"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/runners/after-script-queue.ts",
"chars": 3390,
"preview": "// after-script-queue.ts — queue + executor for deferred after-scripts\n// Notes:\n// - Executes user-provided code in the"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/runners/control-flow-runner.ts",
"chars": 2145,
"preview": "// control-flow-runner.ts — foreach / while orchestration\n\nimport type { ExecCtx } from '../../nodes';\nimport { RunLogge"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/runners/step-executor.ts",
"chars": 7858,
"preview": "/**\n * Step Executor Interface\n *\n * Provides a unified interface for step execution that supports multiple execution mo"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/runners/step-runner.ts",
"chars": 8565,
"preview": "/**\n * step-runner.ts\n *\n * Encapsulates execution of a single step with policies (retry, navigation wait) and plugins.\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/runners/subflow-runner.ts",
"chars": 5595,
"preview": "// subflow-runner.ts — execute a subflow (nodes/edges) using DAG traversal with branch support\n\nimport { STEP_TYPES } fr"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/scheduler.ts",
"chars": 29370,
"preview": "import { STEP_TYPES, TOOL_NAMES } from 'chrome-mcp-shared';\nimport { TOOL_MESSAGE_TYPES } from '@/common/message-types';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/state-manager.ts",
"chars": 2260,
"preview": "// engine/state-manager.ts — lightweight run state store with events and persistence\n\ntype Listener<T> = (payload: T) =>"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/engine/utils/expression.ts",
"chars": 6178,
"preview": "// expression.ts — minimal safe boolean expression evaluator (no access to global scope)\n// Supported:\n// - Literals: nu"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/flow-runner.ts",
"chars": 145,
"preview": "// thin re-export for backward compatibility\nexport { runFlow } from './engine/scheduler';\nexport type { RunOptions } fr"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/flow-store.ts",
"chars": 13491,
"preview": "import type { Flow, RunRecord, NodeBase, Edge } from './types';\nimport { stepsToDAG, type RRNode, type RREdge } from 'ch"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/index.ts",
"chars": 18962,
"preview": "import { BACKGROUND_MESSAGE_TYPES, CONTENT_MESSAGE_TYPES } from '@/common/message-types';\nimport { Flow } from './types'"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/legacy-types.ts",
"chars": 6459,
"preview": "/**\n * Legacy Step Types for Record & Replay\n *\n * This file contains the legacy Step type system that is being phased o"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/assert.ts",
"chars": 4095,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/click.ts",
"chars": 4689,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/conditional.ts",
"chars": 1991,
"preview": "import type { Step } from '../types';\nimport type { ExecCtx, ExecResult, NodeRuntime } from './types';\n\nexport const ifN"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/download-screenshot-attr-event-frame-loop.ts",
"chars": 9870,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/drag.ts",
"chars": 1739,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/execute-flow.ts",
"chars": 3687,
"preview": "import type { ExecCtx, ExecResult, NodeRuntime } from './types';\n\nexport const executeFlowNode: NodeRuntime<any> = {\n v"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/extract.ts",
"chars": 1666,
"preview": "import type { StepExtract } from '../types';\nimport { expandTemplatesDeep } from '../rr-utils';\nimport type { ExecCtx, E"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/fill.ts",
"chars": 4685,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/http.ts",
"chars": 1066,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/index.ts",
"chars": 2443,
"preview": "import type { Step } from '../types';\nimport type { ExecCtx, ExecResult, NodeRuntime } from './types';\nimport { clickNod"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/key.ts",
"chars": 1040,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/loops.ts",
"chars": 1495,
"preview": "import type { ExecCtx, ExecResult, NodeRuntime } from './types';\nimport { ENGINE_CONSTANTS } from '../engine/constants';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/navigate.ts",
"chars": 659,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/script.ts",
"chars": 1305,
"preview": "import type { StepScript } from '../types';\nimport { expandTemplatesDeep, applyAssign } from '../rr-utils';\nimport type "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/scroll.ts",
"chars": 2298,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/tabs.ts",
"chars": 1988,
"preview": "import { TOOL_NAMES } from 'chrome-mcp-shared';\nimport { handleCallTool } from '@/entrypoints/background/tools';\nimport "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/types.ts",
"chars": 1143,
"preview": "import type { RunLogEntry, Step, StepScript } from '../types';\n\n/**\n * Execution context for step execution.\n * Contains"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/nodes/wait.ts",
"chars": 3109,
"preview": "import type { StepWait } from '../types';\nimport { waitForNetworkIdle, waitForNavigation } from '../rr-utils';\nimport { "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/browser-event-listener.ts",
"chars": 2694,
"preview": "import { addNavigationStep } from './flow-builder';\nimport { STEP_TYPES } from '@/common/step-types';\nimport { ensureRec"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/content-injection.ts",
"chars": 2695,
"preview": "import { TOOL_MESSAGE_TYPES } from '@/common/message-types';\n\n// Avoid magic strings for recorder control commands\nexpor"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/content-message-handler.ts",
"chars": 2823,
"preview": "import type { RecordingSessionManager } from './session-manager';\nimport type { Step, VariableDef } from '../types';\nimp"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/flow-builder.ts",
"chars": 3143,
"preview": "import type { Edge, Flow, NodeBase, Step } from '../types';\nimport { STEP_TYPES } from '@/common/step-types';\nimport { r"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/recorder-manager.ts",
"chars": 9877,
"preview": "import type { Flow } from '../types';\nimport { saveFlow } from '../flow-store';\nimport { broadcastControlToTab, ensureRe"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/recording/session-manager.ts",
"chars": 14050,
"preview": "import type { Edge, Flow, NodeBase, Step, VariableDef } from '../types';\nimport { TOOL_MESSAGE_TYPES } from '@/common/me"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/rr-utils.ts",
"chars": 8441,
"preview": "// rr-utils.ts — shared helpers for record-replay runner\n// Note: comments in English\n\nimport {\n TOOL_NAMES,\n topoOrde"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/selector-engine.ts",
"chars": 6778,
"preview": "import { TOOL_MESSAGE_TYPES } from '@/common/message-types';\nimport { TargetLocator, SelectorCandidate } from './types';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/storage/indexeddb-manager.ts",
"chars": 5672,
"preview": "// indexeddb-manager.ts\n// IndexedDB storage manager for Record & Replay data.\n// Stores: flows, runs, published, schedu"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/trigger-store.ts",
"chars": 1561,
"preview": "import { IndexedDbStorage, ensureMigratedFromLocal } from './storage/indexeddb-manager';\n\nexport type TriggerType = 'url"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay/types.ts",
"chars": 5151,
"preview": "/**\n * Record & Replay Core Types\n *\n * This file contains the core type definitions for the record-replay system.\n * Le"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/bootstrap.ts",
"chars": 12833,
"preview": "/**\n * @fileoverview Record-Replay V3 composition root (bootstrap)\n * @description\n * Wires storage, events, scheduler, "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/debug.ts",
"chars": 1993,
"preview": "/**\n * @fileoverview 调试器类型定义\n * @description 定义 Record-Replay V3 中的调试器状态和协议\n */\n\nimport type { JsonValue } from './json'"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/errors.ts",
"chars": 2038,
"preview": "/**\n * @fileoverview 错误类型定义\n * @description 定义 Record-Replay V3 中使用的错误码和错误类型\n */\n\nimport type { JsonValue } from './json"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/events.ts",
"chars": 4525,
"preview": "/**\n * @fileoverview 事件类型定义\n * @description 定义 Record-Replay V3 中的运行事件和状态\n */\n\nimport type { JsonObject, JsonValue, Unix"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/flow.ts",
"chars": 2307,
"preview": "/**\n * @fileoverview Flow 类型定义\n * @description 定义 Record-Replay V3 中的 Flow IR(中间表示)\n */\n\nimport type { ISODateTimeString"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/ids.ts",
"chars": 683,
"preview": "/**\n * @fileoverview ID 类型定义\n * @description 定义 Record-Replay V3 中使用的各种 ID 类型\n */\n\n/** Flow 唯一标识符 */\nexport type FlowId "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/index.ts",
"chars": 394,
"preview": "/**\n * @fileoverview Domain 层导出入口\n * @description 导出所有 Domain 类型定义\n */\n\n// JSON 基础类型\nexport * from './json';\n\n// ID 类型\ne"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/json.ts",
"chars": 502,
"preview": "/**\n * @fileoverview JSON 基础类型定义\n * @description 定义 Record-Replay V3 中使用的 JSON 相关类型\n */\n\n/** JSON 原始类型 */\nexport type Js"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/policy.ts",
"chars": 2542,
"preview": "/**\n * @fileoverview 策略类型定义\n * @description 定义 Record-Replay V3 中使用的超时、重试、错误处理和工件策略\n */\n\nimport type { EdgeLabel, NodeId"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/triggers.ts",
"chars": 2524,
"preview": "/**\n * @fileoverview 触发器类型定义\n * @description 定义 Record-Replay V3 中的触发器规范\n */\n\nimport type { JsonObject, UnixMillis } fro"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/domain/variables.ts",
"chars": 1946,
"preview": "/**\n * @fileoverview 变量类型定义\n * @description 定义 Record-Replay V3 中使用的变量指针和持久化变量\n */\n\nimport type { JsonValue, UnixMillis "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/index.ts",
"chars": 361,
"preview": "/**\n * @fileoverview Engine 层导出入口\n */\n\n// Kernel\nexport * from './kernel';\n\n// Queue\nexport * from './queue';\n\n// Plugin"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/keepalive/index.ts",
"chars": 82,
"preview": "/**\n * @fileoverview Keepalive 模块导出入口\n */\n\nexport * from './offscreen-keepalive';\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/keepalive/offscreen-keepalive.ts",
"chars": 12412,
"preview": "/**\n * @fileoverview Offscreen Keepalive Controller\n * @description Keeps the MV3 service worker alive using an Offscree"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/artifacts.ts",
"chars": 5045,
"preview": "/**\n * @fileoverview 工件(Artifacts)接口\n * @description 定义截图等工件的获取和存储接口\n */\n\nimport type { NodeId, RunId } from '../../doma"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/breakpoints.ts",
"chars": 3324,
"preview": "/**\n * @fileoverview 断点管理器\n * @description 管理调试断点的添加、删除和命中检测\n */\n\nimport type { NodeId, RunId } from '../../domain/ids';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/debug-controller.ts",
"chars": 13878,
"preview": "/**\n * @fileoverview Debug Controller\n * @description Central control plane for debugging - command routing, state aggre"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/index.ts",
"chars": 252,
"preview": "/**\n * @fileoverview Kernel 模块导出入口\n */\n\nexport * from './kernel';\nexport * from './runner';\nexport * from './traversal';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/kernel.ts",
"chars": 3217,
"preview": "/**\n * @fileoverview ExecutionKernel 接口定义\n * @description 定义 Record-Replay V3 的核心执行引擎接口\n */\n\nimport type { JsonObject } "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/recovery-kernel.ts",
"chars": 2890,
"preview": "/**\n * @fileoverview 支持崩溃恢复的 ExecutionKernel 实现 (P3-06)\n * @description\n * 提供 ExecutionKernel 的恢复增强实现,支持 `recover()` 方法。"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/runner.ts",
"chars": 25441,
"preview": "/**\n * @fileoverview RunRunner 接口和实现\n * @description 定义和实现单个 Run 的顺序执行器\n */\n\nimport type { NodeId, RunId } from '../../d"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/traversal.ts",
"chars": 4991,
"preview": "/**\n * @fileoverview DAG 遍历和校验\n * @description 提供 Flow DAG 的校验、遍历和下一节点查找功能\n */\n\nimport type { NodeId, EdgeLabel } from '"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/index.ts",
"chars": 169,
"preview": "/**\n * @fileoverview 插件系统导出入口\n */\n\nexport * from './types';\nexport * from './registry';\nexport * from './v2-action-adapt"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/register-v2-replay-nodes.ts",
"chars": 2860,
"preview": "/**\n * @fileoverview Register RR-V2 replay action handlers as RR-V3 nodes\n * @description\n * Batch registration of V2 ac"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/registry.ts",
"chars": 3036,
"preview": "/**\n * @fileoverview 插件注册表\n * @description 管理节点和触发器插件的注册和查询\n */\n\nimport type { NodeKind } from '../../domain/flow';\nimpo"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/types.ts",
"chars": 3778,
"preview": "/**\n * @fileoverview 插件类型定义\n * @description 定义 Record-Replay V3 中的节点和触发器插件接口\n */\n\nimport { z } from 'zod';\n\nimport type "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/plugins/v2-action-adapter.ts",
"chars": 13573,
"preview": "/**\n * @fileoverview V2 ActionHandler -> V3 NodeDefinition adapter\n * @description Bridges legacy RR-V2 action handlers "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/enqueue-run.ts",
"chars": 5422,
"preview": "/**\n * @fileoverview 共享入队服务\n * @description\n * 提供统一的 Run 入队逻辑,供 RPC Server 和 TriggerManager 共用。\n *\n * 设计理由:\n * - 将原本位于 R"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/index.ts",
"chars": 151,
"preview": "/**\n * @fileoverview Queue 模块导出入口\n */\n\nexport * from './queue';\nexport * from './leasing';\nexport * from './scheduler';\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/leasing.ts",
"chars": 2548,
"preview": "/**\n * @fileoverview 租约管理\n * @description 管理 Run 的租约续约和过期回收\n */\n\nimport type { UnixMillis } from '../../domain/json';\nim"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/queue.ts",
"chars": 4244,
"preview": "/**\n * @fileoverview RunQueue 接口定义\n * @description 定义 Run 队列的管理接口\n */\n\nimport type { JsonObject, UnixMillis } from '../."
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/queue/scheduler.ts",
"chars": 9599,
"preview": "/**\n * @fileoverview RunQueue scheduler (maxParallelRuns)\n * @description\n * Orchestrates atomic claims from RunQueue an"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/recovery/index.ts",
"chars": 115,
"preview": "/**\n * @fileoverview Recovery module exports\n * @description 崩溃恢复模块导出\n */\n\nexport * from './recovery-coordinator';\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/recovery/recovery-coordinator.ts",
"chars": 7685,
"preview": "/**\n * @fileoverview 崩溃恢复协调器 (P3-06)\n * @description\n * MV3 Service Worker 可能随时被终止。此协调器在 SW 启动时协调队列状态和 Run 记录,\n * 使中断的 R"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/storage/index.ts",
"chars": 80,
"preview": "/**\n * @fileoverview Engine Storage 模块导出入口\n */\n\nexport * from './storage-port';\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/storage/storage-port.ts",
"chars": 3716,
"preview": "/**\n * @fileoverview StoragePort 接口定义\n * @description 定义 Storage 层的抽象接口,用于依赖注入\n */\n\nimport type { FlowId, RunId, Trigger"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/events-bus.ts",
"chars": 5646,
"preview": "/**\n * @fileoverview EventsBus Interface and Implementation\n * @description Event subscription, publishing, and persiste"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/index.ts",
"chars": 126,
"preview": "/**\n * @fileoverview Transport 模块导出入口\n */\n\nexport * from './rpc';\nexport * from './rpc-server';\nexport * from './events-"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/rpc-server.ts",
"chars": 37188,
"preview": "/**\n * @fileoverview RPC Server Implementation\n * @description Handles RPC requests from UI via chrome.runtime.Port\n */\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/transport/rpc.ts",
"chars": 3500,
"preview": "/**\n * @fileoverview Port RPC 协议定义\n * @description 定义通过 chrome.runtime.Port 进行通信的协议类型\n */\n\nimport type { JsonObject, Jso"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/command-trigger.ts",
"chars": 4282,
"preview": "/**\n * @fileoverview Command Trigger Handler (P4-04)\n * @description\n * Listens to `chrome.commands.onCommand` and fires"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/context-menu-trigger.ts",
"chars": 6438,
"preview": "/**\n * @fileoverview ContextMenu Trigger Handler (P4-05)\n * @description\n * Uses `chrome.contextMenus` API to create rig"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/cron-trigger.ts",
"chars": 16922,
"preview": "/**\n * @fileoverview Cron Trigger Handler (P4-07)\n * @description\n * Schedules cron triggers via `chrome.alarms` (MV3).\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/dom-trigger.ts",
"chars": 11054,
"preview": "/**\n * @fileoverview DOM Trigger Handler (P4-06)\n * @description\n * Bridges DOM triggers to a content-script MutationObs"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/index.ts",
"chars": 315,
"preview": "/**\n * @fileoverview Triggers 模块导出入口\n */\n\nexport * from './trigger-handler';\nexport * from './trigger-manager';\nexport *"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/interval-trigger.ts",
"chars": 6315,
"preview": "/**\n * @fileoverview Interval Trigger Handler (M3.1)\n * @description\n * 使用 chrome.alarms 的 periodInMinutes 实现固定间隔触发。\n *\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/manual-trigger.ts",
"chars": 1931,
"preview": "/**\n * @fileoverview Manual Trigger Handler (P4-08)\n * @description\n * Manual triggers are the simplest trigger type - t"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/once-trigger.ts",
"chars": 7521,
"preview": "/**\n * @fileoverview Once Trigger Handler (M3.1)\n * @description\n * 使用 chrome.alarms 的 when 参数实现一次性定时触发。\n *\n * 行为:\n * - "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/trigger-handler.ts",
"chars": 1208,
"preview": "/**\n * @fileoverview 触发器处理器接口定义\n * @description 定义各类触发器的统一接口\n */\n\nimport type { TriggerSpec, TriggerKind } from '../../d"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/trigger-manager.ts",
"chars": 11210,
"preview": "/**\n * @fileoverview 触发器管理器\n * @description\n * TriggerManager 负责管理所有触发器 Handler 的生命周期:\n * - 从 TriggerStore 加载触发器并安装\n * -"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/url-trigger.ts",
"chars": 7101,
"preview": "/**\n * @fileoverview URL Trigger Handler (P4-03)\n * @description\n * Listens to `chrome.webNavigation.onCompleted` and fi"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/index.ts",
"chars": 1276,
"preview": "/**\n * @fileoverview Record-Replay V3 公共 API 入口\n * @description 导出所有公共类型和接口\n */\n\n// ==================== Domain ========"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/db.ts",
"chars": 5783,
"preview": "/**\n * @fileoverview V3 IndexedDB 数据库定义\n * @description 定义 rr_v3 数据库的 schema 和初始化逻辑\n */\n\n/** 数据库名称 */\nexport const RR_V3"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/events.ts",
"chars": 4425,
"preview": "/**\n * @fileoverview RunEvent 持久化\n * @description 实现事件的原子 seq 分配和存储\n */\n\nimport type { RunId } from '../domain/ids';\nimp"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/flows.ts",
"chars": 3600,
"preview": "/**\n * @fileoverview FlowV3 持久化\n * @description 实现 Flow 的 CRUD 操作\n */\n\nimport type { FlowId } from '../domain/ids';\nimpo"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/import/index.ts",
"chars": 97,
"preview": "/**\n * @fileoverview Import 模块导出入口\n */\n\nexport * from './v2-reader';\nexport * from './v2-to-v3';\n"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/import/v2-reader.ts",
"chars": 727,
"preview": "/**\n * @fileoverview V2 数据读取器\n * @description 读取 V2 格式的数据(占位实现)\n */\n\n/**\n * V2 数据读取器接口\n * @description Phase 5+ 实现\n */\ne"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/import/v2-to-v3.ts",
"chars": 16705,
"preview": "/**\n * @fileoverview V2 到 V3 数据转换器\n * @description 将 V2 格式数据转换为 V3 格式,支持双向转换\n */\n\nimport type { FlowV3, NodeV3, EdgeV3, "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/index.ts",
"chars": 251,
"preview": "/**\n * @fileoverview Storage 层导出入口\n */\n\nexport * from './db';\nexport * from './flows';\nexport * from './runs';\nexport * "
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/persistent-vars.ts",
"chars": 3243,
"preview": "/**\n * @fileoverview 持久化变量存储\n * @description 实现 $ 前缀变量的持久化,使用 LWW(Last-Write-Wins)策略\n */\n\nimport type { PersistentVarRec"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/queue.ts",
"chars": 18469,
"preview": "/**\n * @fileoverview RunQueue 持久化\n * @description 实现队列的 CRUD 操作和原子 claim\n */\n\nimport type { RunId } from '../domain/ids'"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/runs.ts",
"chars": 3646,
"preview": "/**\n * @fileoverview RunRecordV3 持久化\n * @description 实现 Run 记录的 CRUD 操作\n */\n\nimport type { RunId } from '../domain/ids';"
},
{
"path": "app/chrome-extension/entrypoints/background/record-replay-v3/storage/triggers.ts",
"chars": 2183,
"preview": "/**\n * @fileoverview 触发器存储\n * @description 实现触发器的 CRUD 操作(Phase 4 完整实现)\n */\n\nimport type { TriggerId } from '../domain/i"
},
{
"path": "app/chrome-extension/entrypoints/background/semantic-similarity.ts",
"chars": 12485,
"preview": "import type { ModelPreset } from '@/utils/semantic-similarity-engine';\nimport { OffscreenManager } from '@/utils/offscre"
},
{
"path": "app/chrome-extension/entrypoints/background/storage-manager.ts",
"chars": 3904,
"preview": "import { BACKGROUND_MESSAGE_TYPES } from '@/common/message-types';\n\n/**\n * Get storage statistics\n */\nexport async funct"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/base-browser.ts",
"chars": 5655,
"preview": "import { ToolExecutor } from '@/common/tool-handler';\nimport type { ToolResult } from '@/common/tool-handler';\nimport { "
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/bookmark.ts",
"chars": 20031,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/common.ts",
"chars": 22925,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/computer.ts",
"chars": 50559,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/console-buffer.ts",
"chars": 13156,
"preview": "import { cdpSessionManager } from '@/utils/cdp-session-manager';\n\n/**\n * ConsoleBuffer - 持久化的控制台日志缓冲管理器\n *\n * 为每个 tab 维护"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/console.ts",
"chars": 21495,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/dialog.ts",
"chars": 1911,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/download.ts",
"chars": 4234,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/element-picker.ts",
"chars": 18399,
"preview": "/**\n * Element Picker Tool\n *\n * Implements chrome_request_element_selection - a human-in-the-loop tool that allows\n * u"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/file-upload.ts",
"chars": 7799,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/gif-auto-capture.ts",
"chars": 14949,
"preview": "/**\n * GIF Auto-Capture Hook System\n *\n * Provides automatic frame capture for GIF recording when browser actions succee"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/gif-enhanced-renderer.ts",
"chars": 25374,
"preview": "/**\n * GIF Enhanced Renderer\n *\n * Draws visual affordances (click indicators, drag paths, labels) onto a canvas\n * befo"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/gif-recorder.ts",
"chars": 39315,
"preview": "/**\n * GIF Recorder Tool\n *\n * Records browser tab activity as an animated GIF.\n *\n * Features:\n * - Two recording modes"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/history.ts",
"chars": 7295,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/index.ts",
"chars": 1582,
"preview": "export { navigateTool, closeTabsTool, switchTabTool } from './common';\nexport { windowTool } from './window';\nexport { v"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/inject-script.ts",
"chars": 7699,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/interaction.ts",
"chars": 8444,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/javascript.ts",
"chars": 14925,
"preview": "/**\n * JavaScript Tool - CDP Runtime.evaluate with fallback\n *\n * Execute JavaScript in the browser tab and return the r"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/keyboard.ts",
"chars": 5142,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/network-capture-debugger.ts",
"chars": 39142,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/network-capture-web-request.ts",
"chars": 32256,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/network-capture.ts",
"chars": 5201,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/network-request.ts",
"chars": 3128,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/performance.ts",
"chars": 17969,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/read-page.ts",
"chars": 9212,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/screenshot.ts",
"chars": 21285,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/userscript.ts",
"chars": 25983,
"preview": "import { createErrorResponse, ToolResult } from '@/common/tool-handler';\nimport { BaseBrowserToolExecutor } from '../bas"
},
{
"path": "app/chrome-extension/entrypoints/background/tools/browser/vector-search.ts",
"chars": 9288,
"preview": "/**\n * Vectorized tab content search tool\n * Uses vector database for efficient semantic search\n */\n\nimport { createErro"
}
]
// ... and 452 more files (download for full content)
About this extraction
This page contains the full source code of the hangwin/mcp-chrome GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 652 files (37.5 MB), approximately 1.6M tokens, and a symbol index with 4919 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.