#
tokens: 49508/50000 131/574 files (page 1/60)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 60. Use http://codebase.md/hangwin/mcp-chrome?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   └── workflows
│       └── build-release.yml
├── .gitignore
├── .husky
│   ├── commit-msg
│   └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── .vscode
│   └── extensions.json
├── app
│   ├── chrome-extension
│   │   ├── _locales
│   │   │   ├── de
│   │   │   │   └── messages.json
│   │   │   ├── en
│   │   │   │   └── messages.json
│   │   │   ├── ja
│   │   │   │   └── messages.json
│   │   │   ├── ko
│   │   │   │   └── messages.json
│   │   │   ├── zh_CN
│   │   │   │   └── messages.json
│   │   │   └── zh_TW
│   │   │       └── messages.json
│   │   ├── .env.example
│   │   ├── assets
│   │   │   └── vue.svg
│   │   ├── 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
│   │   │   │   │   ├── builder
│   │   │   │   │   │   ├── components
│   │   │   │   │   │   │   ├── Canvas.vue
│   │   │   │   │   │   │   ├── EdgePropertyPanel.vue
│   │   │   │   │   │   │   ├── KeyValueEditor.vue
│   │   │   │   │   │   │   ├── nodes
│   │   │   │   │   │   │   │   ├── node-util.ts
│   │   │   │   │   │   │   │   ├── NodeCard.vue
│   │   │   │   │   │   │   │   └── NodeIf.vue
│   │   │   │   │   │   │   ├── 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
│   │   │   │   │   │   │   ├── PropertyPanel.vue
│   │   │   │   │   │   │   ├── Sidebar.vue
│   │   │   │   │   │   │   └── TriggerPanel.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
│   │   │   │   │   ├── ConfirmDialog.vue
│   │   │   │   │   ├── ElementMarkerManagement.vue
│   │   │   │   │   ├── icons
│   │   │   │   │   │   ├── BoltIcon.vue
│   │   │   │   │   │   ├── CheckIcon.vue
│   │   │   │   │   │   ├── DatabaseIcon.vue
│   │   │   │   │   │   ├── DocumentIcon.vue
│   │   │   │   │   │   ├── EditIcon.vue
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── MarkerIcon.vue
│   │   │   │   │   │   ├── RecordIcon.vue
│   │   │   │   │   │   ├── RefreshIcon.vue
│   │   │   │   │   │   ├── StopIcon.vue
│   │   │   │   │   │   ├── TabIcon.vue
│   │   │   │   │   │   ├── TrashIcon.vue
│   │   │   │   │   │   ├── VectorIcon.vue
│   │   │   │   │   │   └── WorkflowIcon.vue
│   │   │   │   │   ├── LocalModelPage.vue
│   │   │   │   │   ├── ModelCacheManagement.vue
│   │   │   │   │   ├── ProgressIndicator.vue
│   │   │   │   │   └── ScheduleDialog.vue
│   │   │   │   ├── 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
│   │   │   │   │   ├── agent
│   │   │   │   │   │   ├── AttachmentPreview.vue
│   │   │   │   │   │   ├── ChatInput.vue
│   │   │   │   │   │   ├── CliSettings.vue
│   │   │   │   │   │   ├── ConnectionStatus.vue
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── MessageItem.vue
│   │   │   │   │   │   ├── MessageList.vue
│   │   │   │   │   │   ├── ProjectCreateForm.vue
│   │   │   │   │   │   └── ProjectSelector.vue
│   │   │   │   │   ├── 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
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── SelectionChip.vue
│   │   │   │   │   │   ├── timeline
│   │   │   │   │   │   │   ├── markstream-thinking.ts
│   │   │   │   │   │   │   ├── ThinkingNode.vue
│   │   │   │   │   │   │   ├── TimelineNarrativeStep.vue
│   │   │   │   │   │   │   ├── TimelineStatusStep.vue
│   │   │   │   │   │   │   ├── TimelineToolCallStep.vue
│   │   │   │   │   │   │   ├── TimelineToolResultCardStep.vue
│   │   │   │   │   │   │   └── TimelineUserPromptStep.vue
│   │   │   │   │   │   └── WebEditorChanges.vue
│   │   │   │   │   ├── AgentChat.vue
│   │   │   │   │   ├── rr-v3
│   │   │   │   │   │   └── DebuggerPanel.vue
│   │   │   │   │   ├── SidepanelNavigator.vue
│   │   │   │   │   └── workflows
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── WorkflowListItem.vue
│   │   │   │   │       └── WorkflowsView.vue
│   │   │   │   ├── 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
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── public
│   │   │   ├── icon
│   │   │   │   ├── 128.png
│   │   │   │   ├── 16.png
│   │   │   │   ├── 32.png
│   │   │   │   ├── 48.png
│   │   │   │   └── 96.png
│   │   │   ├── libs
│   │   │   │   └── ort.min.js
│   │   │   └── wxt.svg
│   │   ├── README.md
│   │   ├── 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_bg.wasm
│   │   │   ├── simd_math.js
│   │   │   └── similarity.worker.js
│   │   └── wxt.config.ts
│   └── native-server
│       ├── .npmignore
│       ├── debug.sh
│       ├── install.md
│       ├── jest.config.js
│       ├── package.json
│       ├── README.md
│       ├── 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_zh.md
│   ├── ARCHITECTURE.md
│   ├── CHANGELOG.md
│   ├── CONTRIBUTING_zh.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE.md
│   ├── mcp-cli-config.md
│   ├── TOOLS_zh.md
│   ├── TOOLS.md
│   ├── TROUBLESHOOTING_zh.md
│   ├── TROUBLESHOOTING.md
│   ├── VisualEditor_zh.md
│   ├── VisualEditor.md
│   └── WINDOWS_INSTALL_zh.md
├── eslint.config.js
├── LICENSE
├── 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
│       ├── package.json
│       ├── README.md
│       └── src
│           └── lib.rs
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── prompt
│   ├── content-analize.md
│   ├── excalidraw-prompt.md
│   └── modify-web.md
├── README_zh.md
├── README.md
└── releases
    ├── chrome-extension
    │   └── latest
    │       └── chrome-mcp-server-lastest.zip
    └── README.md
```

# Files

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
1 | *.onnx filter=lfs diff=lfs merge=lfs -text
2 | 
```

--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "semi": true,
 3 |   "singleQuote": true,
 4 |   "tabWidth": 2,
 5 |   "printWidth": 100,
 6 |   "endOfLine": "auto",
 7 |   "proseWrap": "preserve",
 8 |   "htmlWhitespaceSensitivity": "strict"
 9 | }
10 | 
```

--------------------------------------------------------------------------------
/packages/wasm-simd/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # WASM build outputs
 2 | /pkg/
 3 | /target/
 4 | 
 5 | # Rust
 6 | Cargo.lock
 7 | 
 8 | # Node.js
 9 | node_modules/
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | 
14 | # IDE
15 | .vscode/
16 | .idea/
17 | *.swp
18 | *.swo
19 | 
20 | # OS
21 | .DS_Store
22 | Thumbs.db
23 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/.env.example:
--------------------------------------------------------------------------------

```
1 | # Chrome Extension Private Key
2 | # Copy this file to .env and replace with your actual private key
3 | # This key is used for Chrome extension packaging and should be kept secure
4 | CHROME_EXTENSION_KEY=YOUR_PRIVATE_KEY_HERE
5 | 
```

--------------------------------------------------------------------------------
/app/native-server/.npmignore:
--------------------------------------------------------------------------------

```
1 | # Development-only files that should not be published to npm
2 | 
3 | # node_path.txt contains the absolute path to Node.js used during build.
4 | # It's written by build.ts for development hot-reload, but is useless
5 | # (and potentially confusing) in the published package since users will
6 | # have their own Node.js path written by postinstall.
7 | node_path.txt
8 | **/node_path.txt
9 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Logs
 2 | logs
 3 | *.log
 4 | npm-debug.log*
 5 | yarn-debug.log*
 6 | yarn-error.log*
 7 | pnpm-debug.log*
 8 | lerna-debug.log*
 9 | 
10 | node_modules
11 | .output
12 | stats.html
13 | stats-*.json
14 | .wxt
15 | web-ext.config.ts
16 | dist
17 | 
18 | # Editor directories and files
19 | .vscode/*
20 | !.vscode/extensions.json
21 | .idea
22 | .DS_Store
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 | *.onnx
29 | 
30 | # Environment variables
31 | .env
32 | .env.local
33 | .env.*.local
34 | 
35 | # Prevent npm metadata pollution
36 | false/
37 | metadata-v1.3/
38 | registry.npmmirror.com/
39 | registry.npmjs.com/
40 | 
41 | other/
42 | tools_optimize.md
43 | Agents.md
44 | CLAUDE.md
45 | 
46 | **/*/coverage/*
47 | 
48 | .docs/
49 | .claude/
```

--------------------------------------------------------------------------------
/app/chrome-extension/README.md:
--------------------------------------------------------------------------------

```markdown
1 | # WXT + Vue 3
2 | 
3 | This template should help get you started developing with Vue 3 in WXT.
4 | 
5 | ## Recommended IDE Setup
6 | 
7 | - [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
8 | 
```

--------------------------------------------------------------------------------
/packages/wasm-simd/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # @chrome-mcp/wasm-simd
 2 | 
 3 | SIMD-optimized WebAssembly math functions for high-performance vector operations.
 4 | 
 5 | ## Features
 6 | 
 7 | - 🚀 **SIMD Acceleration**: Uses WebAssembly SIMD instructions for 4-8x performance boost
 8 | - 🧮 **Vector Operations**: Optimized cosine similarity, batch processing, and matrix operations
 9 | - 🔧 **Memory Efficient**: Smart memory pooling and aligned buffer management
10 | - 🌐 **Browser Compatible**: Works in all modern browsers with WebAssembly SIMD support
11 | 
12 | ## Performance
13 | 
14 | | Operation                      | JavaScript | SIMD WASM | Speedup |
15 | | ------------------------------ | ---------- | --------- | ------- |
16 | | Cosine Similarity (768d)       | 100ms      | 18ms      | 5.6x    |
17 | | Batch Similarity (100x768d)    | 850ms      | 95ms      | 8.9x    |
18 | | Similarity Matrix (50x50x384d) | 2.1s       | 180ms     | 11.7x   |
19 | 
20 | ## Usage
21 | 
22 | ```rust
23 | // The Rust implementation provides SIMD-optimized functions
24 | use wasm_bindgen::prelude::*;
25 | 
26 | #[wasm_bindgen]
27 | pub struct SIMDMath;
28 | 
29 | #[wasm_bindgen]
30 | impl SIMDMath {
31 |     #[wasm_bindgen(constructor)]
32 |     pub fn new() -> SIMDMath { SIMDMath }
33 | 
34 |     #[wasm_bindgen]
35 |     pub fn cosine_similarity(&self, vec_a: &[f32], vec_b: &[f32]) -> f32 {
36 |         // SIMD-optimized implementation
37 |     }
38 | }
39 | ```
40 | 
41 | ## Building
42 | 
43 | ```bash
44 | # Install dependencies
45 | cargo install wasm-pack
46 | 
47 | # Build for release
48 | npm run build
49 | 
50 | # Build for development
51 | npm run build:dev
52 | ```
53 | 
54 | ## Browser Support
55 | 
56 | - Chrome 91+
57 | - Firefox 89+
58 | - Safari 16.4+
59 | - Edge 91+
60 | 
61 | Older browsers automatically fall back to JavaScript implementations.
62 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Chrome MCP Server 🚀
  2 | 
  3 | [![Stars](https://img.shields.io/github/stars/hangwin/mcp-chrome)](https://img.shields.io/github/stars/hangwin/mcp-chrome)
  4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
  5 | [![TypeScript](https://img.shields.io/badge/TypeScript-5.8+-blue.svg)](https://www.typescriptlang.org/)
  6 | [![Chrome Extension](https://img.shields.io/badge/Chrome-Extension-green.svg)](https://developer.chrome.com/docs/extensions/)
  7 | [![Release](https://img.shields.io/github/v/release/hangwin/mcp-chrome.svg)](https://img.shields.io/github/v/release/hangwin/mcp-chrome.svg)
  8 | 
  9 | > 🌟 **Turn your Chrome browser into your intelligent assistant** - Let AI take control of your browser, transforming it into a powerful AI-controlled automation tool.
 10 | 
 11 | **📖 Documentation**: [English](README.md) | [中文](README_zh.md)
 12 | 
 13 | > The project is still in its early stages and is under intensive development. More features, stability improvements, and other enhancements will follow.
 14 | 
 15 | ---
 16 | 
 17 | ## 🎯 What is Chrome MCP Server?
 18 | 
 19 | 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.
 20 | 
 21 | ## ✨ New Features(2025/12/30)
 22 | 
 23 | - **A New Visual Editor for Claude Code & Codex**, for more detail here: [VisualEditor](docs/VisualEditor.md)
 24 | 
 25 | ## ✨ Core Features
 26 | 
 27 | - 😁 **Chatbot/Model Agnostic**: Let any LLM or chatbot client or agent you prefer automate your browser
 28 | - ⭐️ **Use Your Original Browser**: Seamlessly integrate with your existing browser environment (your configurations, login states, etc.)
 29 | - 💻 **Fully Local**: Pure local MCP server ensuring user privacy
 30 | - 🚄 **Streamable HTTP**: Streamable HTTP connection method
 31 | - 🏎 **Cross-Tab**: Cross-tab context
 32 | - 🧠 **Semantic Search**: Built-in vector database for intelligent browser tab content discovery
 33 | - 🔍 **Smart Content Analysis**: AI-powered text extraction and similarity matching
 34 | - 🌐 **20+ Tools**: Support for screenshots, network monitoring, interactive operations, bookmark management, browsing history, and 20+ other tools
 35 | - 🚀 **SIMD-Accelerated AI**: Custom WebAssembly SIMD optimization for 4-8x faster vector operations
 36 | 
 37 | ## 🆚 Comparison with Similar Projects
 38 | 
 39 | | Comparison Dimension    | Playwright-based MCP Server                                                                                               | Chrome Extension-based MCP Server                                                                      |
 40 | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
 41 | | **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 |
 42 | | **User Session Reuse**  | ❌ Requires re-login                                                                                                      | ✅ Automatically uses existing login state                                                             |
 43 | | **Browser Environment** | ❌ Clean environment lacks user settings                                                                                  | ✅ Fully preserves user environment                                                                    |
 44 | | **API Access**          | ⚠️ Limited to Playwright API                                                                                              | ✅ Full access to Chrome native APIs                                                                   |
 45 | | **Startup Speed**       | ❌ Requires launching browser process                                                                                     | ✅ Only needs to activate extension                                                                    |
 46 | | **Response Speed**      | 50-200ms inter-process communication                                                                                      | ✅ Faster                                                                                              |
 47 | 
 48 | ## 🚀 Quick Start
 49 | 
 50 | ### Prerequisites
 51 | 
 52 | - Node.js >= 20.0.0 and pnpm/npm
 53 | - Chrome/Chromium browser
 54 | 
 55 | ### Installation Steps
 56 | 
 57 | 1. **Download the latest Chrome extension from GitHub**
 58 | 
 59 | Download link: https://github.com/hangwin/mcp-chrome/releases
 60 | 
 61 | 2. **Install mcp-chrome-bridge globally**
 62 | 
 63 | npm
 64 | 
 65 | ```bash
 66 | npm install -g mcp-chrome-bridge
 67 | ```
 68 | 
 69 | pnpm
 70 | 
 71 | ```bash
 72 | # Method 1: Enable scripts globally (recommended)
 73 | pnpm config set enable-pre-post-scripts true
 74 | pnpm install -g mcp-chrome-bridge
 75 | 
 76 | # Method 2: Manual registration (if postinstall doesn't run)
 77 | pnpm install -g mcp-chrome-bridge
 78 | mcp-chrome-bridge register
 79 | ```
 80 | 
 81 | > 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.
 82 | 
 83 | 3. **Load Chrome Extension**
 84 |    - Open Chrome and go to `chrome://extensions/`
 85 |    - Enable "Developer mode"
 86 |    - Click "Load unpacked" and select `your/dowloaded/extension/folder`
 87 |    - Click the extension icon to open the plugin, then click connect to see the MCP configuration
 88 |      <img width="475" alt="Screenshot 2025-06-09 15 52 06" src="https://github.com/user-attachments/assets/241e57b8-c55f-41a4-9188-0367293dc5bc" />
 89 | 
 90 | ### Usage with MCP Protocol Clients
 91 | 
 92 | #### Using Streamable HTTP Connection (👍🏻 Recommended)
 93 | 
 94 | Add the following configuration to your MCP client configuration (using CherryStudio as an example):
 95 | 
 96 | > Streamable HTTP connection method is recommended
 97 | 
 98 | ```json
 99 | {
100 |   "mcpServers": {
101 |     "chrome-mcp-server": {
102 |       "type": "streamableHttp",
103 |       "url": "http://127.0.0.1:12306/mcp"
104 |     }
105 |   }
106 | }
107 | ```
108 | 
109 | #### Using STDIO Connection (Alternative)
110 | 
111 | If your client only supports stdio connection method, please use the following approach:
112 | 
113 | 1. First, check the installation location of the npm package you just installed
114 | 
115 | ```sh
116 | # npm check method
117 | npm list -g mcp-chrome-bridge
118 | # pnpm check method
119 | pnpm list -g mcp-chrome-bridge
120 | ```
121 | 
122 | Assuming the command above outputs the path: /Users/xxx/Library/pnpm/global/5
123 | Then your final path would be: /Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js
124 | 
125 | 2. Replace the configuration below with the final path you just obtained
126 | 
127 | ```json
128 | {
129 |   "mcpServers": {
130 |     "chrome-mcp-stdio": {
131 |       "command": "npx",
132 |       "args": [
133 |         "node",
134 |         "/Users/xxx/Library/pnpm/global/5/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js"
135 |       ]
136 |     }
137 |   }
138 | }
139 | ```
140 | 
141 | eg:config in augment:
142 | 
143 | <img width="494" alt="截屏2025-06-22 22 11 25" src="https://github.com/user-attachments/assets/48eefc0c-a257-4d3b-8bbe-d7ff716de2bf" />
144 | 
145 | ## 🛠️ Available Tools
146 | 
147 | Complete tool list: [Complete Tool List](docs/TOOLS.md)
148 | 
149 | <details>
150 | <summary><strong>📊 Browser Management (6 tools)</strong></summary>
151 | 
152 | - `get_windows_and_tabs` - List all browser windows and tabs
153 | - `chrome_navigate` - Navigate to URLs and control viewport
154 | - `chrome_switch_tab` - Switch the current active tab
155 | - `chrome_close_tabs` - Close specific tabs or windows
156 | - `chrome_go_back_or_forward` - Browser navigation control
157 | - `chrome_inject_script` - Inject content scripts into web pages
158 | - `chrome_send_command_to_inject_script` - Send commands to injected content scripts
159 | </details>
160 | 
161 | <details>
162 | <summary><strong>📸 Screenshots & Visual (1 tool)</strong></summary>
163 | 
164 | - `chrome_screenshot` - Advanced screenshot capture with element targeting, full-page support, and custom dimensions
165 | </details>
166 | 
167 | <details>
168 | <summary><strong>🌐 Network Monitoring (4 tools)</strong></summary>
169 | 
170 | - `chrome_network_capture_start/stop` - webRequest API network capture
171 | - `chrome_network_debugger_start/stop` - Debugger API with response bodies
172 | - `chrome_network_request` - Send custom HTTP requests
173 | </details>
174 | 
175 | <details>
176 | <summary><strong>🔍 Content Analysis (4 tools)</strong></summary>
177 | 
178 | - `search_tabs_content` - AI-powered semantic search across browser tabs
179 | - `chrome_get_web_content` - Extract HTML/text content from pages
180 | - `chrome_get_interactive_elements` - Find clickable elements
181 | - `chrome_console` - Capture and retrieve console output from browser tabs
182 | </details>
183 | 
184 | <details>
185 | <summary><strong>🎯 Interaction (3 tools)</strong></summary>
186 | 
187 | - `chrome_click_element` - Click elements using CSS selectors
188 | - `chrome_fill_or_select` - Fill forms and select options
189 | - `chrome_keyboard` - Simulate keyboard input and shortcuts
190 | </details>
191 | 
192 | <details>
193 | <summary><strong>📚 Data Management (5 tools)</strong></summary>
194 | 
195 | - `chrome_history` - Search browser history with time filters
196 | - `chrome_bookmark_search` - Find bookmarks by keywords
197 | - `chrome_bookmark_add` - Add new bookmarks with folder support
198 | - `chrome_bookmark_delete` - Delete bookmarks
199 | </details>
200 | 
201 | ## 🧪 Usage Examples
202 | 
203 | ### AI helps you summarize webpage content and automatically control Excalidraw for drawing
204 | 
205 | prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)
206 | Instruction: Help me summarize the current page content, then draw a diagram to aid my understanding.
207 | https://www.youtube.com/watch?v=3fBPdUBWVz0
208 | 
209 | https://github.com/user-attachments/assets/fd17209b-303d-48db-9e5e-3717141df183
210 | 
211 | ### After analyzing the content of the image, the LLM automatically controls Excalidraw to replicate the image
212 | 
213 | prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)|[content-analize](prompt/content-analize.md)
214 | Instruction: First, analyze the content of the image, and then replicate the image by combining the analysis with the content of the image.
215 | https://www.youtube.com/watch?v=tEPdHZBzbZk
216 | 
217 | https://github.com/user-attachments/assets/60d12b1a-9b74-40f4-994c-95e8fa1fc8d3
218 | 
219 | ### AI automatically injects scripts and modifies webpage styles
220 | 
221 | prompt: [modify-web-prompt](prompt/modify-web.md)
222 | Instruction: Help me modify the current page's style and remove advertisements.
223 | https://youtu.be/twI6apRKHsk
224 | 
225 | https://github.com/user-attachments/assets/69cb561c-2e1e-4665-9411-4a3185f9643e
226 | 
227 | ### AI automatically captures network requests for you
228 | 
229 | query: I want to know what the search API for Xiaohongshu is and what the response structure looks like
230 | 
231 | https://youtu.be/1hHKr7XKqnQ
232 | 
233 | https://github.com/user-attachments/assets/dc7e5cab-b9af-4b9a-97ce-18e4837318d9
234 | 
235 | ### AI helps analyze your browsing history
236 | 
237 | query: Analyze my browsing history from the past month
238 | 
239 | https://youtu.be/jf2UZfrR2Vk
240 | 
241 | https://github.com/user-attachments/assets/31b2e064-88c6-4adb-96d7-50748b826eae
242 | 
243 | ### Web page conversation
244 | 
245 | query: Translate and summarize the current web page
246 | https://youtu.be/FlJKS9UQyC8
247 | 
248 | https://github.com/user-attachments/assets/aa8ef2a1-2310-47e6-897a-769d85489396
249 | 
250 | ### AI automatically takes screenshots for you (web page screenshots)
251 | 
252 | query: Take a screenshot of Hugging Face's homepage
253 | https://youtu.be/7ycK6iksWi4
254 | 
255 | https://github.com/user-attachments/assets/65c6eee2-6366-493d-a3bd-2b27529ff5b3
256 | 
257 | ### AI automatically takes screenshots for you (element screenshots)
258 | 
259 | query: Capture the icon from Hugging Face's homepage
260 | https://youtu.be/ev8VivANIrk
261 | 
262 | https://github.com/user-attachments/assets/d0cf9785-c2fe-4729-a3c5-7f2b8b96fe0c
263 | 
264 | ### AI helps manage bookmarks
265 | 
266 | query: Add the current page to bookmarks and put it in an appropriate folder
267 | 
268 | https://youtu.be/R_83arKmFTo
269 | 
270 | https://github.com/user-attachments/assets/15a7d04c-0196-4b40-84c2-bafb5c26dfe0
271 | 
272 | ### Automatically close web pages
273 | 
274 | query: Close all shadcn-related web pages
275 | 
276 | https://youtu.be/2wzUT6eNVg4
277 | 
278 | https://github.com/user-attachments/assets/83de4008-bb7e-494d-9b0f-98325cfea592
279 | 
280 | ## 🤝 Contributing
281 | 
282 | We welcome contributions! Please see [CONTRIBUTING.md](docs/CONTRIBUTING.md) for detailed guidelines.
283 | 
284 | ## 🚧 Future Roadmap
285 | 
286 | We have exciting plans for the future development of Chrome MCP Server:
287 | 
288 | - [ ] Authentication
289 | - [ ] Recording and Playback
290 | - [ ] Workflow Automation
291 | - [ ] Enhanced Browser Support (Firefox Extension)
292 | 
293 | ---
294 | 
295 | **Want to contribute to any of these features?** Check out our [Contributing Guide](docs/CONTRIBUTING.md) and join our development community!
296 | 
297 | ## 📄 License
298 | 
299 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
300 | 
301 | ## 📚 More Documentation
302 | 
303 | - [Architecture Design](docs/ARCHITECTURE.md) - Detailed technical architecture documentation
304 | - [TOOLS API](docs/TOOLS.md) - Complete tool API documentation
305 | - [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issue solutions
306 | 
```

--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Contributing Guide 🤝
  2 | 
  3 | Thank you for your interest in contributing to Chrome MCP Server! This document provides guidelines and information for contributors.
  4 | 
  5 | ## 🎯 How to Contribute
  6 | 
  7 | We welcome contributions in many forms:
  8 | 
  9 | - 🐛 Bug reports and fixes
 10 | - ✨ New features and tools
 11 | - 📚 Documentation improvements
 12 | - 🧪 Tests and performance optimizations
 13 | - 🌐 Translations and internationalization
 14 | - 💡 Ideas and suggestions
 15 | 
 16 | ## 🚀 Getting Started
 17 | 
 18 | ### Prerequisites
 19 | 
 20 | - **Node.js 20+** and **pnpm or npm** (latest version)
 21 | - **Chrome/Chromium** browser for testing
 22 | - **Git** for version control
 23 | - **Rust** (for WASM development, optional)
 24 | - **TypeScript** knowledge
 25 | 
 26 | ### Development Setup
 27 | 
 28 | 1. **Fork and clone the repository**
 29 | 
 30 | ```bash
 31 | git clone https://github.com/YOUR_USERNAME/chrome-mcp-server.git
 32 | cd chrome-mcp-server
 33 | ```
 34 | 
 35 | 2. **Install dependencies**
 36 | 
 37 | ```bash
 38 | pnpm install
 39 | ```
 40 | 
 41 | 3. **Start the project**
 42 | 
 43 | ```bash
 44 | npm run dev
 45 | ```
 46 | 
 47 | 4. **Load the extension in Chrome**
 48 |    - Open `chrome://extensions/`
 49 |    - Enable "Developer mode"
 50 |    - Click "Load unpacked" and select `your/extension/dist`
 51 | 
 52 | ## 🏗️ Project Structure
 53 | 
 54 | ```
 55 | chrome-mcp-server/
 56 | ├── app/
 57 | │   ├── chrome-extension/     # Chrome extension (WXT + Vue 3)
 58 | │   │   ├── entrypoints/      # Background scripts, popup, content scripts
 59 | │   │   ├── utils/            # AI models, vector database, utilities
 60 | │   │   └── workers/          # Web Workers for AI processing
 61 | │   └── native-server/        # Native messaging server (Fastify + TypeScript)
 62 | │       ├── src/mcp/          # MCP protocol implementation
 63 | │       └── src/server/       # HTTP server and native messaging
 64 | ├── packages/
 65 | │   ├── shared/               # Shared types and utilities
 66 | │   └── wasm-simd/           # SIMD-optimized WebAssembly math functions
 67 | └── docs/                    # Documentation
 68 | ```
 69 | 
 70 | ## 🛠️ Development Workflow
 71 | 
 72 | ### Adding New Tools
 73 | 
 74 | 1. **Define the tool schema in `packages/shared/src/tools.ts`**:
 75 | 
 76 | ```typescript
 77 | {
 78 |   name: 'your_new_tool',
 79 |   description: 'Description of what your tool does',
 80 |   inputSchema: {
 81 |     type: 'object',
 82 |     properties: {
 83 |       // Define parameters
 84 |     },
 85 |     required: ['param1']
 86 |   }
 87 | }
 88 | ```
 89 | 
 90 | 2. **Implement the tool in `app/chrome-extension/entrypoints/background/tools/browser/`**:
 91 | 
 92 | ```typescript
 93 | class YourNewTool extends BaseBrowserToolExecutor {
 94 |   name = TOOL_NAMES.BROWSER.YOUR_NEW_TOOL;
 95 | 
 96 |   async execute(args: YourToolParams): Promise<ToolResult> {
 97 |     // Implementation
 98 |   }
 99 | }
100 | ```
101 | 
102 | 3. **Export the tool in `app/chrome-extension/entrypoints/background/tools/browser/index.ts`**
103 | 
104 | 4. **Add tests in the appropriate test directory**
105 | 
106 | ### Code Style Guidelines
107 | 
108 | - **TypeScript**: Use strict TypeScript with proper typing
109 | - **ESLint**: Follow the configured ESLint rules (`pnpm lint`)
110 | - **Prettier**: Format code with Prettier (`pnpm format`)
111 | - **Naming**: Use descriptive names and follow existing patterns
112 | - **Comments**: Add JSDoc comments for public APIs
113 | - **Error Handling**: Always handle errors gracefully
114 | 
115 | ## 📝 Pull Request Process
116 | 
117 | 1. **Create a feature branch**
118 | 
119 | ```bash
120 | git checkout -b feature/your-feature-name
121 | ```
122 | 
123 | 2. **Make your changes**
124 |    - Follow the code style guidelines
125 |    - Add tests for new functionality
126 |    - Update documentation if needed
127 | 
128 | 3. **Test your changes**
129 |    - Ensure all existing tests pass
130 |    - Test the Chrome extension manually
131 |    - Verify MCP protocol compatibility
132 | 
133 | 4. **Commit your changes**
134 | 
135 | ```bash
136 | git add .
137 | git commit -m "feat: add your feature description"
138 | ```
139 | 
140 | We use [Conventional Commits](https://www.conventionalcommits.org/):
141 | 
142 | - `feat:` for new features
143 | - `fix:` for bug fixes
144 | - `docs:` for documentation changes
145 | - `test:` for adding tests
146 | - `refactor:` for code refactoring
147 | 
148 | 5. **Push and create a Pull Request**
149 | 
150 | ```bash
151 | git push origin feature/your-feature-name
152 | ```
153 | 
154 | ## 🐛 Bug Reports
155 | 
156 | When reporting bugs, please include:
157 | 
158 | - **Environment**: OS, Chrome version, Node.js version
159 | - **Steps to reproduce**: Clear, step-by-step instructions
160 | - **Expected behavior**: What should happen
161 | - **Actual behavior**: What actually happens
162 | - **Screenshots/logs**: If applicable
163 | - **MCP client**: Which MCP client you're using (Claude Desktop, etc.)
164 | 
165 | ## 💡 Feature Requests
166 | 
167 | For feature requests, please provide:
168 | 
169 | - **Use case**: Why is this feature needed?
170 | - **Proposed solution**: How should it work?
171 | - **Alternatives**: Any alternative solutions considered?
172 | - **Additional context**: Screenshots, examples, etc.
173 | 
174 | ## 🔧 Development Tips
175 | 
176 | ### Using WASM SIMD
177 | 
178 | If you're contributing to the WASM SIMD package:
179 | 
180 | ```bash
181 | cd packages/wasm-simd
182 | # Install Rust and wasm-pack if not already installed
183 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
184 | cargo install wasm-pack
185 | 
186 | # Build WASM package
187 | pnpm build
188 | 
189 | # The built files will be copied to app/chrome-extension/workers/
190 | ```
191 | 
192 | ### Debugging Chrome Extension
193 | 
194 | - Use Chrome DevTools for debugging extension popup and background scripts
195 | - Check `chrome://extensions/` for extension errors
196 | - Use `console.log` statements for debugging
197 | - Monitor the native messaging connection in the background script
198 | 
199 | ### Testing MCP Protocol
200 | 
201 | - Use MCP Inspector for protocol debugging
202 | - Test with different MCP clients (Claude Desktop, custom clients)
203 | - Verify tool schemas and responses match MCP specifications
204 | 
205 | ## 📚 Resources
206 | 
207 | - [Model Context Protocol Specification](https://modelcontextprotocol.io/)
208 | - [Chrome Extension Development](https://developer.chrome.com/docs/extensions/)
209 | - [WXT Framework Documentation](https://wxt.dev/)
210 | - [TypeScript Handbook](https://www.typescriptlang.org/docs/)
211 | 
212 | ## 🤝 Community
213 | 
214 | - **GitHub Issues**: For bug reports and feature requests
215 | - **GitHub Discussions**: For questions and general discussion
216 | - **Pull Requests**: For code contributions
217 | 
218 | ## 📄 License
219 | 
220 | By contributing to Chrome MCP Server, you agree that your contributions will be licensed under the MIT License.
221 | 
222 | ## 🎯 Contributor Guidelines
223 | 
224 | ### New Contributors
225 | 
226 | If you're contributing to an open source project for the first time:
227 | 
228 | 1. **Start small**: Look for issues labeled "good first issue"
229 | 2. **Read the code**: Familiarize yourself with the project structure and coding style
230 | 3. **Ask questions**: Ask questions in GitHub Discussions
231 | 4. **Learn the tools**: Get familiar with Git, GitHub, TypeScript, and other tools
232 | 
233 | ### Experienced Contributors
234 | 
235 | - **Architecture improvements**: Propose system-level improvements
236 | - **Performance optimization**: Identify and fix performance bottlenecks
237 | - **New features**: Design and implement complex new features
238 | - **Mentor newcomers**: Help new contributors get started
239 | 
240 | ### Documentation Contributions
241 | 
242 | - **API documentation**: Improve tool documentation and examples
243 | - **Tutorials**: Create usage guides and best practices
244 | - **Translations**: Help translate documentation to other languages
245 | - **Video content**: Create demo videos and tutorials
246 | 
247 | ### Testing Contributions
248 | 
249 | - **Unit tests**: Write tests for new features
250 | - **Integration tests**: Test interactions between components
251 | - **Performance tests**: Benchmark testing and performance regression detection
252 | - **User testing**: Functional testing in real-world scenarios
253 | 
254 | ## 🏆 Contributor Recognition
255 | 
256 | We value every contribution, no matter how big or small. Contributors will be recognized in the following ways:
257 | 
258 | - **README acknowledgments**: Contributors listed in the project README
259 | - **Release notes**: Contributors thanked in version release notes
260 | - **Contributor badges**: Contributor badges on GitHub profiles
261 | - **Community recognition**: Special thanks in community discussions
262 | 
263 | Thank you for considering contributing to Chrome MCP Server! Your participation makes this project better.
264 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/node-spec-registry.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from 'chrome-mcp-shared';
2 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/node-spec.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from 'chrome-mcp-shared';
2 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/node-specs-builtin.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from 'chrome-mcp-shared';
2 | 
```

--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------

```yaml
1 | packages:
2 |   - 'app/*'
3 |   - 'packages/*'
```

--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "recommendations": ["Vue.volar"]
3 | }
4 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/tsconfig.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "extends": "./.wxt/tsconfig.json"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/mcp/stdio-config.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "url": "http://127.0.0.1:12306/mcp"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/commitlint.config.cjs:
--------------------------------------------------------------------------------

```
1 | module.exports = {
2 |   extends: ['@commitlint/config-conventional'],
3 | };
4 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/agent/db/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | /**
2 |  * Database module exports.
3 |  */
4 | export * from './schema';
5 | export * from './client';
6 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/content.ts:
--------------------------------------------------------------------------------

```typescript
1 | export default defineContentScript({
2 |   matches: ['*://*.google.com/*'],
3 |   main() {},
4 | });
5 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/options/main.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | 
4 | createApp(App).mount('#app');
5 | 
```

--------------------------------------------------------------------------------
/packages/shared/src/constants.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const DEFAULT_SERVER_PORT = 12306;
2 | export const HOST_NAME = 'com.chromemcp.nativehost';
3 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/scripts/register-dev.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { registerUserLevelHostWithNodePath } from './utils';
2 | 
3 | registerUserLevelHostWithNodePath();
4 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/server/routes/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | /**
2 |  * Routes module exports.
3 |  */
4 | export { registerAgentRoutes, type AgentRoutesOptions } from './agent';
5 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/workflows/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | export { default as WorkflowsView } from './WorkflowsView.vue';
2 | export { default as WorkflowListItem } from './WorkflowListItem.vue';
3 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/flow-runner.ts:
--------------------------------------------------------------------------------

```typescript
1 | // thin re-export for backward compatibility
2 | export { runFlow } from './engine/scheduler';
3 | export type { RunOptions } from './engine/scheduler';
4 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/offscreen/index.html:
--------------------------------------------------------------------------------

```html
1 | <!DOCTYPE html>
2 | <html lang="en">
3 |   <head>
4 |     <meta charset="UTF-8" />
5 |   </head>
6 |   <body>
7 |     <script type="module" src="./main.ts"></script>
8 |   </body>
9 | </html>
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/builder/main.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | 
4 | // Tailwind first, then custom tokens
5 | import '../styles/tailwind.css';
6 | 
7 | createApp(App).mount('#app');
8 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/welcome/main.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | 
4 | // Tailwind first, then custom tokens
5 | import '../styles/tailwind.css';
6 | 
7 | createApp(App).mount('#app');
8 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/shared/quick-panel/providers/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Quick Panel Search Providers
 3 |  *
 4 |  * Exports all search providers for Quick Panel.
 5 |  */
 6 | 
 7 | export {
 8 |   createTabsProvider,
 9 |   type TabsProviderOptions,
10 |   type TabsSearchResultData,
11 | } from './tabs-provider';
12 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/scripts/constant.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const COMMAND_NAME = 'mcp-chrome-bridge';
2 | export const EXTENSION_ID = 'hbdgbgagpkpjffpklnamcljpakneikee';
3 | export const HOST_NAME = 'com.chromemcp.nativehost';
4 | export const DESCRIPTION = 'Node.js Host for Browser Bridge Extension';
5 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/web-editor-v2/ui/property-panel/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | /**
2 |  * Property Panel Module
3 |  *
4 |  * Exports the property panel component and its types.
5 |  */
6 | 
7 | export { createPropertyPanel } from './property-panel';
8 | export type { PropertyPanel, PropertyPanelOptions, PropertyPanelTab, DesignControl } from './types';
9 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay-v3/storage/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Storage 层导出入口
 3 |  */
 4 | 
 5 | export * from './db';
 6 | export * from './flows';
 7 | export * from './runs';
 8 | export * from './events';
 9 | export * from './queue';
10 | export * from './persistent-vars';
11 | export * from './triggers';
12 | export * from './import';
13 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay-v3/engine/kernel/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Kernel 模块导出入口
 3 |  */
 4 | 
 5 | export * from './kernel';
 6 | export * from './runner';
 7 | export * from './traversal';
 8 | export * from './breakpoints';
 9 | export * from './artifacts';
10 | export * from './debug-controller';
11 | export * from './recovery-kernel';
12 | 
```

--------------------------------------------------------------------------------
/packages/shared/src/labels.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // labels.ts — centralized labels for edges and other enums
 2 | 
 3 | export const EDGE_LABELS = {
 4 |   DEFAULT: 'default',
 5 |   TRUE: 'true',
 6 |   FALSE: 'false',
 7 |   ON_ERROR: 'onError',
 8 | } as const;
 9 | 
10 | export type EdgeLabel = (typeof EDGE_LABELS)[keyof typeof EDGE_LABELS];
11 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/common/step-types.ts:
--------------------------------------------------------------------------------

```typescript
1 | // step-types.ts — re-export shared constants to keep single source of truth
2 | export { STEP_TYPES } from 'chrome-mcp-shared';
3 | export type StepTypeConst =
4 |   (typeof import('chrome-mcp-shared'))['STEP_TYPES'][keyof (typeof import('chrome-mcp-shared'))['STEP_TYPES']];
5 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/options/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html>
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>Userscripts Manager</title>
 7 |   </head>
 8 |   <body>
 9 |     <div id="app"></div>
10 |     <script type="module" src="./main.ts"></script>
11 |   </body>
12 | </html>
13 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/env.d.ts:
--------------------------------------------------------------------------------

```typescript
1 | /// <reference types="unplugin-icons/types/vue" />
2 | declare module '*.vue' {
3 |   import type { DefineComponent } from 'vue';
4 |   type Props = Record<string, never>;
5 |   type RawBindings = Record<string, never>;
6 |   const component: DefineComponent<Props, RawBindings, any>;
7 |   export default component;
8 | }
9 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/composables/useRRV3Rpc.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Re-export shared useRRV3Rpc composable
 3 |  * @description This file re-exports the shared composable for backward compatibility
 4 |  */
 5 | 
 6 | export {
 7 |   useRRV3Rpc,
 8 |   type UseRRV3Rpc,
 9 |   type UseRRV3RpcOptions,
10 |   type RpcRequestOptions,
11 | } from '@/entrypoints/shared/composables/useRRV3Rpc';
12 | 
```

--------------------------------------------------------------------------------
/packages/shared/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export * from './constants';
 2 | export * from './types';
 3 | export * from './tools';
 4 | export * from './rr-graph';
 5 | export * from './step-types';
 6 | export * from './labels';
 7 | export * from './node-spec';
 8 | export * from './node-spec-registry';
 9 | export * from './node-specs-builtin';
10 | export * from './agent-types';
11 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/shared/utils/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Shared Utilities Index
 3 |  * @description Utility functions shared between UI entrypoints
 4 |  */
 5 | 
 6 | // Flow conversion utilities
 7 | export {
 8 |   flowV2ToV3ForRpc,
 9 |   flowV3ToV2ForBuilder,
10 |   isFlowV3,
11 |   isFlowV2,
12 |   extractFlowCandidates,
13 |   type FlowConversionResult,
14 | } from './rr-flow-convert';
15 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay-v3/engine/triggers/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Triggers 模块导出入口
 3 |  */
 4 | 
 5 | export * from './trigger-handler';
 6 | export * from './trigger-manager';
 7 | export * from './url-trigger';
 8 | export * from './command-trigger';
 9 | export * from './context-menu-trigger';
10 | export * from './dom-trigger';
11 | export * from './cron-trigger';
12 | export * from './manual-trigger';
13 | 
```

--------------------------------------------------------------------------------
/packages/wasm-simd/Cargo.toml:
--------------------------------------------------------------------------------

```toml
 1 | [package]
 2 | name = "simd-math"
 3 | version = "0.1.0"
 4 | edition = "2021"
 5 | 
 6 | [lib]
 7 | crate-type = ["cdylib"]
 8 | 
 9 | [dependencies]
10 | wasm-bindgen = "0.2"
11 | wide = "0.7"
12 | console_error_panic_hook = "0.1"
13 | 
14 | [dependencies.web-sys]
15 | version = "0.3"
16 | features = [
17 |   "console",
18 | ]
19 | 
20 | [profile.release]
21 | opt-level = 3
22 | lto = true
23 | codegen-units = 1
24 | panic = "abort"
25 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/shims/devtools.d.ts:
--------------------------------------------------------------------------------

```typescript
1 | // Single-file shim for all deep imports from chrome-devtools-frontend.
2 | // This prevents TypeScript from type-checking the upstream DevTools source tree.
3 | // Runtime still loads the real package; this file is types-only and local to TS.
4 | declare module 'chrome-devtools-frontend/*' {
5 |   const anyExport: any;
6 |   export = anyExport;
7 | }
8 | 
```

--------------------------------------------------------------------------------
/packages/shared/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "compilerOptions": {
 3 |       "target": "ES2020",
 4 |       "module": "NodeNext",
 5 |       "moduleResolution": "NodeNext",
 6 |       "esModuleInterop": true,
 7 |       "declaration": true,
 8 |       "outDir": "./dist",
 9 |       "strict": true,
10 |       "skipLibCheck": true
11 |     },
12 |     "include": ["src/**/*"],
13 |     "exclude": ["node_modules", "dist"]
14 |   }
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/StopIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" :class="className">
 3 |     <rect x="6" y="6" width="12" height="12" rx="1" fill="currentColor" />
 4 |   </svg>
 5 | </template>
 6 | 
 7 | <script lang="ts" setup>
 8 | interface Props {
 9 |   className?: string;
10 | }
11 | 
12 | withDefaults(defineProps<Props>(), {
13 |   className: 'icon-default',
14 | });
15 | </script>
16 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/variables.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // variables.ts — Shared variable suggestion types for builder UI
 2 | export type VariableOrigin = 'global' | 'node';
 3 | 
 4 | export interface VariableOption {
 5 |   key: string;
 6 |   origin: VariableOrigin;
 7 |   nodeId?: string;
 8 |   nodeName?: string;
 9 | }
10 | 
11 | export const VAR_TOKEN_OPEN = '{';
12 | export const VAR_TOKEN_CLOSE = '}';
13 | export const VAR_PLACEHOLDER = '{}';
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html lang="zh-CN">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>工作流管理</title>
 7 |     <meta name="manifest.type" content="side_panel" />
 8 |   </head>
 9 |   <body>
10 |     <div id="app"></div>
11 |     <script type="module" src="./main.ts"></script>
12 |   </body>
13 | </html>
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/builder/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html lang="zh-CN">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>工作流编辑器</title>
 7 |     <meta name="manifest.type" content="unlisted_page" />
 8 |   </head>
 9 |   <body>
10 |     <div id="app"></div>
11 |     <script type="module" src="./main.ts"></script>
12 |   </body>
13 | </html>
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/shared/element-picker/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Element Picker (UI)
 3 |  *
 4 |  * A Quick Panel-styled floating panel used by chrome_request_element_selection.
 5 |  */
 6 | 
 7 | export { createElementPickerController } from './controller';
 8 | export type {
 9 |   ElementPickerController,
10 |   ElementPickerControllerOptions,
11 |   ElementPickerUiState,
12 |   ElementPickerUiRequest,
13 |   ElementPickerUiPatch,
14 | } from './controller';
15 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html lang="en">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>Default Popup Title</title>
 7 |     <meta name="manifest.type" content="browser_action" />
 8 |   </head>
 9 |   <body>
10 |     <div id="app"></div>
11 |     <script type="module" src="./main.ts"></script>
12 |   </body>
13 | </html>
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay-v3/engine/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Engine 层导出入口
 3 |  */
 4 | 
 5 | // Kernel
 6 | export * from './kernel';
 7 | 
 8 | // Queue
 9 | export * from './queue';
10 | 
11 | // Plugins
12 | export * from './plugins';
13 | 
14 | // Transport
15 | export * from './transport';
16 | 
17 | // Keepalive
18 | export * from './keepalive';
19 | 
20 | // Recovery
21 | export * from './recovery';
22 | 
23 | // Triggers
24 | export * from './triggers';
25 | 
26 | // Storage Port
27 | export * from './storage';
28 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyClick.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div>
 3 |     <SelectorEditor :node="node" :allowPick="true" />
 4 |   </div>
 5 | </template>
 6 | 
 7 | <script lang="ts" setup>
 8 | /* eslint-disable vue/no-mutating-props */
 9 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
10 | import SelectorEditor from './SelectorEditor.vue';
11 | 
12 | defineProps<{ node: NodeBase }>();
13 | </script>
14 | 
15 | <style scoped></style>
16 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/welcome/index.html:
--------------------------------------------------------------------------------

```html
 1 | <!doctype html>
 2 | <html lang="en">
 3 |   <head>
 4 |     <meta charset="UTF-8" />
 5 |     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |     <title>Welcome to Chrome MCP Server</title>
 7 |     <meta name="manifest.type" content="unlisted_page" />
 8 |   </head>
 9 |   <body>
10 |     <div id="app"></div>
11 |     <script type="module" src="./main.ts"></script>
12 |   </body>
13 | </html>
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/RecordIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" :class="className">
 3 |     <circle cx="12" cy="12" r="8" :fill="recording ? '#ef4444' : 'currentColor'" />
 4 |   </svg>
 5 | </template>
 6 | 
 7 | <script lang="ts" setup>
 8 | interface Props {
 9 |   className?: string;
10 |   recording?: boolean;
11 | }
12 | 
13 | withDefaults(defineProps<Props>(), {
14 |   className: 'icon-default',
15 |   recording: false,
16 | });
17 | </script>
18 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/types/icons.d.ts:
--------------------------------------------------------------------------------

```typescript
1 | // Type shim for unplugin-icons virtual modules used as Vue components
2 | // Keeps TS happy in IDE and during type-check without generating code.
3 | declare module '~icons/*' {
4 |   import type { DefineComponent } from 'vue';
5 |   // Use explicit, non-empty object types to satisfy eslint rule
6 |   const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>;
7 |   export default component;
8 | }
9 | 
```

--------------------------------------------------------------------------------
/app/native-server/jest.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | module.exports = {
 2 |   preset: 'ts-jest',
 3 |   testEnvironment: 'node',
 4 |   roots: ['<rootDir>/src'],
 5 |   testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
 6 |   collectCoverage: true,
 7 |   collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/scripts/**/*'],
 8 |   coverageDirectory: 'coverage',
 9 |   coverageThreshold: {
10 |     global: {
11 |       branches: 70,
12 |       functions: 80,
13 |       lines: 80,
14 |       statements: 80,
15 |     },
16 |   },
17 | };
18 | 
```

--------------------------------------------------------------------------------
/packages/shared/src/node-spec-registry.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // node-spec-registry.ts — runtime registry for NodeSpec (shared between UI/runtime)
 2 | import type { NodeSpec } from './node-spec';
 3 | 
 4 | const REG = new Map<string, NodeSpec>();
 5 | 
 6 | export function registerNodeSpec(spec: NodeSpec) {
 7 |   REG.set(spec.type, spec);
 8 | }
 9 | 
10 | export function getNodeSpec(type: string): NodeSpec | undefined {
11 |   return REG.get(type);
12 | }
13 | 
14 | export function listNodeSpecs(): NodeSpec[] {
15 |   return Array.from(REG.values());
16 | }
17 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/common/node-types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // node-types.ts — centralized node type constants for Builder/UI layer
 2 | // Combines all executable Step types with UI-only nodes (e.g., trigger, delay)
 3 | 
 4 | import { STEP_TYPES } from './step-types';
 5 | 
 6 | export const NODE_TYPES = {
 7 |   // Executable step types (spread from STEP_TYPES)
 8 |   ...STEP_TYPES,
 9 |   // UI-only nodes
10 |   TRIGGER: 'trigger',
11 |   DELAY: 'delay',
12 | } as const;
13 | 
14 | export type NodeTypeConst = (typeof NODE_TYPES)[keyof typeof NODE_TYPES];
15 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/mcp/mcp-server.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
 2 | import { setupTools } from './register-tools';
 3 | 
 4 | export let mcpServer: Server | null = null;
 5 | 
 6 | export const getMcpServer = () => {
 7 |   if (mcpServer) {
 8 |     return mcpServer;
 9 |   }
10 |   mcpServer = new Server(
11 |     {
12 |       name: 'ChromeMcpServer',
13 |       version: '1.0.0',
14 |     },
15 |     {
16 |       capabilities: {
17 |         tools: {},
18 |       },
19 |     },
20 |   );
21 | 
22 |   setupTools(mcpServer);
23 |   return mcpServer;
24 | };
25 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/tests/__mocks__/hnswlib-wasm-static.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Mock for hnswlib-wasm-static
 3 |  * @description Provides a stub for vector database in test environment
 4 |  */
 5 | 
 6 | export const HierarchicalNSW = class MockHierarchicalNSW {
 7 |   constructor() {}
 8 |   initIndex() {}
 9 |   addPoint() {}
10 |   searchKnn() {
11 |     return { neighbors: [], distances: [] };
12 |   }
13 |   getCurrentCount() {
14 |     return 0;
15 |   }
16 |   resizeIndex() {}
17 |   getPoint() {
18 |     return [];
19 |   }
20 |   markDelete() {}
21 | };
22 | 
23 | export default { HierarchicalNSW };
24 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/shared/composables/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Shared UI Composables
 3 |  * @description Composables shared between multiple UI entrypoints (Sidepanel, Builder, Popup, etc.)
 4 |  *
 5 |  * Note: These composables are for UI-only use. Do not import them in background scripts
 6 |  * as they depend on Vue and will bloat the service worker bundle.
 7 |  */
 8 | 
 9 | // RR V3 RPC Client
10 | export { useRRV3Rpc } from './useRRV3Rpc';
11 | export type { UseRRV3Rpc, UseRRV3RpcOptions, RpcRequestOptions } from './useRRV3Rpc';
12 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyDelay.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">延迟 (ms)</label>
 5 |       <input class="form-input" type="number" v-model.number="(node as any).config.ms" min="0" />
 6 |     </div>
 7 |   </div>
 8 | </template>
 9 | 
10 | <script lang="ts" setup>
11 | /* eslint-disable vue/no-mutating-props */
12 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
13 | defineProps<{ node: NodeBase }>();
14 | </script>
15 | 
16 | <style scoped></style>
17 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/BoltIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="1.5"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/assets/vue.svg:
--------------------------------------------------------------------------------

```
1 | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
2 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyKey.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">按键序列</label>
 5 |       <input
 6 |         class="form-input"
 7 |         v-model="(node as any).config.keys"
 8 |         placeholder="如 Backspace Enter 或 cmd+a"
 9 |       />
10 |     </div>
11 |   </div>
12 | </template>
13 | 
14 | <script lang="ts" setup>
15 | /* eslint-disable vue/no-mutating-props */
16 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
17 | defineProps<{ node: NodeBase }>();
18 | </script>
19 | 
20 | <style scoped></style>
21 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyNavigate.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group" data-field="navigate.url">
 4 |       <label class="form-label">URL 地址</label>
 5 |       <input
 6 |         class="form-input"
 7 |         v-model="(node as any).config.url"
 8 |         placeholder="https://example.com"
 9 |       />
10 |     </div>
11 |   </div>
12 | </template>
13 | 
14 | <script lang="ts" setup>
15 | /* eslint-disable vue/no-mutating-props */
16 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
17 | defineProps<{ node: NodeBase }>();
18 | </script>
19 | 
20 | <style scoped></style>
21 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/toast.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // toast.ts - lightweight toast event bus for builder UI
 2 | // Usage: import { toast } and call toast('message', 'warn'|'error'|'info')
 3 | 
 4 | export type ToastLevel = 'info' | 'warn' | 'error';
 5 | 
 6 | export function toast(message: string, level: ToastLevel = 'warn') {
 7 |   try {
 8 |     const ev = new CustomEvent('rr_toast', { detail: { message: String(message), level } });
 9 |     window.dispatchEvent(ev);
10 |   } catch {
11 |     // as a last resort
12 |     console[level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log']('[toast]', message);
13 |   }
14 | }
15 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/CheckIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     viewBox="0 0 20 20"
 5 |     fill="currentColor"
 6 |     :class="className"
 7 |   >
 8 |     <path
 9 |       fill-rule="evenodd"
10 |       d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.052-.143Z"
11 |       clip-rule="evenodd"
12 |     />
13 |   </svg>
14 | </template>
15 | 
16 | <script lang="ts" setup>
17 | interface Props {
18 |   className?: string;
19 | }
20 | 
21 | withDefaults(defineProps<Props>(), {
22 |   className: 'icon-small',
23 | });
24 | </script>
25 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/common/tool-handler.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { CallToolResult, TextContent, ImageContent } from '@modelcontextprotocol/sdk/types.js';
 2 | 
 3 | export interface ToolResult extends CallToolResult {
 4 |   content: (TextContent | ImageContent)[];
 5 |   isError: boolean;
 6 | }
 7 | 
 8 | export interface ToolExecutor {
 9 |   execute(args: any): Promise<ToolResult>;
10 | }
11 | 
12 | export const createErrorResponse = (
13 |   message: string = 'Unknown error, please try again',
14 | ): ToolResult => {
15 |   return {
16 |     content: [
17 |       {
18 |         type: 'text',
19 |         text: message,
20 |       },
21 |     ],
22 |     isError: true,
23 |   };
24 | };
25 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/tailwind.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { Config } from 'tailwindcss';
 2 | 
 3 | // Tailwind v4 config (TypeScript). The Vite plugin `@tailwindcss/vite`
 4 | // will auto-detect and load this file. No `content` field is required in v4.
 5 | export default {
 6 |   theme: {
 7 |     extend: {
 8 |       colors: {
 9 |         brand: {
10 |           DEFAULT: '#7C3AED',
11 |           dark: '#5B21B6',
12 |           light: '#A78BFA',
13 |         },
14 |       },
15 |       boxShadow: {
16 |         card: '0 6px 20px rgba(0,0,0,0.08)',
17 |       },
18 |       borderRadius: {
19 |         xl: '12px',
20 |       },
21 |     },
22 |   },
23 |   plugins: [],
24 | } satisfies Config;
25 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/EditIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent-chat/AgentTimeline.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="pl-1 space-y-3">
 3 |     <!-- Timeline container -->
 4 |     <div class="relative pl-5 space-y-4 ml-1">
 5 |       <AgentTimelineItem
 6 |         v-for="(item, index) in items"
 7 |         :key="item.id"
 8 |         :item="item"
 9 |         :is-last="index === items.length - 1"
10 |       />
11 |     </div>
12 |   </div>
13 | </template>
14 | 
15 | <script lang="ts" setup>
16 | import type { TimelineItem, AgentThreadState } from '../../composables/useAgentThreads';
17 | import AgentTimelineItem from './AgentTimelineItem.vue';
18 | 
19 | defineProps<{
20 |   items: TimelineItem[];
21 |   state: AgentThreadState;
22 | }>();
23 | </script>
24 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/RefreshIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/agent/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Re-export agent types from shared package for backward compatibility.
 3 |  * All types are now defined in packages/shared/src/agent-types.ts to ensure
 4 |  * consistency between native-server and chrome-extension.
 5 |  */
 6 | export {
 7 |   type AgentRole,
 8 |   type AgentMessage,
 9 |   type StreamTransport,
10 |   type AgentStatusEvent,
11 |   type AgentConnectedEvent,
12 |   type AgentHeartbeatEvent,
13 |   type RealtimeEvent,
14 |   type AgentAttachment,
15 |   type AgentCliPreference,
16 |   type AgentActRequest,
17 |   type AgentActResponse,
18 |   type AgentProject,
19 |   type AgentEngineInfo,
20 |   type AgentStoredMessage,
21 | } from 'chrome-mcp-shared';
22 | 
```

--------------------------------------------------------------------------------
/app/native-server/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2018",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "lib": ["ES2018", "DOM"],
 7 |     "outDir": "dist",
 8 |     "rootDir": "src",
 9 |     "strict": true,
10 |     "esModuleInterop": true,
11 |     "forceConsistentCasingInFileNames": true,
12 |     "skipLibCheck": true,
13 |     "declaration": true,
14 |     "sourceMap": true,
15 |     "resolveJsonModule": true,
16 |     "baseUrl": ".",
17 |     "paths": {
18 |       "chrome-devtools-frontend/*": ["src/shims/devtools.d.ts"]
19 |     }
20 |   },
21 |   "include": ["src/**/*"],
22 |   "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
23 | }
24 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent/MessageList.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="flex-1 overflow-y-auto px-4 py-3 space-y-3 bg-slate-50">
 3 |     <div v-if="messages.length === 0" class="text-xs text-slate-500 text-center mt-4">
 4 |       Start a conversation with your local agent. Messages stream from the native server via SSE and
 5 |       are persisted per project.
 6 |     </div>
 7 | 
 8 |     <MessageItem v-for="message in messages" :key="message.id" :message="message" />
 9 |   </div>
10 | </template>
11 | 
12 | <script lang="ts" setup>
13 | import type { AgentMessage } from 'chrome-mcp-shared';
14 | import MessageItem from './MessageItem.vue';
15 | 
16 | defineProps<{
17 |   messages: AgentMessage[];
18 | }>();
19 | </script>
20 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Agent Chat Components
 3 |  * Export all sub-components for the agent chat feature.
 4 |  */
 5 | export { default as ConnectionStatus } from './ConnectionStatus.vue';
 6 | export { default as ProjectSelector } from './ProjectSelector.vue';
 7 | export { default as ProjectCreateForm } from './ProjectCreateForm.vue';
 8 | export { default as CliSettings } from './CliSettings.vue';
 9 | export { default as MessageList } from './MessageList.vue';
10 | export { default as MessageItem } from './MessageItem.vue';
11 | export { default as ChatInput } from './ChatInput.vue';
12 | export { default as AttachmentPreview } from './AttachmentPreview.vue';
13 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/DatabaseIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/engine/plugins/breakpoint.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { RunPlugin, StepContext } from './types';
 2 | import { runState } from '../state-manager';
 3 | 
 4 | export function breakpointPlugin(): RunPlugin {
 5 |   return {
 6 |     name: 'breakpoint',
 7 |     async onBeforeStep(ctx: StepContext) {
 8 |       try {
 9 |         const step: any = ctx.step as any;
10 |         const hasBreakpoint = step?.$breakpoint === true || step?.breakpoint === true;
11 |         if (!hasBreakpoint) return;
12 |         // mark run paused for external UI to resume
13 |         await runState.update(ctx.runId, { status: 'stopped', updatedAt: Date.now() } as any);
14 |         return { pause: true };
15 |       } catch {}
16 |       return;
17 |     },
18 |   };
19 | }
20 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/VectorIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M9 4.5a4.5 4.5 0 0 1 6 0M9 4.5V3a1.5 1.5 0 0 1 1.5-1.5h3A1.5 1.5 0 0 1 15 3v1.5M9 4.5a4.5 4.5 0 0 0-4.5 4.5v7.5A1.5 1.5 0 0 0 6 18h12a1.5 1.5 0 0 0 1.5-1.5V9a4.5 4.5 0 0 0-4.5-4.5M12 12l2.25 2.25M12 12l-2.25-2.25M12 12v6"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/nodes/navigate.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { TOOL_NAMES } from 'chrome-mcp-shared';
 2 | import { handleCallTool } from '@/entrypoints/background/tools';
 3 | import type { Step } from '../types';
 4 | import type { ExecCtx, ExecResult, NodeRuntime } from './types';
 5 | 
 6 | export const navigateNode: NodeRuntime<any> = {
 7 |   validate: (step) => {
 8 |     const ok = !!(step as any).url;
 9 |     return ok ? { ok } : { ok, errors: ['缺少 URL'] };
10 |   },
11 |   run: async (_ctx: ExecCtx, step: Step) => {
12 |     const url = (step as any).url;
13 |     const res = await handleCallTool({ name: TOOL_NAMES.BROWSER.NAVIGATE, args: { url } });
14 |     if ((res as any).isError) throw new Error('navigate failed');
15 |     return {} as ExecResult;
16 |   },
17 | };
18 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/TabIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-16.5 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 0 0 2.25-2.25V6.75a2.25 2.25 0 0 0-2.25-2.25H6.75A2.25 2.25 0 0 0 4.5 6.75v10.5a2.25 2.25 0 0 0 2.25 2.25Z"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyOpenTab.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">URL 地址(可选)</label>
 5 |       <input
 6 |         class="form-input"
 7 |         v-model="(node as any).config.url"
 8 |         placeholder="https://example.com"
 9 |       />
10 |     </div>
11 |     <div class="form-group checkbox-group">
12 |       <label class="checkbox-label"
13 |         ><input type="checkbox" v-model="(node as any).config.newWindow" /> 新窗口</label
14 |       >
15 |     </div>
16 |   </div>
17 | </template>
18 | 
19 | <script lang="ts" setup>
20 | /* eslint-disable vue/no-mutating-props */
21 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
22 | defineProps<{ node: NodeBase }>();
23 | </script>
24 | 
25 | <style scoped></style>
26 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent/AttachmentPreview.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="flex flex-wrap gap-2 mb-2">
 3 |     <div
 4 |       v-for="(att, idx) in attachments"
 5 |       :key="idx"
 6 |       class="flex items-center gap-1 bg-slate-100 px-2 py-1 rounded text-xs max-w-[150px]"
 7 |     >
 8 |       <span class="truncate" :title="att.name">{{ att.name }}</span>
 9 |       <button
10 |         type="button"
11 |         class="text-red-500 hover:text-red-700 flex-shrink-0"
12 |         @click="$emit('remove', idx)"
13 |       >
14 |         &times;
15 |       </button>
16 |     </div>
17 |   </div>
18 | </template>
19 | 
20 | <script lang="ts" setup>
21 | import type { AgentAttachment } from 'chrome-mcp-shared';
22 | 
23 | defineProps<{
24 |   attachments: AgentAttachment[];
25 | }>();
26 | 
27 | defineEmits<{
28 |   remove: [index: number];
29 | }>();
30 | </script>
31 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/DocumentIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/MarkerIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="2"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z"
14 |     />
15 |     <path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" />
16 |   </svg>
17 | </template>
18 | 
19 | <script lang="ts" setup>
20 | interface Props {
21 |   className?: string;
22 | }
23 | 
24 | withDefaults(defineProps<Props>(), {
25 |   className: 'icon-default',
26 | });
27 | </script>
28 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/widgets/FieldKeySequence.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="keys">
 3 |     <input class="form-input" :placeholder="placeholder" :value="text" @input="onInput" />
 4 |     <div class="help">示例:Backspace Enter 或 cmd+a</div>
 5 |   </div>
 6 | </template>
 7 | 
 8 | <script lang="ts" setup>
 9 | import { ref, watchEffect } from 'vue';
10 | const props = defineProps<{ modelValue?: string; field?: any }>();
11 | const emit = defineEmits<{ (e: 'update:modelValue', v?: string): void }>();
12 | const text = ref<string>(props.modelValue ?? '');
13 | const placeholder = props.field?.placeholder || 'Backspace Enter 或 cmd+a';
14 | function onInput(ev: any) {
15 |   const v = String(ev?.target?.value ?? '');
16 |   text.value = v;
17 |   emit('update:modelValue', v);
18 | }
19 | watchEffect(() => (text.value = props.modelValue ?? ''));
20 | </script>
21 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent-chat/AgentConversation.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="px-5 py-6 space-y-8">
 3 |     <!-- Empty State -->
 4 |     <div v-if="threads.length === 0" class="py-10 text-center">
 5 |       <p
 6 |         class="text-2xl italic opacity-40"
 7 |         :style="{
 8 |           fontFamily: 'var(--ac-font-heading)',
 9 |           color: 'var(--ac-text-subtle)',
10 |         }"
11 |       >
12 |         How can I help you code today?
13 |       </p>
14 |     </div>
15 | 
16 |     <!-- Request Threads -->
17 |     <AgentRequestThread v-for="thread in threads" :key="thread.id" :thread="thread" />
18 |   </div>
19 | </template>
20 | 
21 | <script lang="ts" setup>
22 | import type { AgentThread } from '../../composables/useAgentThreads';
23 | import AgentRequestThread from './AgentRequestThread.vue';
24 | 
25 | defineProps<{
26 |   threads: AgentThread[];
27 | }>();
28 | </script>
29 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/actions/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Action System - 导出模块
 3 |  */
 4 | 
 5 | // 类型导出
 6 | export * from './types';
 7 | 
 8 | // 注册表导出
 9 | export {
10 |   ActionRegistry,
11 |   createActionRegistry,
12 |   ok,
13 |   invalid,
14 |   failed,
15 |   tryResolveString,
16 |   tryResolveNumber,
17 |   tryResolveJson,
18 |   tryResolveValue,
19 |   type BeforeExecuteArgs,
20 |   type BeforeExecuteHook,
21 |   type AfterExecuteArgs,
22 |   type AfterExecuteHook,
23 |   type ActionRegistryHooks,
24 | } from './registry';
25 | 
26 | // 适配器导出
27 | export {
28 |   execCtxToActionCtx,
29 |   stepToAction,
30 |   actionResultToExecResult,
31 |   createStepExecutor,
32 |   isActionSupported,
33 |   getActionType,
34 |   type StepExecutionAttempt,
35 | } from './adapter';
36 | 
37 | // Handler 工厂导出
38 | export {
39 |   createReplayActionRegistry,
40 |   registerReplayHandlers,
41 |   getSupportedActionTypes,
42 |   isActionTypeSupported,
43 | } from './handlers';
44 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export { default as DocumentIcon } from './DocumentIcon.vue';
 2 | export { default as DatabaseIcon } from './DatabaseIcon.vue';
 3 | export { default as BoltIcon } from './BoltIcon.vue';
 4 | export { default as TrashIcon } from './TrashIcon.vue';
 5 | export { default as CheckIcon } from './CheckIcon.vue';
 6 | export { default as TabIcon } from './TabIcon.vue';
 7 | export { default as VectorIcon } from './VectorIcon.vue';
 8 | export { default as RecordIcon } from './RecordIcon.vue';
 9 | export { default as StopIcon } from './StopIcon.vue';
10 | export { default as WorkflowIcon } from './WorkflowIcon.vue';
11 | export { default as RefreshIcon } from './RefreshIcon.vue';
12 | export { default as EditIcon } from './EditIcon.vue';
13 | export { default as MarkerIcon } from './MarkerIcon.vue';
14 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent-chat/timeline/markstream-thinking.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Markstream-vue custom component registration for <thinking> tag.
 3 |  *
 4 |  * This module registers a custom renderer for <thinking> tags in markdown content.
 5 |  * When markstream-vue encounters <thinking>...</thinking>, it will use ThinkingNode.vue
 6 |  * to render a collapsible thinking section instead of raw HTML.
 7 |  *
 8 |  * Usage:
 9 |  * 1. Import this module once (side-effect import) to register the component
10 |  * 2. Add `custom-id="agentchat"` and `:custom-html-tags="['thinking']"` to MarkdownRender
11 |  */
12 | import { setCustomComponents } from 'markstream-vue';
13 | import ThinkingNode from './ThinkingNode.vue';
14 | 
15 | /** Scope ID for AgentChat markdown rendering */
16 | export const AGENTCHAT_MD_SCOPE = 'agentchat';
17 | 
18 | // Register the thinking node component
19 | setCustomComponents(AGENTCHAT_MD_SCOPE, {
20 |   thinking: ThinkingNode,
21 | });
22 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/agent/project-types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Re-export AgentProject from shared package and define local input types.
 3 |  */
 4 | import type { AgentCliPreference, AgentProject } from 'chrome-mcp-shared';
 5 | 
 6 | // Re-export for backward compatibility
 7 | export type { AgentProject };
 8 | 
 9 | export interface CreateOrUpdateProjectInput {
10 |   id?: string;
11 |   name: string;
12 |   description?: string;
13 |   rootPath: string;
14 |   preferredCli?: AgentCliPreference;
15 |   selectedModel?: string;
16 |   /**
17 |    * Whether to use Claude Code Router (CCR) for this project.
18 |    */
19 |   useCcr?: boolean;
20 |   /**
21 |    * Whether to enable the local Chrome MCP server integration for this project.
22 |    * Defaults to true when omitted.
23 |    */
24 |   enableChromeMcp?: boolean;
25 |   /**
26 |    * If true, create the directory if it doesn't exist.
27 |    * Should only be set after user confirmation.
28 |    */
29 |   allowCreate?: boolean;
30 | }
31 | 
```

--------------------------------------------------------------------------------
/packages/shared/src/step-types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // step-types.ts — centralized step type constants for UI + runtime
 2 | 
 3 | export const STEP_TYPES = {
 4 |   CLICK: 'click',
 5 |   DBLCLICK: 'dblclick',
 6 |   FILL: 'fill',
 7 |   TRIGGER_EVENT: 'triggerEvent',
 8 |   SET_ATTRIBUTE: 'setAttribute',
 9 |   SCREENSHOT: 'screenshot',
10 |   SWITCH_FRAME: 'switchFrame',
11 |   LOOP_ELEMENTS: 'loopElements',
12 |   KEY: 'key',
13 |   SCROLL: 'scroll',
14 |   DRAG: 'drag',
15 |   WAIT: 'wait',
16 |   ASSERT: 'assert',
17 |   SCRIPT: 'script',
18 |   IF: 'if',
19 |   FOREACH: 'foreach',
20 |   WHILE: 'while',
21 |   NAVIGATE: 'navigate',
22 |   HTTP: 'http',
23 |   EXTRACT: 'extract',
24 |   OPEN_TAB: 'openTab',
25 |   SWITCH_TAB: 'switchTab',
26 |   CLOSE_TAB: 'closeTab',
27 |   HANDLE_DOWNLOAD: 'handleDownload',
28 |   EXECUTE_FLOW: 'executeFlow',
29 |   // UI-only helpers
30 |   TRIGGER: 'trigger',
31 |   DELAY: 'delay',
32 | } as const;
33 | 
34 | export type StepTypeConst = (typeof STEP_TYPES)[keyof typeof STEP_TYPES];
35 | 
```

--------------------------------------------------------------------------------
/packages/wasm-simd/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@chrome-mcp/wasm-simd",
 3 |   "version": "0.1.0",
 4 |   "description": "SIMD-optimized WebAssembly math functions for Chrome MCP",
 5 |   "main": "pkg/simd_math.js",
 6 |   "types": "pkg/simd_math.d.ts",
 7 |   "files": [
 8 |     "pkg/"
 9 |   ],
10 |   "scripts": {
11 |     "build": "wasm-pack build --target web --out-dir pkg --release",
12 |     "build:dev": "wasm-pack build --target web --out-dir pkg --dev",
13 |     "clean": "rimraf pkg/",
14 |     "test": "wasm-pack test --headless --firefox"
15 |   },
16 |   "keywords": [
17 |     "wasm",
18 |     "simd",
19 |     "webassembly",
20 |     "math",
21 |     "cosine-similarity",
22 |     "vector-operations"
23 |   ],
24 |   "author": "hangye",
25 |   "license": "MIT",
26 |   "devDependencies": {
27 |     "rimraf": "^5.0.0"
28 |   },
29 |   "repository": {
30 |     "type": "git",
31 |     "url": "git+https://github.com/your-repo/chrome-mcp-server.git",
32 |     "directory": "packages/wasm-simd"
33 |   }
34 | }
35 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | import serverInstance from './server';
 3 | import nativeMessagingHostInstance from './native-messaging-host';
 4 | 
 5 | try {
 6 |   serverInstance.setNativeHost(nativeMessagingHostInstance); // Server needs setNativeHost method
 7 |   nativeMessagingHostInstance.setServer(serverInstance); // NativeHost needs setServer method
 8 |   nativeMessagingHostInstance.start();
 9 | } catch (error) {
10 |   process.exit(1);
11 | }
12 | 
13 | process.on('error', (error) => {
14 |   process.exit(1);
15 | });
16 | 
17 | // Handle process signals and uncaught exceptions
18 | process.on('SIGINT', () => {
19 |   process.exit(0);
20 | });
21 | 
22 | process.on('SIGTERM', () => {
23 |   process.exit(0);
24 | });
25 | 
26 | process.on('exit', (code) => {
27 | });
28 | 
29 | process.on('uncaughtException', (error) => {
30 |   process.exit(1);
31 | });
32 | 
33 | process.on('unhandledRejection', (reason) => {
34 |   // Don't exit immediately, let the program continue running
35 | });
36 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/TrashIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="1.5"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/widgets/FieldCode.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="code">
 3 |     <textarea
 4 |       class="form-input mono"
 5 |       rows="6"
 6 |       :placeholder="placeholder"
 7 |       :value="text"
 8 |       @input="onInput"
 9 |     ></textarea>
10 |   </div>
11 | </template>
12 | 
13 | <script lang="ts" setup>
14 | import { ref, watchEffect } from 'vue';
15 | const props = defineProps<{ modelValue?: string; field?: any }>();
16 | const emit = defineEmits<{ (e: 'update:modelValue', v?: string): void }>();
17 | const text = ref<string>(props.modelValue ?? '');
18 | const placeholder = props.field?.placeholder || '/* code */';
19 | function onInput(ev: any) {
20 |   const v = String(ev?.target?.value ?? '');
21 |   text.value = v;
22 |   emit('update:modelValue', v);
23 | }
24 | watchEffect(() => (text.value = props.modelValue ?? ''));
25 | </script>
26 | 
27 | <style scoped>
28 | .mono {
29 |   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
30 | }
31 | </style>
32 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/web-editor-v2/ui/property-panel/controls/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Design Controls Index
 3 |  *
 4 |  * Exports all design control factories.
 5 |  */
 6 | 
 7 | export { createSizeControl, type SizeControlOptions } from './size-control';
 8 | export { createSpacingControl, type SpacingControlOptions } from './spacing-control';
 9 | export { createPositionControl, type PositionControlOptions } from './position-control';
10 | export { createLayoutControl, type LayoutControlOptions } from './layout-control';
11 | export { createTypographyControl, type TypographyControlOptions } from './typography-control';
12 | export { createAppearanceControl, type AppearanceControlOptions } from './appearance-control';
13 | export { createEffectsControl, type EffectsControlOptions } from './effects-control';
14 | export { createGradientControl, type GradientControlOptions } from './gradient-control';
15 | export { createTokenPicker, type TokenPicker, type TokenPickerOptions } from './token-picker';
16 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/main.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { createApp } from 'vue';
 2 | import { NativeMessageType } from 'chrome-mcp-shared';
 3 | import App from './App.vue';
 4 | 
 5 | // Tailwind first, then custom tokens
 6 | import '../styles/tailwind.css';
 7 | // AgentChat theme tokens
 8 | import './styles/agent-chat.css';
 9 | 
10 | import { preloadAgentTheme } from './composables';
11 | 
12 | /**
13 |  * Initialize and mount the Vue app.
14 |  * Preloads theme before mounting to prevent flash.
15 |  */
16 | async function init(): Promise<void> {
17 |   // Preload theme from storage and apply to document
18 |   // This happens before Vue mounts, preventing theme flash
19 |   await preloadAgentTheme();
20 | 
21 |   // Trigger ensure native connection (fire-and-forget, don't block UI mounting)
22 |   void chrome.runtime.sendMessage({ type: NativeMessageType.ENSURE_NATIVE }).catch(() => {
23 |     // Silent failure - background will handle reconnection
24 |   });
25 | 
26 |   // Mount Vue app
27 |   createApp(App).mount('#app');
28 | }
29 | 
30 | init();
31 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/icons/WorkflowIcon.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <svg
 3 |     xmlns="http://www.w3.org/2000/svg"
 4 |     fill="none"
 5 |     viewBox="0 0 24 24"
 6 |     stroke-width="1.5"
 7 |     stroke="currentColor"
 8 |     :class="className"
 9 |   >
10 |     <path
11 |       stroke-linecap="round"
12 |       stroke-linejoin="round"
13 |       d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z"
14 |     />
15 |   </svg>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | interface Props {
20 |   className?: string;
21 | }
22 | 
23 | withDefaults(defineProps<Props>(), {
24 |   className: 'icon-default',
25 | });
26 | </script>
27 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/engine/policies/retry.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // engine/policies/retry.ts — unified retry/backoff policy
 2 | 
 3 | export type BackoffKind = 'none' | 'exp';
 4 | 
 5 | export interface RetryOptions {
 6 |   count?: number; // max attempts beyond the first run
 7 |   intervalMs?: number;
 8 |   backoff?: BackoffKind;
 9 | }
10 | 
11 | export async function withRetry<T>(
12 |   run: () => Promise<T>,
13 |   onRetry?: (attempt: number, err: any) => Promise<void> | void,
14 |   opts?: RetryOptions,
15 | ): Promise<T> {
16 |   const max = Math.max(0, Number(opts?.count ?? 0));
17 |   const base = Math.max(0, Number(opts?.intervalMs ?? 0));
18 |   const backoff = (opts?.backoff || 'none') as BackoffKind;
19 |   let attempt = 0;
20 |   while (true) {
21 |     try {
22 |       return await run();
23 |     } catch (e) {
24 |       if (attempt >= max) throw e;
25 |       if (onRetry) await onRetry(attempt, e);
26 |       const delay = base > 0 ? (backoff === 'exp' ? base * Math.pow(2, attempt) : base) : 0;
27 |       if (delay > 0) await new Promise((r) => setTimeout(r, delay));
28 |       attempt += 1;
29 |     }
30 |   }
31 | }
32 | 
```

--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "chrome-mcp-shared",
 3 |   "version": "1.0.1",
 4 |   "author": "hangye",
 5 |   "main": "dist/index.js",
 6 |   "module": "./dist/index.mjs",
 7 |   "types": "dist/index.d.ts",
 8 |   "exports": {
 9 |     ".": {
10 |       "import": {
11 |         "types": "./dist/index.d.ts",
12 |         "default": "./dist/index.mjs"
13 |       },
14 |       "require": {
15 |         "types": "./dist/index.d.ts",
16 |         "default": "./dist/index.js"
17 |       }
18 |     }
19 |   },
20 |   "scripts": {
21 |     "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22 |     "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23 |     "lint": "npx eslint 'src/**/*.{js,ts}'",
24 |     "lint:fix": "npx eslint 'src/**/*.{js,ts}' --fix",
25 |     "format": "prettier --write 'src/**/*.{js,ts,json}'"
26 |   },
27 |   "files": [
28 |     "dist"
29 |   ],
30 |   "devDependencies": {
31 |     "@types/node": "^18.0.0",
32 |     "@typescript-eslint/parser": "^8.32.0",
33 |     "tsup": "^8.4.0"
34 |   },
35 |   "dependencies": {
36 |     "@modelcontextprotocol/sdk": "^1.11.0",
37 |     "zod": "^3.24.4"
38 |   }
39 | }
40 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyTriggerEvent.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div>
 3 |     <SelectorEditor :node="node" :allowPick="true" />
 4 |     <div class="form-section">
 5 |       <div class="form-group">
 6 |         <label class="form-label">事件类型</label>
 7 |         <input
 8 |           class="form-input"
 9 |           v-model="(node as any).config.event"
10 |           placeholder="如 input/change/mouseover"
11 |         />
12 |       </div>
13 |       <div class="form-group checkbox-group">
14 |         <label class="checkbox-label"
15 |           ><input type="checkbox" v-model="(node as any).config.bubbles" /> 冒泡</label
16 |         >
17 |         <label class="checkbox-label"
18 |           ><input type="checkbox" v-model="(node as any).config.cancelable" /> 可取消</label
19 |         >
20 |       </div>
21 |     </div>
22 |   </div>
23 | </template>
24 | 
25 | <script lang="ts" setup>
26 | /* eslint-disable vue/no-mutating-props */
27 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
28 | import SelectorEditor from './SelectorEditor.vue';
29 | 
30 | defineProps<{ node: NodeBase }>();
31 | </script>
32 | 
33 | <style scoped></style>
34 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/engine/constants.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // constants.ts — centralized engine constants and labels
 2 | import { EDGE_LABELS } from 'chrome-mcp-shared';
 3 | 
 4 | export const ENGINE_CONSTANTS = {
 5 |   DEFAULT_WAIT_MS: 5000,
 6 |   MAX_WAIT_MS: 120000,
 7 |   NETWORK_IDLE_SAMPLE_MS: 1200,
 8 |   MAX_ITERATIONS: 1000,
 9 |   MAX_FOREACH_CONCURRENCY: 16,
10 |   EDGE_LABELS: EDGE_LABELS,
11 | } as const;
12 | 
13 | export type EdgeLabel =
14 |   (typeof ENGINE_CONSTANTS.EDGE_LABELS)[keyof typeof ENGINE_CONSTANTS.EDGE_LABELS];
15 | 
16 | // Centralized stepId values used in run logs for non-step events
17 | export const LOG_STEP_IDS = {
18 |   GLOBAL_TIMEOUT: 'global-timeout',
19 |   PLUGIN_RUN_START: 'plugin-runStart',
20 |   VARIABLE_COLLECT: 'variable-collect',
21 |   BINDING_CHECK: 'binding-check',
22 |   NETWORK_CAPTURE: 'network-capture',
23 |   DAG_REQUIRED: 'dag-required',
24 |   DAG_CYCLE: 'dag-cycle',
25 |   LOOP_GUARD: 'loop-guard',
26 |   PLUGIN_RUN_END: 'plugin-runEnd',
27 |   RUNSTATE_UPDATE: 'runState-update',
28 |   RUNSTATE_DELETE: 'runState-delete',
29 | } as const;
30 | 
31 | export type LogStepId = (typeof LOG_STEP_IDS)[keyof typeof LOG_STEP_IDS];
32 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/tools/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { createErrorResponse } from '@/common/tool-handler';
 2 | import { ERROR_MESSAGES } from '@/common/constants';
 3 | import * as browserTools from './browser';
 4 | import { flowRunTool, listPublishedFlowsTool } from './record-replay';
 5 | 
 6 | const tools = { ...browserTools, flowRunTool, listPublishedFlowsTool } as any;
 7 | const toolsMap = new Map(Object.values(tools).map((tool: any) => [tool.name, tool]));
 8 | 
 9 | /**
10 |  * Tool call parameter interface
11 |  */
12 | export interface ToolCallParam {
13 |   name: string;
14 |   args: any;
15 | }
16 | 
17 | /**
18 |  * Handle tool execution
19 |  */
20 | export const handleCallTool = async (param: ToolCallParam) => {
21 |   const tool = toolsMap.get(param.name);
22 |   if (!tool) {
23 |     return createErrorResponse(`Tool ${param.name} not found`);
24 |   }
25 | 
26 |   try {
27 |     return await tool.execute(param.args);
28 |   } catch (error) {
29 |     console.error(`Tool execution failed for ${param.name}:`, error);
30 |     return createErrorResponse(
31 |       error instanceof Error ? error.message : ERROR_MESSAGES.TOOL_EXECUTION_FAILED,
32 |     );
33 |   }
34 | };
35 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyWait.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">等待条件 (JSON)</label>
 5 |       <textarea
 6 |         class="form-textarea"
 7 |         v-model="waitJson"
 8 |         rows="4"
 9 |         placeholder='{"text":"ok","appear":true}'
10 |       ></textarea>
11 |     </div>
12 |   </div>
13 | </template>
14 | 
15 | <script lang="ts" setup>
16 | /* eslint-disable vue/no-mutating-props */
17 | import { computed } from 'vue';
18 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
19 | 
20 | const props = defineProps<{ node: NodeBase }>();
21 | 
22 | const waitJson = computed({
23 |   get() {
24 |     const n = props.node;
25 |     if (!n || n.type !== 'wait') return '';
26 |     try {
27 |       return JSON.stringify((n as any).config?.condition || {}, null, 2);
28 |     } catch {
29 |       return '';
30 |     }
31 |   },
32 |   set(v: string) {
33 |     const n = props.node;
34 |     if (!n || n.type !== 'wait') return;
35 |     try {
36 |       (n as any).config = { ...((n as any).config || {}), condition: JSON.parse(v || '{}') };
37 |     } catch {}
38 |   },
39 | });
40 | </script>
41 | 
42 | <style scoped></style>
43 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/vitest.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { fileURLToPath } from 'node:url';
 2 | 
 3 | import { defineConfig } from 'vitest/config';
 4 | 
 5 | const rootDir = fileURLToPath(new URL('.', import.meta.url));
 6 | 
 7 | export default defineConfig({
 8 |   resolve: {
 9 |     alias: {
10 |       // Match WXT's path aliases from .wxt/tsconfig.json
11 |       '@': rootDir,
12 |       '~': rootDir,
13 |       // Mock hnswlib-wasm-static to avoid native module issues in tests
14 |       'hnswlib-wasm-static': `${rootDir}/tests/__mocks__/hnswlib-wasm-static.ts`,
15 |     },
16 |   },
17 |   test: {
18 |     environment: 'jsdom',
19 |     include: ['tests/**/*.test.ts'],
20 |     exclude: ['node_modules', '.output', 'dist', '.wxt'],
21 |     setupFiles: ['tests/vitest.setup.ts'],
22 |     environmentOptions: {
23 |       jsdom: {
24 |         // Provide a stable URL for anchor/href tests
25 |         url: 'https://example.com/',
26 |       },
27 |     },
28 |     // Auto-cleanup mocks between tests
29 |     clearMocks: true,
30 |     restoreMocks: true,
31 |     // TypeScript support via esbuild (faster than ts-jest)
32 |     typecheck: {
33 |       enabled: false, // Run separately with vue-tsc
34 |     },
35 |   },
36 | });
37 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertySetAttribute.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div>
 3 |     <SelectorEditor :node="node" :allowPick="true" />
 4 |     <div class="form-section">
 5 |       <div class="form-group">
 6 |         <label class="form-label">属性名</label>
 7 |         <input
 8 |           class="form-input"
 9 |           v-model="(node as any).config.name"
10 |           placeholder="如 value/src/disabled 等"
11 |         />
12 |       </div>
13 |       <div class="form-group">
14 |         <label class="form-label">属性值(留空并勾选删除则移除)</label>
15 |         <input class="form-input" v-model="(node as any).config.value" placeholder="属性值" />
16 |       </div>
17 |       <div class="form-group checkbox-group">
18 |         <label class="checkbox-label"
19 |           ><input type="checkbox" v-model="(node as any).config.remove" /> 删除属性</label
20 |         >
21 |       </div>
22 |     </div>
23 |   </div>
24 | </template>
25 | 
26 | <script lang="ts" setup>
27 | /* eslint-disable vue/no-mutating-props */
28 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
29 | import SelectorEditor from './SelectorEditor.vue';
30 | 
31 | defineProps<{ node: NodeBase }>();
32 | </script>
33 | 
34 | <style scoped></style>
35 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyCloseTab.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">按 URL 关闭(可选)</label>
 5 |       <input class="form-input" v-model="(node as any).config.url" placeholder="子串匹配 URL" />
 6 |     </div>
 7 |     <div class="form-group">
 8 |       <label class="form-label">Tab IDs(JSON 数组,可选)</label>
 9 |       <textarea class="form-textarea" v-model="tabIdsJson" rows="2" placeholder="[1,2]"></textarea>
10 |     </div>
11 |   </div>
12 | </template>
13 | 
14 | <script lang="ts" setup>
15 | /* eslint-disable vue/no-mutating-props */
16 | import { computed } from 'vue';
17 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
18 | 
19 | const props = defineProps<{ node: NodeBase }>();
20 | 
21 | const tabIdsJson = computed({
22 |   get() {
23 |     try {
24 |       const arr = (props.node as any).config?.tabIds;
25 |       return Array.isArray(arr) ? JSON.stringify(arr) : '';
26 |     } catch {
27 |       return '';
28 |     }
29 |   },
30 |   set(v: string) {
31 |     try {
32 |       (props.node as any).config.tabIds = v ? JSON.parse(v) : [];
33 |     } catch {}
34 |   },
35 | });
36 | </script>
37 | 
38 | <style scoped></style>
39 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/nodes/key.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { TOOL_NAMES } from 'chrome-mcp-shared';
 2 | import { handleCallTool } from '@/entrypoints/background/tools';
 3 | import type { StepKey } from '../types';
 4 | import { expandTemplatesDeep } from '../rr-utils';
 5 | import type { ExecCtx, ExecResult, NodeRuntime } from './types';
 6 | 
 7 | export const keyNode: NodeRuntime<StepKey> = {
 8 |   run: async (ctx, step: StepKey) => {
 9 |     const s = expandTemplatesDeep(step as StepKey, ctx.vars) as StepKey;
10 |     const args: { keys: string; frameId?: number; selector?: string } = { keys: s.keys };
11 | 
12 |     // Support target selector for focusing before key input
13 |     if (s.target && s.target.candidates?.length) {
14 |       const selector = s.target.candidates[0]?.value;
15 |       if (selector) {
16 |         args.selector = selector;
17 |       }
18 |     }
19 | 
20 |     if (typeof ctx.frameId === 'number') {
21 |       args.frameId = ctx.frameId;
22 |     }
23 | 
24 |     const res = await handleCallTool({
25 |       name: TOOL_NAMES.BROWSER.KEYBOARD,
26 |       args,
27 |     });
28 |     if ((res as any).isError) throw new Error('key failed');
29 |     return {} as ExecResult;
30 |   },
31 | };
32 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/nodes/http.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { TOOL_NAMES } from 'chrome-mcp-shared';
 2 | import { handleCallTool } from '@/entrypoints/background/tools';
 3 | import type { StepHttp } from '../types';
 4 | import { applyAssign, expandTemplatesDeep } from '../rr-utils';
 5 | import type { ExecCtx, ExecResult, NodeRuntime } from './types';
 6 | 
 7 | export const httpNode: NodeRuntime<StepHttp> = {
 8 |   run: async (ctx: ExecCtx, step: StepHttp) => {
 9 |     const s: any = expandTemplatesDeep(step as any, ctx.vars);
10 |     const res = await handleCallTool({
11 |       name: TOOL_NAMES.BROWSER.NETWORK_REQUEST,
12 |       args: {
13 |         url: s.url,
14 |         method: s.method || 'GET',
15 |         headers: s.headers || {},
16 |         body: s.body,
17 |         formData: s.formData,
18 |       },
19 |     });
20 |     const text = (res as any)?.content?.find((c: any) => c.type === 'text')?.text;
21 |     try {
22 |       const payload = text ? JSON.parse(text) : null;
23 |       if (s.saveAs && payload !== undefined) ctx.vars[s.saveAs] = payload;
24 |       if (s.assign && payload !== undefined) applyAssign(ctx.vars, payload, s.assign);
25 |     } catch {}
26 |     return {} as ExecResult;
27 |   },
28 | };
29 | 
```

--------------------------------------------------------------------------------
/app/native-server/src/types/devtools-frontend.d.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // Minimal ambient declarations to avoid compiling chrome-devtools-frontend sources.
 2 | // We intentionally treat these modules as `any` to keep our build lightweight and decoupled
 3 | // from DevTools' internal TypeScript and lib targets.
 4 | 
 5 | declare module 'chrome-devtools-frontend/front_end/models/trace/trace.js' {
 6 |   // Shape used by our code: TraceModel + Types + Insights
 7 |   export const TraceModel: any;
 8 |   export const Types: any;
 9 |   export const Insights: any;
10 | }
11 | 
12 | declare module 'chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js' {
13 |   export class PerformanceTraceFormatter {
14 |     constructor(...args: any[]);
15 |     formatTraceSummary(): string;
16 |   }
17 | }
18 | 
19 | declare module 'chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js' {
20 |   export class PerformanceInsightFormatter {
21 |     constructor(...args: any[]);
22 |     formatInsight(): string;
23 |   }
24 | }
25 | 
26 | declare module 'chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js' {
27 |   export const AgentFocus: any;
28 | }
29 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyHandleDownload.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">文件名包含(可选)</label>
 5 |       <input
 6 |         class="form-input"
 7 |         v-model="(node as any).config.filenameContains"
 8 |         placeholder="子串匹配文件名或URL"
 9 |       />
10 |     </div>
11 |     <div class="form-group">
12 |       <label class="form-label">超时(ms)</label>
13 |       <input class="form-input" v-model="(node as any).config.timeoutMs" placeholder="默认 60000" />
14 |     </div>
15 |     <div class="form-group checkbox-group">
16 |       <label class="checkbox-label"
17 |         ><input type="checkbox" v-model="(node as any).config.waitForComplete" />
18 |         等待下载完成</label
19 |       >
20 |     </div>
21 |     <div class="form-group">
22 |       <label class="form-label">保存到变量</label>
23 |       <input class="form-input" v-model="(node as any).config.saveAs" placeholder="默认 download" />
24 |     </div>
25 |   </div>
26 | </template>
27 | 
28 | <script lang="ts" setup>
29 | /* eslint-disable vue/no-mutating-props */
30 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
31 | defineProps<{ node: NodeBase }>();
32 | </script>
33 | 
34 | <style scoped></style>
35 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent/ConnectionStatus.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="px-4 py-2 border-b border-slate-200 flex items-center justify-between gap-2">
 3 |     <div class="flex items-center gap-2 text-xs text-slate-600">
 4 |       <span :class="['inline-flex h-2 w-2 rounded-full', statusColor]"></span>
 5 |       <span>{{ statusText }}</span>
 6 |     </div>
 7 |     <button
 8 |       class="btn-secondary !px-3 !py-1 text-xs"
 9 |       :disabled="connecting"
10 |       @click="$emit('reconnect')"
11 |     >
12 |       {{ connecting ? 'Reconnecting...' : 'Reconnect' }}
13 |     </button>
14 |   </div>
15 | </template>
16 | 
17 | <script lang="ts" setup>
18 | import { computed } from 'vue';
19 | 
20 | const props = defineProps<{
21 |   isServerReady: boolean;
22 |   nativeConnected: boolean;
23 |   connecting: boolean;
24 | }>();
25 | 
26 | defineEmits<{
27 |   reconnect: [];
28 | }>();
29 | 
30 | const statusColor = computed(() => {
31 |   if (props.isServerReady) return 'bg-green-500';
32 |   if (props.nativeConnected) return 'bg-yellow-500';
33 |   return 'bg-slate-400';
34 | });
35 | 
36 | const statusText = computed(() => {
37 |   if (props.isServerReady) return 'Agent server connected';
38 |   if (props.nativeConnected) return 'Connecting to agent server...';
39 |   return 'Native host not connected';
40 | });
41 | </script>
42 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/actions/handlers/delay.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Delay Action Handler
 3 |  *
 4 |  * Provides a simple pause in execution flow.
 5 |  * Supports variable resolution for dynamic delay times.
 6 |  */
 7 | 
 8 | import { failed, invalid, ok, tryResolveNumber } from '../registry';
 9 | import type { ActionHandler } from '../types';
10 | 
11 | /** Maximum delay time to prevent integer overflow in setTimeout */
12 | const MAX_DELAY_MS = 2_147_483_647;
13 | 
14 | export const delayHandler: ActionHandler<'delay'> = {
15 |   type: 'delay',
16 | 
17 |   validate: (action) => {
18 |     if (action.params.sleep === undefined) {
19 |       return invalid('Missing sleep parameter');
20 |     }
21 |     return ok();
22 |   },
23 | 
24 |   describe: (action) => {
25 |     const ms = typeof action.params.sleep === 'number' ? action.params.sleep : '(dynamic)';
26 |     return `Delay ${ms}ms`;
27 |   },
28 | 
29 |   run: async (ctx, action) => {
30 |     const resolved = tryResolveNumber(action.params.sleep, ctx.vars);
31 |     if (!resolved.ok) {
32 |       return failed('VALIDATION_ERROR', resolved.error);
33 |     }
34 | 
35 |     const ms = Math.max(0, Math.min(MAX_DELAY_MS, Math.floor(resolved.value)));
36 | 
37 |     if (ms > 0) {
38 |       await new Promise((resolve) => setTimeout(resolve, ms));
39 |     }
40 | 
41 |     return { status: 'success' };
42 |   },
43 | };
44 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/nodes/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { RunLogEntry, Step, StepScript } from '../types';
 2 | 
 3 | /**
 4 |  * Execution context for step execution.
 5 |  * Contains runtime state that may change during flow execution.
 6 |  */
 7 | export interface ExecCtx {
 8 |   /** Runtime variables accessible to steps */
 9 |   vars: Record<string, any>;
10 |   /** Logger function for recording execution events */
11 |   logger: (e: RunLogEntry) => void;
12 |   /**
13 |    * Current tab ID for this execution context.
14 |    * Managed by Scheduler, may change after openTab/switchTab actions.
15 |    */
16 |   tabId?: number;
17 |   /**
18 |    * Current frame ID within the tab.
19 |    * Used for iframe targeting, 0 for main frame.
20 |    */
21 |   frameId?: number;
22 | }
23 | 
24 | export interface ExecResult {
25 |   alreadyLogged?: boolean;
26 |   deferAfterScript?: StepScript | null;
27 |   nextLabel?: string;
28 |   control?:
29 |     | { kind: 'foreach'; listVar: string; itemVar: string; subflowId: string; concurrency?: number }
30 |     | { kind: 'while'; condition: any; subflowId: string; maxIterations: number };
31 | }
32 | 
33 | export interface NodeRuntime<S extends Step = Step> {
34 |   validate?: (step: S) => { ok: boolean; errors?: string[] };
35 |   run: (ctx: ExecCtx, step: S) => Promise<ExecResult | void>;
36 | }
37 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyExtract.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">元素选择器(可选)</label>
 5 |       <input class="form-input" v-model="(node as any).config.selector" placeholder="CSS 选择器" />
 6 |     </div>
 7 |     <div class="form-group">
 8 |       <label class="form-label">属性</label>
 9 |       <input
10 |         class="form-input"
11 |         v-model="(node as any).config.attr"
12 |         placeholder="text/textContent 或属性名"
13 |       />
14 |     </div>
15 |     <div class="form-group">
16 |       <label class="form-label">自定义 JS(返回值)</label>
17 |       <textarea
18 |         class="form-textarea"
19 |         v-model="(node as any).config.js"
20 |         rows="3"
21 |         placeholder="return document.title"
22 |       ></textarea>
23 |     </div>
24 |     <div class="form-group" :class="{ invalid: !(node as any).config?.saveAs }">
25 |       <label class="form-label">保存为变量</label>
26 |       <input class="form-input" v-model="(node as any).config.saveAs" placeholder="变量名" />
27 |     </div>
28 |   </div>
29 | </template>
30 | 
31 | <script lang="ts" setup>
32 | /* eslint-disable vue/no-mutating-props */
33 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
34 | defineProps<{ node: NodeBase }>();
35 | </script>
36 | 
37 | <style scoped></style>
38 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/sidepanel/components/agent/MessageItem.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="flex flex-col gap-1 rounded-lg px-3 py-2 max-w-full" :class="messageClasses">
 3 |     <div class="flex items-center justify-between gap-2 text-[11px] opacity-70">
 4 |       <span>{{ senderName }}</span>
 5 |       <span v-if="message.createdAt">
 6 |         {{ formatTime(message.createdAt) }}
 7 |       </span>
 8 |     </div>
 9 |     <div class="whitespace-pre-wrap break-words text-xs leading-relaxed">
10 |       {{ message.content }}
11 |     </div>
12 |     <div v-if="message.isStreaming && !message.isFinal" class="text-[10px] opacity-60 mt-0.5">
13 |       Streaming...
14 |     </div>
15 |   </div>
16 | </template>
17 | 
18 | <script lang="ts" setup>
19 | import { computed } from 'vue';
20 | import type { AgentMessage } from 'chrome-mcp-shared';
21 | 
22 | const props = defineProps<{
23 |   message: AgentMessage;
24 | }>();
25 | 
26 | const messageClasses = computed(() => {
27 |   return props.message.role === 'user'
28 |     ? 'bg-white border border-slate-200 self-end'
29 |     : 'bg-slate-900 text-slate-50 self-start';
30 | });
31 | 
32 | const senderName = computed(() => {
33 |   return props.message.role === 'user' ? 'You' : props.message.cliSource || 'Agent';
34 | });
35 | 
36 | function formatTime(dateStr: string): string {
37 |   return new Date(dateStr).toLocaleTimeString();
38 | }
39 | </script>
40 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/shared/selector/strategies/css-path.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * CSS Path Strategy - 基于 DOM 路径的选择器策略
 3 |  * 使用 nth-of-type 生成完整的 CSS 路径
 4 |  */
 5 | 
 6 | import type { SelectorCandidate, SelectorStrategy } from '../types';
 7 | 
 8 | export const cssPathStrategy: SelectorStrategy = {
 9 |   id: 'css-path',
10 |   generate(ctx) {
11 |     if (!ctx.options.includeCssPath) return [];
12 | 
13 |     const { element } = ctx;
14 | 
15 |     const segments: string[] = [];
16 |     let current: Element | null = element;
17 | 
18 |     while (current) {
19 |       const tag = current.tagName?.toLowerCase?.() ?? '';
20 |       if (!tag) break;
21 | 
22 |       let segment = tag;
23 | 
24 |       const parent = current.parentElement;
25 |       if (parent) {
26 |         const siblings = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);
27 |         if (siblings.length > 1) {
28 |           const index = siblings.indexOf(current) + 1;
29 |           if (index > 0) segment += `:nth-of-type(${index})`;
30 |         }
31 |       }
32 | 
33 |       segments.unshift(segment);
34 | 
35 |       if (tag === 'body') break;
36 |       current = parent;
37 |     }
38 | 
39 |     const selector = segments.length ? segments.join(' > ') : 'body';
40 | 
41 |     const out: SelectorCandidate[] = [
42 |       { type: 'css', value: selector, source: 'generated', strategy: 'css-path' },
43 |     ];
44 |     return out;
45 |   },
46 | };
47 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/widgets/FieldExpression.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="expr">
 3 |     <input class="form-input mono" :placeholder="placeholder" :value="text" @input="onInput" />
 4 |     <div v-if="err" class="error-item">{{ err }}</div>
 5 |   </div>
 6 | </template>
 7 | 
 8 | <script lang="ts" setup>
 9 | import { ref, watchEffect } from 'vue';
10 | import { evalExpression } from '@/entrypoints/background/record-replay/engine/utils/expression';
11 | 
12 | const props = defineProps<{ modelValue?: string; field?: any }>();
13 | const emit = defineEmits<{ (e: 'update:modelValue', v?: string): void }>();
14 | const text = ref<string>(props.modelValue ?? '');
15 | const err = ref<string>('');
16 | const placeholder = props.field?.placeholder || 'e.g. vars.a > 0 && vars.flag';
17 | 
18 | function onInput(ev: any) {
19 |   const v = String(ev?.target?.value ?? '');
20 |   text.value = v;
21 |   try {
22 |     // just validate; allow empty
23 |     if (v.trim()) {
24 |       evalExpression(v, { vars: {} as any });
25 |     }
26 |     err.value = '';
27 |   } catch (e: any) {
28 |     err.value = '表达式解析错误';
29 |   }
30 |   emit('update:modelValue', v);
31 | }
32 | 
33 | watchEffect(() => {
34 |   text.value = props.modelValue ?? '';
35 | });
36 | </script>
37 | 
38 | <style scoped>
39 | .mono {
40 |   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
41 | }
42 | </style>
43 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyFill.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div>
 3 |     <SelectorEditor :node="node" :allowPick="true" />
 4 |     <div class="form-section">
 5 |       <div class="form-group" data-field="fill.value">
 6 |         <label class="form-label">输入值</label>
 7 |         <VarInput v-model="value" :variables="variables" placeholder="支持 {变量名} 格式" />
 8 |       </div>
 9 |     </div>
10 |   </div>
11 | </template>
12 | 
13 | <script lang="ts" setup>
14 | /* eslint-disable vue/no-mutating-props */
15 | import { computed } from 'vue';
16 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
17 | import type { VariableOption } from '@/entrypoints/popup/components/builder/model/variables';
18 | import SelectorEditor from './SelectorEditor.vue';
19 | import VarInput from '@/entrypoints/popup/components/builder/widgets/VarInput.vue';
20 | 
21 | const props = defineProps<{ node: NodeBase; variables?: VariableOption[] }>();
22 | const variables = computed<VariableOption[]>(() => (props.variables || []).slice());
23 | const value = computed<string>({
24 |   get() {
25 |     return String((props.node as any)?.config?.value ?? '');
26 |   },
27 |   set(v: string) {
28 |     if (!props.node) return;
29 |     if (!(props.node as any).config) (props.node as any).config = {} as any;
30 |     (props.node as any).config.value = v;
31 |   },
32 | });
33 | </script>
34 | 
35 | <style scoped></style>
36 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyForeach.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">列表变量</label>
 5 |       <input
 6 |         class="form-input"
 7 |         v-model="(node as any).config.listVar"
 8 |         placeholder="workflow.list"
 9 |       />
10 |     </div>
11 |     <div class="form-group">
12 |       <label class="form-label">循环项变量名</label>
13 |       <input class="form-input" v-model="(node as any).config.itemVar" placeholder="默认 item" />
14 |     </div>
15 |     <div class="form-group">
16 |       <label class="form-label">子流 ID</label>
17 |       <input
18 |         class="form-input"
19 |         v-model="(node as any).config.subflowId"
20 |         placeholder="选择或新建子流"
21 |       />
22 |       <button class="btn-sm" style="margin-top: 8px" @click="onCreateSubflow">新建子流</button>
23 |     </div>
24 |   </div>
25 | </template>
26 | 
27 | <script lang="ts" setup>
28 | /* eslint-disable vue/no-mutating-props */
29 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
30 | 
31 | const props = defineProps<{ node: NodeBase }>();
32 | const emit = defineEmits<{ (e: 'create-subflow', id: string): void }>();
33 | 
34 | function onCreateSubflow() {
35 |   const id = prompt('请输入新子流ID');
36 |   if (!id) return;
37 |   emit('create-subflow', id);
38 |   const n = props.node as any;
39 |   if (n && n.config) n.config.subflowId = id;
40 | }
41 | </script>
42 | 
43 | <style scoped></style>
44 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay-v3/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * @fileoverview Record-Replay V3 公共 API 入口
 3 |  * @description 导出所有公共类型和接口
 4 |  */
 5 | 
 6 | // ==================== Domain ====================
 7 | export * from './domain';
 8 | 
 9 | // ==================== Engine ====================
10 | export * from './engine';
11 | 
12 | // ==================== Storage ====================
13 | export * from './storage';
14 | 
15 | // ==================== Factory Functions ====================
16 | 
17 | import type { StoragePort } from './engine/storage/storage-port';
18 | import { createFlowsStore } from './storage/flows';
19 | import { createRunsStore } from './storage/runs';
20 | import { createEventsStore } from './storage/events';
21 | import { createQueueStore } from './storage/queue';
22 | import { createPersistentVarsStore } from './storage/persistent-vars';
23 | import { createTriggersStore } from './storage/triggers';
24 | 
25 | /**
26 |  * 创建完整的 StoragePort 实现
27 |  */
28 | export function createStoragePort(): StoragePort {
29 |   return {
30 |     flows: createFlowsStore(),
31 |     runs: createRunsStore(),
32 |     events: createEventsStore(),
33 |     queue: createQueueStore(),
34 |     persistentVars: createPersistentVarsStore(),
35 |     triggers: createTriggersStore(),
36 |   };
37 | }
38 | 
39 | // ==================== Version ====================
40 | 
41 | /** V3 API 版本 */
42 | export const RR_V3_VERSION = '3.0.0' as const;
43 | 
44 | /** 是否为 V3 API */
45 | export const IS_RR_V3 = true as const;
46 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/widgets/FieldDuration.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="duration">
 3 |     <div class="row">
 4 |       <input class="form-input" type="number" :value="val" @input="onNum" min="0" />
 5 |       <select class="form-input unit" :value="unit" @change="onUnit">
 6 |         <option value="ms">ms</option>
 7 |         <option value="s">s</option>
 8 |       </select>
 9 |     </div>
10 |   </div>
11 | </template>
12 | 
13 | <script lang="ts" setup>
14 | import { ref, watchEffect } from 'vue';
15 | const props = defineProps<{ modelValue?: number; field?: any }>();
16 | const emit = defineEmits<{ (e: 'update:modelValue', v?: number): void }>();
17 | const unit = ref<'ms' | 's'>('ms');
18 | const val = ref<number>(Number(props.modelValue || 0));
19 | watchEffect(() => {
20 |   const ms = Number(props.modelValue || 0);
21 |   if (ms % 1000 === 0 && ms >= 1000) {
22 |     unit.value = 's';
23 |     val.value = ms / 1000;
24 |   } else {
25 |     unit.value = 'ms';
26 |     val.value = ms;
27 |   }
28 | });
29 | function onNum(ev: any) {
30 |   const n = Number(ev?.target?.value || 0);
31 |   val.value = n;
32 |   emit('update:modelValue', unit.value === 's' ? n * 1000 : n);
33 | }
34 | function onUnit(ev: any) {
35 |   unit.value = ev?.target?.value === 's' ? 's' : 'ms';
36 |   emit('update:modelValue', unit.value === 's' ? val.value * 1000 : val.value);
37 | }
38 | </script>
39 | 
40 | <style scoped>
41 | .row {
42 |   display: flex;
43 |   gap: 8px;
44 |   align-items: center;
45 | }
46 | .unit {
47 |   width: 84px;
48 | }
49 | </style>
50 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/record-replay/nodes/script.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { StepScript } from '../types';
 2 | import { expandTemplatesDeep, applyAssign } from '../rr-utils';
 3 | import type { ExecCtx, ExecResult, NodeRuntime } from './types';
 4 | 
 5 | export const scriptNode: NodeRuntime<StepScript> = {
 6 |   run: async (ctx: ExecCtx, step: StepScript) => {
 7 |     const s: any = expandTemplatesDeep(step as any, ctx.vars);
 8 |     if (s.when === 'after') return { deferAfterScript: s } as ExecResult;
 9 |     const world = s.world || 'ISOLATED';
10 |     const code = String(s.code || '');
11 |     if (!code.trim()) return {} as ExecResult;
12 |     const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
13 |     const tabId = tabs?.[0]?.id;
14 |     if (typeof tabId !== 'number') throw new Error('Active tab not found');
15 |     const frameIds = typeof ctx.frameId === 'number' ? [ctx.frameId] : undefined;
16 |     const [{ result }] = await chrome.scripting.executeScript({
17 |       target: { tabId, frameIds } as any,
18 |       func: (userCode: string) => {
19 |         try {
20 |           return (0, eval)(userCode);
21 |         } catch {
22 |           return null;
23 |         }
24 |       },
25 |       args: [code],
26 |       world: world as any,
27 |     } as any);
28 |     if (s.saveAs) ctx.vars[s.saveAs] = result;
29 |     if (s.assign && typeof s.assign === 'object') applyAssign(ctx.vars, result, s.assign);
30 |     return {} as ExecResult;
31 |   },
32 | };
33 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertyAssert.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">断言条件 (JSON)</label>
 5 |       <textarea
 6 |         class="form-textarea"
 7 |         v-model="assertJson"
 8 |         rows="4"
 9 |         placeholder='{"exists":"#id"}'
10 |       ></textarea>
11 |     </div>
12 |     <div class="form-group">
13 |       <label class="form-label">失败策略</label>
14 |       <select class="form-select" v-model="(node as any).config.failStrategy">
15 |         <option value="stop">stop</option>
16 |         <option value="warn">warn</option>
17 |         <option value="retry">retry</option>
18 |       </select>
19 |     </div>
20 |   </div>
21 | </template>
22 | 
23 | <script lang="ts" setup>
24 | /* eslint-disable vue/no-mutating-props */
25 | import { computed } from 'vue';
26 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
27 | 
28 | const props = defineProps<{ node: NodeBase }>();
29 | 
30 | const assertJson = computed({
31 |   get() {
32 |     const n = props.node;
33 |     if (!n || n.type !== 'assert') return '';
34 |     try {
35 |       return JSON.stringify((n as any).config?.assert || {}, null, 2);
36 |     } catch {
37 |       return '';
38 |     }
39 |   },
40 |   set(v: string) {
41 |     const n = props.node;
42 |     if (!n || n.type !== 'assert') return;
43 |     try {
44 |       (n as any).config = { ...((n as any).config || {}), assert: JSON.parse(v || '{}') };
45 |     } catch {}
46 |   },
47 | });
48 | </script>
49 | 
50 | <style scoped></style>
51 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/model/form-widget-registry.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // form-widget-registry.ts — global widget registry for PropertyFormRenderer
 2 | import FieldExpression from '@/entrypoints/popup/components/builder/widgets/FieldExpression.vue';
 3 | import FieldSelector from '@/entrypoints/popup/components/builder/widgets/FieldSelector.vue';
 4 | import FieldDuration from '@/entrypoints/popup/components/builder/widgets/FieldDuration.vue';
 5 | import FieldCode from '@/entrypoints/popup/components/builder/widgets/FieldCode.vue';
 6 | import FieldKeySequence from '@/entrypoints/popup/components/builder/widgets/FieldKeySequence.vue';
 7 | import FieldTargetLocator from '@/entrypoints/popup/components/builder/widgets/FieldTargetLocator.vue';
 8 | import type { Component } from 'vue';
 9 | 
10 | const REG = new Map<string, Component>();
11 | 
12 | export function registerDefaultWidgets() {
13 |   REG.set('expression', FieldExpression as unknown as Component);
14 |   REG.set('selector', FieldSelector as unknown as Component);
15 |   REG.set('duration', FieldDuration as unknown as Component);
16 |   REG.set('code', FieldCode as unknown as Component);
17 |   REG.set('keysequence', FieldKeySequence as unknown as Component);
18 |   // Structured TargetLocator based on a selector input
19 |   REG.set('targetlocator', FieldTargetLocator as unknown as Component);
20 | }
21 | 
22 | export function getWidget(name?: string): Component | null {
23 |   if (!name) return null;
24 |   return REG.get(name) || null;
25 | }
26 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/shared/selector/strategies/text.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Text Strategy - Text content based selector strategy
 3 |  *
 4 |  * This is the lowest priority fallback strategy. Text selectors are less
 5 |  * stable than attribute-based or structural selectors because text content
 6 |  * is more likely to change (i18n, dynamic content, etc.).
 7 |  */
 8 | 
 9 | import type { SelectorCandidate, SelectorStrategy } from '../types';
10 | 
11 | /**
12 |  * Weight penalty for text selectors.
13 |  * This ensures text selectors rank after all other strategies including anchor-relpath.
14 |  * anchor-relpath uses -10, so text uses -20 to rank lower.
15 |  */
16 | const TEXT_STRATEGY_WEIGHT = -20;
17 | 
18 | function normalizeText(value: string): string {
19 |   return String(value || '')
20 |     .replace(/\s+/g, ' ')
21 |     .trim();
22 | }
23 | 
24 | export const textStrategy: SelectorStrategy = {
25 |   id: 'text',
26 | 
27 |   generate(ctx) {
28 |     if (!ctx.options.includeText) return [];
29 | 
30 |     const { element, options } = ctx;
31 |     const tag = element.tagName?.toLowerCase?.() ?? '';
32 |     if (!tag || !options.textTags.includes(tag)) return [];
33 | 
34 |     const raw = element.textContent || '';
35 |     const text = normalizeText(raw).slice(0, options.textMaxLength);
36 |     if (!text) return [];
37 | 
38 |     return [
39 |       {
40 |         type: 'text',
41 |         value: text,
42 |         match: 'contains',
43 |         tagNameHint: tag,
44 |         weight: TEXT_STRATEGY_WEIGHT,
45 |         source: 'generated',
46 |         strategy: 'text',
47 |       },
48 |     ];
49 |   },
50 | };
51 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | import js from '@eslint/js';
 2 | import globals from 'globals';
 3 | import tseslint from 'typescript-eslint';
 4 | import pluginVue from 'eslint-plugin-vue';
 5 | import { defineConfig } from 'eslint/config';
 6 | import prettierConfig from 'eslint-config-prettier';
 7 | 
 8 | export default defineConfig([
 9 |   // Global ignores - these apply to all configurations
10 |   {
11 |     ignores: [
12 |       'dist/**',
13 |       '.output/**',
14 |       '.wxt/**',
15 |       'node_modules/**',
16 |       'logs/**',
17 |       '*.log',
18 |       '.cache/**',
19 |       '.temp/**',
20 |       '.vscode/**',
21 |       '!.vscode/extensions.json',
22 |       '.idea/**',
23 |       '.DS_Store',
24 |       'Thumbs.db',
25 |       '*.zip',
26 |       '*.tar.gz',
27 |       'stats.html',
28 |       'stats-*.json',
29 |       'libs/**',
30 |       'workers/**',
31 |       'public/libs/**',
32 |     ],
33 |   },
34 |   js.configs.recommended,
35 |   {
36 |     files: ['**/*.{js,mjs,cjs,ts,vue}'],
37 |     languageOptions: {
38 |       globals: {
39 |         ...globals.browser,
40 |         chrome: 'readonly',
41 |       },
42 |     },
43 |   },
44 |   ...tseslint.configs.recommended,
45 |   {
46 |     rules: {
47 |       '@typescript-eslint/no-explicit-any': 'off',
48 |       '@typescript-eslint/no-unused-vars': 'off',
49 |       'no-empty': 'off',
50 |     },
51 |   },
52 |   pluginVue.configs['flat/essential'],
53 |   { files: ['**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser } } },
54 |   // Prettier configuration - must be placed last to override previous rules
55 |   prettierConfig,
56 | ]);
57 | 
```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/popup/components/builder/components/properties/PropertySwitchTab.vue:
--------------------------------------------------------------------------------

```vue
 1 | <template>
 2 |   <div class="form-section">
 3 |     <div class="form-group">
 4 |       <label class="form-label">Tab ID(可选)</label>
 5 |       <input
 6 |         class="form-input"
 7 |         type="number"
 8 |         v-model.number="(node as any).config.tabId"
 9 |         placeholder="数字"
10 |       />
11 |     </div>
12 |     <div class="form-group" :class="{ invalid: needOne && !hasAny }">
13 |       <label class="form-label">URL 包含(可选)</label>
14 |       <input class="form-input" v-model="(node as any).config.urlContains" placeholder="子串匹配" />
15 |     </div>
16 |     <div class="form-group" :class="{ invalid: needOne && !hasAny }">
17 |       <label class="form-label">标题包含(可选)</label>
18 |       <input
19 |         class="form-input"
20 |         v-model="(node as any).config.titleContains"
21 |         placeholder="子串匹配"
22 |       />
23 |     </div>
24 |     <div
25 |       v-if="needOne && !hasAny"
26 |       class="text-xs text-slate-500"
27 |       style="padding: 0 20px; color: var(--rr-danger)"
28 |       >需提供 tabId 或 URL/标题包含</div
29 |     >
30 |   </div>
31 | </template>
32 | 
33 | <script lang="ts" setup>
34 | /* eslint-disable vue/no-mutating-props */
35 | import { computed } from 'vue';
36 | import type { NodeBase } from '@/entrypoints/background/record-replay/types';
37 | 
38 | const props = defineProps<{ node: NodeBase }>();
39 | const needOne = true;
40 | const hasAny = computed(() => {
41 |   const c: any = (props.node as any).config || {};
42 |   return !!(c.tabId || c.urlContains || c.titleContains);
43 | });
44 | </script>
45 | 
46 | <style scoped></style>
47 | 
```
Page 1/60FirstPrevNextLast