diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml
index c4e41a1..dc4ab55 100644
--- a/.github/workflows/deploy-demo.yml
+++ b/.github/workflows/deploy-demo.yml
@@ -28,7 +28,7 @@ jobs:
run: npm run build:website
- name: Setup Pages
- uses: actions/configure-pages@v5
+ uses: actions/configure-pages@v6
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
@@ -37,4 +37,4 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
- uses: actions/deploy-pages@v4
+ uses: actions/deploy-pages@v5
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3599e02..ca044ae 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,42 +2,13 @@
♥️ We welcome contributions from everyone.
-## 🚀 Quick Start
-
-### Development Setup
-
-1. **Prerequisites**
- - `macOS` / `Linux` / `WSL`
- - `node.js >= 20` with `npm >= 10`
- - An editor that supports `ts/eslint/prettier`
- - Make sure `eslint`, `prettier` and `commitlint` work well. Un-linted code won't pass the CI.
-
-2. **Setup**
-
- ```bash
- npm i
- npm start # Start demo and documentation site
- npm run build # Build libs and website
- ```
-
-### Project Structure
-
-This is a **monorepo** with npm workspaces containing **4 main packages**:
-
-- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm
-- **Core** (`packages/core/`) - Core agent logic without UI (npm: `@page-agent/core`)
-- **Extension** (`packages/extension/`) - Chrome extension for multi-page tasks and browser-level automation
-- **Website** (`packages/website/`) - React documentation and landing page. Also as demo and test page for the core lib. private package `@page-agent/website`
-
-> We use a simplified monorepo solution with `native npm-workspace + ts reference + vite alias`. No fancy tooling. Hoisting is required.
->
-> - When developing. Use alias so that we don't have to pre-build.
-> - When bundling. Use external and disable ts `paths` alias.
-> - When bundling `IIFE` and `Website`. Bundle everything together.
+For local development workflows, setup, local LLM config, extension development, testing on other websites, and more details, see [docs/developer-guide.md](docs/developer-guide.md).
## 🤝 How to Contribute
-### Reporting Issues
+> **[Maintainer's Note](https://github.com/alibaba/page-agent/issues/349)**
+
+### Opening Issues
- Use the GitHub issue tracker to report bugs or request features
- Search existing issues before creating new ones
@@ -46,147 +17,24 @@ This is a **monorepo** with npm workspaces containing **4 main packages**:
### Code Contributions
-1. **Fork and Clone**
-
- ```bash
- git clone https://github.com/your-username/page-agent.git
- cd page-agent
- ```
-
-2. **Create Feature Branch**
-
- ```bash
- git checkout -b feat/your-feature-name
- ```
-
-3. **Make Changes**
- - Follow existing code style and patterns
- - Add tests for new functionality
- - Update documentation as needed
-
-4. **Test Your Changes**
- - Build and lint everything.
- - Test in our demo website
- - Test it on other websites if applicable
- - `@TODO: test suite`
-
-5. **Commit and Push**
-
- ```bash
- git add .
- git commit -m "feat: add awesome feature"
- git push origin feat/your-feature-name
- ```
-
-6. **Create Pull Request**
- - Provide clear description of changes
- - Link related issues
- - Include screenshots for UI changes
-
-## 📝 Code Style
-
-### General Guidelines
-
-- Use TypeScript for type safety
-- Follow existing naming conventions
-- Write meaningful commit messages
-- Keep functions small and focused
-- Add JSDoc for public APIs
+1. Follow existing code style and patterns
+2. Update documentation as needed
+3. Add JSDoc for public APIs
+4. Build and lint everything
+5. Test in our demo website, and on other websites if applicable
+6. Include screenshots for UI changes
### Vibe Coding with AI
-> [Vibe coding](https://en.wikipedia.org/wiki/Vibe_coding)
-
+- Vibe coding is **NOT** allowed for the core lib or the extension!!!
- Vibe coding is **RECOMMENDED** when maintaining **the demo, the website, the UI and tests**.
- - We have a [website/AGENTS.md](packages/website/AGENTS.md) for that.
-- Vibe coding is **NOT** allowed for the core lib!!!
-- NEVER try to vibe coding the MV3 extension!!! It is HELL.
+- Make sure your AI references `AGENTS.md` and `website/AGENTS.md` for better quality.
- Review anything AI wrote before make a commit. You are the author of anything you commit. NOT AI.
-If your AI assistant does not support [AGENTS.md](https://agents.md/). Add an alias for it:
-
-- claude-code (`CLAUDE.md`)
-
- ```markdown
- @AGENTS.md
- ```
-
-- antigravity (`.agent/rules/alias.md`)
-
- ```markdown
- ---
- trigger: always_on
- ---
-
- @../../AGENTS.md
- ```
-
-## 🔧 Development Workflows
-
-### Test With Your Own LLM API
-
-- Create a `.env` file in the repo root with your LLM API config
-
- ```env
- LLM_MODEL_NAME=gpt-5.2
- LLM_API_KEY=your-api-key
- LLM_BASE_URL=https://api.your-llm-provider.com/v1
- ```
-
-- **Ollama example** (tested on 0.15 + qwen3:14b, RTX3090 24GB):
-
- ```env
- LLM_BASE_URL="http://localhost:11434/v1"
- LLM_API_KEY="NA"
- LLM_MODEL_NAME="qwen3:14b"
- ```
-
- > @see https://alibaba.github.io/page-agent/docs/features/models#ollama for configuration
-
-- **Restart the dev server** to load new env vars
-- If not provided, the demo will use the free testing proxy by default. By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
-
-### Extension Development
-
-```bash
-# make sure you ran `npm run build:libs` first
-# and every time you changed the core libs
-npm run dev -w @page-agent/ext
-npm run zip -w @page-agent/ext
-```
-
-- Update `packages/extension/docs/extension_api.md` for API integration details
-
-### Testing on Other Websites
-
-- Start and serve a local `iife` script
-
- ```bash
- npm run dev:demo # Serving IIFE with auto rebuild at http://localhost:5174/page-agent.demo.js
- ```
-
-- Add a new bookmark
-
- ```javascript
- javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5174/page-agent.demo.js?t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
- ```
-
-- Click the bookmark on any page to load Page-Agent
-
-> Warning: AK in your local `.env` will be inlined in the iife script. Be very careful when you distribute the script.
-
-### Adding Documentation
-
-Ask an AI to help you add documentation to the `website/` package. Follow the existing style.
-
-> Our AGENTS.md file and guardrails are designed for this purpose. But please be careful and review anything AI generated.
-
## 🚫 What We Don't Accept
- Breaking changes and large PRs without prior discussion
- Heavy dependencies to core libs
-- Contributions without proper testing
-- Code that doesn't follow project conventions
- Dependencies or code with licenses incompatible with MIT
- Bot or AI-generated pull requests without meaningful human involvement
@@ -194,12 +42,6 @@ Ask an AI to help you add documentation to the `website/` package. Follow the ex
By contributing to this project, you agree that your contributions will be licensed under the MIT License.
-> CLA is optional.
-
-## 💬 Questions?
-
-- Open a GitHub issue for technical questions
-- Check existing documentation and issues first
-- Be respectful and constructive in discussions
+---
Thank you for helping make PageAgent better! 🎉
diff --git a/README.md b/README.md
index 7a9dd89..05824f4 100644
--- a/README.md
+++ b/README.md
@@ -25,15 +25,16 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
- **📖 Text-based DOM manipulation**
- No screenshots. No multi-modal LLMs or special permissions needed.
- **🧠 Bring your own LLMs**
-- **🎨 Pretty UI with human-in-the-loop**
- **🐙 Optional [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension) for multi-page tasks.**
+ - And an [MCP Server (Beta)](https://alibaba.github.io/page-agent/docs/features/mcp-server) to control it from outside
## 💡 Use Cases
- **SaaS AI Copilot** — Ship an AI copilot in your product in lines of code. No backend rewrite.
- **Smart Form Filling** — Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.
- **Accessibility** — Make any web app accessible through natural language. Voice commands, screen readers, zero barrier.
-- **Multi-page Agent** — Extend your own agent's reach across browser tabs with the optional [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension).
+- **Multi-page Agent** — Extend your own web agent's reach across browser tabs [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension).
+- **MCP** - Allow your agent clients to control your browser.
## 🚀 Quick Start
@@ -49,8 +50,8 @@ Fastest way to try PageAgent with our free Demo LLM:
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
-| Global | https://cdn.jsdelivr.net/npm/page-agent@1.6.0/dist/iife/page-agent.demo.js |
-| China | https://registry.npmmirror.com/page-agent/1.6.0/files/dist/iife/page-agent.demo.js |
+| Global | https://cdn.jsdelivr.net/npm/page-agent@1.7.0/dist/iife/page-agent.demo.js |
+| China | https://registry.npmmirror.com/page-agent/1.7.0/files/dist/iife/page-agent.demo.js |
### NPM Installation
@@ -75,11 +76,15 @@ For more programmatic usage, see [📖 Documentations](https://alibaba.github.io
## 🤝 Contributing
-We welcome contributions from the community! Follow our instructions in [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines.
+We welcome contributions from the community! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/developer-guide.md](docs/developer-guide.md) for local development workflows.
-Please read [Code of Conduct](docs/CODE_OF_CONDUCT.md) before contributing.
+Please read the [maintainer's note](https://github.com/alibaba/page-agent/issues/349) on principles and current state.
-Contributions generated entirely by bots or agents without substantial human involvement will not be accepted, and bot accounts may be blocked.
+Contributions generated entirely by **bots or AI** without substantial human involvement will **not be accepted**.
+
+## ⚖️ License
+
+[MIT License](LICENSE)
## 👏 Acknowledgments
@@ -97,23 +102,18 @@ Licensed under the MIT License
We gratefully acknowledge the browser-use project and its contributors for their
excellent work on web automation and DOM interaction patterns that helped make
this project possible.
-
-Third-party dependencies and their licenses can be found in the package.json
-file and in the node_modules directory after installation.
```
-## 📄 License
+## 🌟 Awesome Page Agent
-[MIT License](LICENSE)
+Built something cool with PageAgent? Add it here! Open a PR to share your project.
+
+> These are community projects — not maintained or endorsed by us. Use at your own discretion.
+
+| Project | Description |
+| -------- | ----------------------------------------------------------- |
+| _Yours?_ | [Open a PR](https://github.com/alibaba/page-agent/pulls) 🙌 |
---
**⭐ Star this repo if you find PageAgent helpful!**
-
-
-
-
-
-
-
-
diff --git a/docs/README-zh.md b/docs/README-zh.md
index 9f6a166..1a025b8 100644
--- a/docs/README-zh.md
+++ b/docs/README-zh.md
@@ -20,20 +20,20 @@
## ✨ Features
- **🎯 轻松集成**
- - 无需 `浏览器插件` / `Python` / `无头浏览器`。
- - 纯页面内 JavaScript,一切都在你的网页中完成。
+ - 无需 `浏览器插件` / `Python` / `无头浏览器`,纯页面内 JavaScript
- **📖 基于文本的 DOM 操作**
- - 无需截图,无需多模态模型或特殊权限。
-- **🧠 用你自己的 LLM**
-- **🎨 精美 UI,支持人机协同**
-- **🐙 可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),支持跨页面任务。**
+ - 无需截图,无需多模态模型或特殊权限
+- **🧠 自备 LLM**
+- 🐙 可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),支持跨页面任务
+ - [MCP Server (Beta)](https://alibaba.github.io/page-agent/docs/features/mcp-server)
## 💡 应用场景
-- **SaaS AI 副驾驶** — 几行代码为你的产品加上 AI 副驾驶,无需重写后端。
+- **SaaS AI Copilot** — 几行代码为你的产品加上 AI 副驾驶,无需重写后端。
- **智能表单填写** — 把 20 次点击变成一句话。ERP、CRM、管理后台的最佳拍档。
- **无障碍增强** — 用自然语言让任何网页无障碍。语音指令、屏幕阅读器,零门槛。
-- **跨页面 Agent** — 通过可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),让你自己的 Agent 跨标签页工作。
+- **跨页面 Agent** — 通过可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),让你自己的 Web Agent 跨标签页工作。
+- 通过 MCP 为现有 Agent 加入浏览器控制能力。
## 🚀 快速开始
@@ -49,8 +49,8 @@
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
-| Global | https://cdn.jsdelivr.net/npm/page-agent@1.6.0/dist/iife/page-agent.demo.js |
-| China | https://registry.npmmirror.com/page-agent/1.6.0/files/dist/iife/page-agent.demo.js |
+| Global | https://cdn.jsdelivr.net/npm/page-agent@1.7.0/dist/iife/page-agent.demo.js |
+| China | https://registry.npmmirror.com/page-agent/1.7.0/files/dist/iife/page-agent.demo.js |
### NPM 安装
@@ -75,11 +75,13 @@ await agent.execute('点击登录按钮')
## 🤝 贡献
-欢迎社区贡献!请参阅 [CONTRIBUTING.md](../CONTRIBUTING.md) 了解安装与贡献指南。请在贡献前阅读[行为准则](CODE_OF_CONDUCT.md)。
+欢迎社区贡献!请参阅 [CONTRIBUTING.md](../CONTRIBUTING.md) 了解安装与贡献指南。
-我们不接受未经实质性人类参与、完全由 Bot 或 Agent 自动生成的代码,机器人账号可能被禁止参与互动。
+提交 issue 或 PR 之前,请先阅读[作者声明](https://github.com/alibaba/page-agent/issues/349)和[行为准则](CODE_OF_CONDUCT.md)。
-## 👏 致谢
+我们不接受未经实质性人类参与、完全由 Bot 或 Agent 自动生成的代码。
+
+## 👏 声明与致谢
本项目基于 **[`browser-use`](https://github.com/browser-use/browser-use)** 的优秀工作构建。
@@ -95,12 +97,9 @@ Licensed under the MIT License
We gratefully acknowledge the browser-use project and its contributors for their
excellent work on web automation and DOM interaction patterns that helped make
this project possible.
-
-Third-party dependencies and their licenses can be found in the package.json
-file and in the node_modules directory after installation.
```
-## 📄 许可证
+## ⚖️ 许可证
[MIT License](../LICENSE)
@@ -108,10 +107,3 @@ file and in the node_modules directory after installation.
**⭐ 如果觉得 PageAgent 有用或有趣,请给项目点个星!**
-
-
-
-
-
-
-
diff --git a/SECURITY.md b/docs/SECURITY.md
similarity index 100%
rename from SECURITY.md
rename to docs/SECURITY.md
diff --git a/docs/developer-guide.md b/docs/developer-guide.md
new file mode 100644
index 0000000..8e34ce0
--- /dev/null
+++ b/docs/developer-guide.md
@@ -0,0 +1,117 @@
+# Developer Guide
+
+This file is for local development workflows.
+
+For contribution rules and expectations, see [../CONTRIBUTING.md](../CONTRIBUTING.md).
+
+## 🚀 Quick Start
+
+### Development Setup
+
+1. **Prerequisites**
+ - `macOS` / `Linux` / `WSL`
+ - `node.js >= 20` with `npm >= 10`
+ - An editor that supports `ts/eslint/prettier`
+ - Make sure `eslint`, `prettier` and `commitlint` work well. Un-linted code won't pass the CI.
+
+2. **Setup**
+
+ ```bash
+ npm i
+ npm start # Start demo and documentation site
+ npm run build # Build libs and website
+ ```
+
+## 📦 Project Structure
+
+This is a **monorepo** with npm workspaces containing **4 main packages**:
+
+- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm
+- **Core** (`packages/core/`) - Core agent logic without UI (npm: `@page-agent/core`)
+- **Extension** (`packages/extension/`) - Chrome extension for multi-page tasks and browser-level automation
+- **Website** (`packages/website/`) - React documentation and landing page. Also as demo and test page for the core lib. private package `@page-agent/website`
+
+> We use a simplified monorepo solution with `native npm-workspace + ts reference + vite alias`. No fancy tooling. Hoisting is required.
+>
+> - When developing. Use alias so that we don't have to pre-build.
+> - When bundling. Use external and disable ts `paths` alias.
+> - When bundling `IIFE` and `Website`. Bundle everything together.
+
+## 🤖 AGENTS.md Alias
+
+If your AI assistant does not support [AGENTS.md](https://agents.md/). Add an alias for it:
+
+- claude-code (`CLAUDE.md`)
+
+ ```markdown
+ @AGENTS.md
+ ```
+
+- antigravity (`.agent/rules/alias.md`)
+
+ ```markdown
+ ---
+ trigger: always_on
+ ---
+
+ @../../AGENTS.md
+ ```
+
+## 🔧 Development Workflows
+
+### Test With Your Own LLM API
+
+- Create a `.env` file in the repo root with your LLM API config
+
+ ```env
+ LLM_MODEL_NAME=gpt-5.2
+ LLM_API_KEY=your-api-key
+ LLM_BASE_URL=https://api.your-llm-provider.com/v1
+ ```
+
+- **Ollama example** (tested on 0.15 + qwen3:14b, RTX3090 24GB):
+
+ ```env
+ LLM_BASE_URL="http://localhost:11434/v1"
+ LLM_API_KEY="NA"
+ LLM_MODEL_NAME="qwen3:14b"
+ ```
+
+ > @see https://alibaba.github.io/page-agent/docs/features/models#ollama for configuration
+
+- **Restart the dev server** to load new env vars
+- If not provided, the demo will use the free testing proxy by default. By using it, you agree to its [terms](./terms-and-privacy.md).
+
+### Extension Development
+
+```bash
+# make sure you ran `npm run build:libs` first and every time you changed the core libs
+npm run dev -w @page-agent/ext
+npm run zip -w @page-agent/ext
+```
+
+- Update `packages/extension/docs/extension_api.md` for API integration details
+
+### Testing on Other Websites
+
+- Start and serve a local `iife` script
+
+ ```bash
+ npm run dev:demo # Serving IIFE with auto rebuild at http://localhost:5174/page-agent.demo.js
+ ```
+
+- Add a new bookmark
+
+ ```javascript
+ javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5174/page-agent.demo.js?t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
+ ```
+
+- Click the bookmark on any page to load Page-Agent
+
+> Warning: AK in your local `.env` will be inlined in the iife script. Be very careful when you distribute the script.
+
+### Adding Documentation
+
+Ask an AI to help you add documentation to the `website/` package. Follow the existing style.
+
+> Our AGENTS.md file and guardrails are designed for this purpose. But please be careful and review anything AI generated.
diff --git a/package-lock.json b/package-lock.json
index 1ed40cf..3799cd9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "root",
- "version": "1.6.0",
+ "version": "1.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "root",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"workspaces": [
"packages/page-controller",
@@ -23,7 +23,7 @@
"@commitlint/config-conventional": "^20.5.0",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.57.7",
- "@tailwindcss/vite": "^4.2.1",
+ "@tailwindcss/vite": "^4.2.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0",
"@vitejs/plugin-react-swc": "^4.3.0",
@@ -41,7 +41,7 @@
"lint-staged": "^16.4.0",
"prettier": "^3.8.0",
"typescript": "^5.9.3",
- "typescript-eslint": "^8.57.1",
+ "typescript-eslint": "^8.58.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.1",
"vite-bundle-analyzer": "^1.3.6",
@@ -1819,9 +1819,9 @@
}
},
"node_modules/@modelcontextprotocol/sdk": {
- "version": "1.27.1",
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz",
- "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==",
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
+ "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==",
"license": "MIT",
"dependencies": {
"@hono/node-server": "^1.19.9",
@@ -3352,49 +3352,49 @@
}
},
"node_modules/@tailwindcss/node": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz",
- "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz",
+ "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.5",
"enhanced-resolve": "^5.19.0",
"jiti": "^2.6.1",
- "lightningcss": "1.31.1",
+ "lightningcss": "1.32.0",
"magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
- "tailwindcss": "4.2.1"
+ "tailwindcss": "4.2.2"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz",
- "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz",
+ "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.2.1",
- "@tailwindcss/oxide-darwin-arm64": "4.2.1",
- "@tailwindcss/oxide-darwin-x64": "4.2.1",
- "@tailwindcss/oxide-freebsd-x64": "4.2.1",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1",
- "@tailwindcss/oxide-linux-arm64-musl": "4.2.1",
- "@tailwindcss/oxide-linux-x64-gnu": "4.2.1",
- "@tailwindcss/oxide-linux-x64-musl": "4.2.1",
- "@tailwindcss/oxide-wasm32-wasi": "4.2.1",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1",
- "@tailwindcss/oxide-win32-x64-msvc": "4.2.1"
+ "@tailwindcss/oxide-android-arm64": "4.2.2",
+ "@tailwindcss/oxide-darwin-arm64": "4.2.2",
+ "@tailwindcss/oxide-darwin-x64": "4.2.2",
+ "@tailwindcss/oxide-freebsd-x64": "4.2.2",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.2.2",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.2.2",
+ "@tailwindcss/oxide-linux-x64-musl": "4.2.2",
+ "@tailwindcss/oxide-wasm32-wasi": "4.2.2",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.2.2"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz",
- "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz",
+ "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==",
"cpu": [
"arm64"
],
@@ -3409,9 +3409,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz",
- "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz",
+ "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==",
"cpu": [
"arm64"
],
@@ -3426,9 +3426,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz",
- "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz",
+ "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==",
"cpu": [
"x64"
],
@@ -3443,9 +3443,9 @@
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz",
- "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz",
+ "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==",
"cpu": [
"x64"
],
@@ -3460,9 +3460,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz",
- "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz",
+ "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==",
"cpu": [
"arm"
],
@@ -3477,9 +3477,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz",
- "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz",
+ "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==",
"cpu": [
"arm64"
],
@@ -3494,9 +3494,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz",
- "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz",
+ "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==",
"cpu": [
"arm64"
],
@@ -3511,9 +3511,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz",
- "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz",
+ "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==",
"cpu": [
"x64"
],
@@ -3528,9 +3528,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz",
- "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz",
+ "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==",
"cpu": [
"x64"
],
@@ -3545,9 +3545,9 @@
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz",
- "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz",
+ "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
@@ -3639,9 +3639,9 @@
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz",
- "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz",
+ "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==",
"cpu": [
"arm64"
],
@@ -3656,9 +3656,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz",
- "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz",
+ "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==",
"cpu": [
"x64"
],
@@ -3673,18 +3673,18 @@
}
},
"node_modules/@tailwindcss/vite": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz",
- "integrity": "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz",
+ "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@tailwindcss/node": "4.2.1",
- "@tailwindcss/oxide": "4.2.1",
- "tailwindcss": "4.2.1"
+ "@tailwindcss/node": "4.2.2",
+ "@tailwindcss/oxide": "4.2.2",
+ "tailwindcss": "4.2.2"
},
"peerDependencies": {
- "vite": "^5.2.0 || ^6 || ^7"
+ "vite": "^5.2.0 || ^6 || ^7 || ^8"
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
@@ -3807,9 +3807,9 @@
}
},
"node_modules/@types/chrome": {
- "version": "0.1.37",
- "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.37.tgz",
- "integrity": "sha512-IJE4ceuDO7lrEuua7Pow47zwNcI8E6qqkowRP7aFPaZ0lrjxh6y836OPqqkIZeTX64FTogbw+4RNH0+QrweCTQ==",
+ "version": "0.1.38",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.38.tgz",
+ "integrity": "sha512-5aK4m9wZqoWAoB98aElESLm/5pXpqJnFWMNoiCs/XdPsXR6wNdVkJFSdQ9Wr4PnTuUrxD0SuNuDHh3EG5QeBzA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3893,20 +3893,20 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz",
- "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
+ "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.57.1",
- "@typescript-eslint/type-utils": "8.57.1",
- "@typescript-eslint/utils": "8.57.1",
- "@typescript-eslint/visitor-keys": "8.57.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/type-utils": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.4.0"
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3916,9 +3916,9 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.57.1",
+ "@typescript-eslint/parser": "^8.58.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
@@ -3932,16 +3932,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz",
- "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
+ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.57.1",
- "@typescript-eslint/types": "8.57.1",
- "@typescript-eslint/typescript-estree": "8.57.1",
- "@typescript-eslint/visitor-keys": "8.57.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3953,18 +3953,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz",
- "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
+ "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.57.1",
- "@typescript-eslint/types": "^8.57.1",
+ "@typescript-eslint/tsconfig-utils": "^8.58.0",
+ "@typescript-eslint/types": "^8.58.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3975,18 +3975,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz",
- "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
+ "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.57.1",
- "@typescript-eslint/visitor-keys": "8.57.1"
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3997,9 +3997,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz",
- "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
+ "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4010,21 +4010,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz",
- "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
+ "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.57.1",
- "@typescript-eslint/typescript-estree": "8.57.1",
- "@typescript-eslint/utils": "8.57.1",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
"debug": "^4.4.3",
- "ts-api-utils": "^2.4.0"
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4035,13 +4035,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz",
- "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4053,21 +4053,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz",
- "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.57.1",
- "@typescript-eslint/tsconfig-utils": "8.57.1",
- "@typescript-eslint/types": "8.57.1",
- "@typescript-eslint/visitor-keys": "8.57.1",
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.4.0"
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4077,20 +4077,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz",
- "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
+ "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.57.1",
- "@typescript-eslint/types": "8.57.1",
- "@typescript-eslint/typescript-estree": "8.57.1"
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4101,17 +4101,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz",
- "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.57.1",
+ "@typescript-eslint/types": "8.58.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -4237,9 +4237,9 @@
"license": "MIT"
},
"node_modules/@wxt-dev/browser": {
- "version": "0.1.37",
- "resolved": "https://registry.npmjs.org/@wxt-dev/browser/-/browser-0.1.37.tgz",
- "integrity": "sha512-I32XWCNRy2W6UgbaVXz8BHGBGtm8urGRRBrcNLagUBXTrBi7wCE6zWePUvvK+nUl7qUCZ7iQ1ufdP0c1DEWisw==",
+ "version": "0.1.38",
+ "resolved": "https://registry.npmjs.org/@wxt-dev/browser/-/browser-0.1.38.tgz",
+ "integrity": "sha512-Y9nUfNOMqgsoO8KQ1BssrwzHEmrSr/2pUowAG4Wcr9EyKyhOK7mC7Vdyj2kXAmp5NOUXHjhghzJ6qIb5h+RbCA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5851,9 +5851,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.19.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
- "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "version": "5.20.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
+ "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6676,13 +6676,13 @@
}
},
"node_modules/framer-motion": {
- "version": "12.37.0",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.37.0.tgz",
- "integrity": "sha512-j/QUcZS9Nw3NzZWoAbkzr3ETRFHyVeQMlGOUYUmG15U+uiyn9DqIktYruVPDcqY8I35qYR70JaZBvFmS6p+Pdg==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
+ "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.37.0",
+ "motion-dom": "^12.38.0",
"motion-utils": "^12.36.0",
"tslib": "^2.4.0"
},
@@ -7821,9 +7821,9 @@
}
},
"node_modules/lightningcss": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
- "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
@@ -7837,23 +7837,23 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "lightningcss-android-arm64": "1.31.1",
- "lightningcss-darwin-arm64": "1.31.1",
- "lightningcss-darwin-x64": "1.31.1",
- "lightningcss-freebsd-x64": "1.31.1",
- "lightningcss-linux-arm-gnueabihf": "1.31.1",
- "lightningcss-linux-arm64-gnu": "1.31.1",
- "lightningcss-linux-arm64-musl": "1.31.1",
- "lightningcss-linux-x64-gnu": "1.31.1",
- "lightningcss-linux-x64-musl": "1.31.1",
- "lightningcss-win32-arm64-msvc": "1.31.1",
- "lightningcss-win32-x64-msvc": "1.31.1"
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
}
},
"node_modules/lightningcss-android-arm64": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz",
- "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
"cpu": [
"arm64"
],
@@ -7872,9 +7872,9 @@
}
},
"node_modules/lightningcss-darwin-arm64": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz",
- "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
"cpu": [
"arm64"
],
@@ -7893,9 +7893,9 @@
}
},
"node_modules/lightningcss-darwin-x64": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz",
- "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
"cpu": [
"x64"
],
@@ -7914,9 +7914,9 @@
}
},
"node_modules/lightningcss-freebsd-x64": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz",
- "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
"cpu": [
"x64"
],
@@ -7935,9 +7935,9 @@
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz",
- "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
"cpu": [
"arm"
],
@@ -7956,9 +7956,9 @@
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz",
- "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
"cpu": [
"arm64"
],
@@ -7977,9 +7977,9 @@
}
},
"node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz",
- "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
"cpu": [
"arm64"
],
@@ -7998,9 +7998,9 @@
}
},
"node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz",
- "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
"cpu": [
"x64"
],
@@ -8019,9 +8019,9 @@
}
},
"node_modules/lightningcss-linux-x64-musl": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz",
- "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
"cpu": [
"x64"
],
@@ -8040,9 +8040,9 @@
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz",
- "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
"cpu": [
"arm64"
],
@@ -8061,9 +8061,9 @@
}
},
"node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.31.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz",
- "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
"cpu": [
"x64"
],
@@ -8296,9 +8296,9 @@
}
},
"node_modules/lucide-react": {
- "version": "0.577.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz",
- "integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz",
+ "integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==",
"dev": true,
"license": "ISC",
"peerDependencies": {
@@ -8521,13 +8521,13 @@
}
},
"node_modules/motion": {
- "version": "12.37.0",
- "resolved": "https://registry.npmjs.org/motion/-/motion-12.37.0.tgz",
- "integrity": "sha512-Ph6oyO5hGSIAPjDsqwchEP+EKXjyFK0ci6FTIFBbx+qaMl8zLzLzPLzd9q3DKhAHcvnV7LxQonMyA+FyAv9+gA==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
+ "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "framer-motion": "^12.37.0",
+ "framer-motion": "^12.38.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -8548,9 +8548,9 @@
}
},
"node_modules/motion-dom": {
- "version": "12.37.0",
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.37.0.tgz",
- "integrity": "sha512-LnppZuwX1jQizRWTl9LBLMN3RbAEmdQkX/2Af0UW70NCqYJI/7GfI83vQP9Ucel/Avc0Tf2ZWy8FHawuc0O6Vg==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
+ "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10114,9 +10114,9 @@
}
},
"node_modules/simple-icons": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.12.0.tgz",
- "integrity": "sha512-fDJDqXUpkb2twqH+eBQpJsCYUE6jEH7VkuuPL9dH16sbLf6KKnwyijULmcx7SCoy3c2L6pl8WCzt+4rpYjoWfw==",
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.14.0.tgz",
+ "integrity": "sha512-2Nvs3jJpCfMWQerD4zdv91g/MpnWn81a7uhyAC0reuhrjmS2MtSmwIKwewOJR6Xe97ZmfltDntCDqKJIBawQOw==",
"dev": true,
"funding": [
{
@@ -10424,16 +10424,16 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
- "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz",
+ "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
- "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz",
+ "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10549,9 +10549,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
- "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10683,16 +10683,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz",
- "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
+ "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.57.1",
- "@typescript-eslint/parser": "8.57.1",
- "@typescript-eslint/typescript-estree": "8.57.1",
- "@typescript-eslint/utils": "8.57.1"
+ "@typescript-eslint/eslint-plugin": "8.58.0",
+ "@typescript-eslint/parser": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10703,7 +10703,7 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/ufo": {
@@ -11451,9 +11451,9 @@
"license": "ISC"
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -11489,9 +11489,9 @@
}
},
"node_modules/wxt": {
- "version": "0.20.19",
- "resolved": "https://registry.npmjs.org/wxt/-/wxt-0.20.19.tgz",
- "integrity": "sha512-LNQXDyStuenNSLLbSs3aXDscKB6g6NYUXppBu7uAmIUZNKLy04Hyg3EE9p9w683t0B+j2CBYciDmqglfwisNuA==",
+ "version": "0.20.20",
+ "resolved": "https://registry.npmjs.org/wxt/-/wxt-0.20.20.tgz",
+ "integrity": "sha512-OGvOD1YEXwasjlOmfYzCGlIa88Jm9mxjM+hqx7zw+Xctg+TKjhF1bIt7vVJ1oT1t4RqvczTAcD2mUduiDltZaw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11500,7 +11500,7 @@
"@webext-core/fake-browser": "^1.3.4",
"@webext-core/isolated-element": "^1.1.3",
"@webext-core/match-patterns": "^1.0.3",
- "@wxt-dev/browser": "^0.1.37",
+ "@wxt-dev/browser": "^0.1.38",
"@wxt-dev/storage": "^1.0.0",
"async-mutex": "^0.5.0",
"c12": "^3.3.3",
@@ -11537,7 +11537,7 @@
"tinyglobby": "^0.2.15",
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0",
"vite": "^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0",
- "vite-node": "^3.2.4 || ^5.0.0",
+ "vite-node": "^3.2.4 || ^5.0.0 || ^6.0.0",
"web-ext-run": "^0.2.4"
},
"bin": {
@@ -11799,11 +11799,11 @@
},
"packages/core": {
"name": "@page-agent/core",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"dependencies": {
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0",
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0",
"chalk": "^5.6.2"
},
"devDependencies": {
@@ -11815,13 +11815,13 @@
},
"packages/extension": {
"name": "@page-agent/ext",
- "version": "1.6.0",
+ "version": "1.7.0",
"hasInstallScript": true,
"dependencies": {
- "@page-agent/core": "1.6.0",
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0",
- "@page-agent/ui": "1.6.0",
+ "@page-agent/core": "1.7.0",
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0",
+ "@page-agent/ui": "1.7.0",
"ai-motion": "^0.4.8",
"chalk": "^5.6.2"
},
@@ -11832,25 +11832,25 @@
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
- "@types/chrome": "^0.1.37",
+ "@types/chrome": "^0.1.38",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.1",
"@wxt-dev/module-react": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb": "^8.0.3",
- "lucide-react": "^0.577.0",
- "motion": "^12.37.0",
+ "lucide-react": "^1.7.0",
+ "motion": "^12.38.0",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
- "simple-icons": "^16.12.0",
+ "simple-icons": "^16.14.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
- "wxt": "^0.20.19"
+ "wxt": "^0.20.20"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
@@ -11858,7 +11858,7 @@
},
"packages/llms": {
"name": "@page-agent/llms",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"dependencies": {
"chalk": "^5.6.2"
@@ -11872,11 +11872,11 @@
},
"packages/mcp": {
"name": "@page-agent/mcp",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.27.1",
- "ws": "^8.19.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
+ "ws": "^8.20.0",
"zod": "^4.3.5"
},
"bin": {
@@ -11887,13 +11887,13 @@
}
},
"packages/page-agent": {
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"dependencies": {
- "@page-agent/core": "1.6.0",
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0",
- "@page-agent/ui": "1.6.0",
+ "@page-agent/core": "1.7.0",
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0",
+ "@page-agent/ui": "1.7.0",
"chalk": "^5.6.2"
},
"devDependencies": {
@@ -11905,7 +11905,7 @@
},
"packages/page-controller": {
"name": "@page-agent/page-controller",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT",
"dependencies": {
"ai-motion": "^0.4.8"
@@ -11913,12 +11913,12 @@
},
"packages/ui": {
"name": "@page-agent/ui",
- "version": "1.6.0",
+ "version": "1.7.0",
"license": "MIT"
},
"packages/website": {
"name": "@page-agent/website",
- "version": "1.6.0",
+ "version": "1.7.0",
"devDependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8",
@@ -11929,13 +11929,13 @@
"@types/react-dom": "^19.2.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "lucide-react": "^0.577.0",
- "motion": "^12.37.0",
+ "lucide-react": "^1.7.0",
+ "motion": "^12.38.0",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
- "simple-icons": "^16.12.0",
+ "simple-icons": "^16.14.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
diff --git a/package.json b/package.json
index 21b827b..1aa2b4b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"workspaces": [
"packages/page-controller",
@@ -42,7 +42,7 @@
"@commitlint/config-conventional": "^20.5.0",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.57.7",
- "@tailwindcss/vite": "^4.2.1",
+ "@tailwindcss/vite": "^4.2.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0",
"@vitejs/plugin-react-swc": "^4.3.0",
@@ -60,7 +60,7 @@
"lint-staged": "^16.4.0",
"prettier": "^3.8.0",
"typescript": "^5.9.3",
- "typescript-eslint": "^8.57.1",
+ "typescript-eslint": "^8.58.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.1",
"vite-plugin-css-injected-by-js": "^4.0.1",
diff --git a/packages/core/package.json b/packages/core/package.json
index bca9c1c..455f2c5 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,7 +1,7 @@
{
"name": "@page-agent/core",
"private": false,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"main": "./dist/esm/page-agent-core.js",
"module": "./dist/esm/page-agent-core.js",
@@ -44,8 +44,8 @@
},
"dependencies": {
"chalk": "^5.6.2",
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0"
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
diff --git a/packages/extension/docs/extension_api.md b/packages/extension/docs/extension_api.md
index d49272d..1586660 100644
--- a/packages/extension/docs/extension_api.md
+++ b/packages/extension/docs/extension_api.md
@@ -118,9 +118,18 @@ export interface ExecuteConfig {
model: string
apiKey?: string
+ // Global system-level instructions for the agent.
+ // Equivalent to AgentConfig.instructions.system.
+ systemInstruction?: string
+
// Include the initial tab where page JS starts. Default: true.
includeInitialTab?: boolean
+ // Control all unpinned tabs in the window instead of only the tab group.
+ // When enabled, agent sees and can switch to every non-pinned tab.
+ // Default: false. Experimental.
+ experimentalIncludeAllTabs?: boolean
+
onStatusChange?: (status: AgentStatus) => void
onActivity?: (activity: AgentActivity) => void
onHistoryUpdate?: (history: HistoricalEvent[]) => void
@@ -207,7 +216,11 @@ interface ExecuteConfig {
baseURL: string
model: string
apiKey?: string
+
+ systemInstruction?: string
+
includeInitialTab?: boolean
+ experimentalIncludeAllTabs?: boolean
onStatusChange?: (status: AgentStatus) => void
onActivity?: (activity: AgentActivity) => void
onHistoryUpdate?: (history: HistoricalEvent[]) => void
diff --git a/packages/extension/package.json b/packages/extension/package.json
index d7e312e..1d129e3 100644
--- a/packages/extension/package.json
+++ b/packages/extension/package.json
@@ -1,7 +1,7 @@
{
"name": "@page-agent/ext",
"private": true,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"scripts": {
"dev": "wxt",
@@ -16,31 +16,31 @@
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
- "@types/chrome": "^0.1.37",
+ "@types/chrome": "^0.1.38",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.1",
"@wxt-dev/module-react": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb": "^8.0.3",
- "lucide-react": "^0.577.0",
- "motion": "^12.37.0",
+ "lucide-react": "^1.7.0",
+ "motion": "^12.38.0",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
- "simple-icons": "^16.12.0",
+ "simple-icons": "^16.14.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
- "wxt": "^0.20.19"
+ "wxt": "^0.20.20"
},
"dependencies": {
- "@page-agent/core": "1.6.0",
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0",
- "@page-agent/ui": "1.6.0",
+ "@page-agent/core": "1.7.0",
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0",
+ "@page-agent/ui": "1.7.0",
"ai-motion": "^0.4.8",
"chalk": "^5.6.2"
},
diff --git a/packages/extension/src/agent/MultiPageAgent.ts b/packages/extension/src/agent/MultiPageAgent.ts
index f2e7b0a..aa768bc 100644
--- a/packages/extension/src/agent/MultiPageAgent.ts
+++ b/packages/extension/src/agent/MultiPageAgent.ts
@@ -11,13 +11,18 @@ function detectLanguage(): 'en-US' | 'zh-CN' {
return lang.startsWith('zh') ? 'zh-CN' : 'en-US'
}
+interface MultiPageAgentConfig extends AgentConfig {
+ includeInitialTab?: boolean
+ experimentalIncludeAllTabs?: boolean
+}
+
/**
* MultiPageAgent
* - use with extension
* - can be used from a side panel or a content script
*/
export class MultiPageAgent extends PageAgentCore {
- constructor(config: AgentConfig & { includeInitialTab?: boolean }) {
+ constructor(config: MultiPageAgentConfig) {
// multi page controller
const tabsController = new TabsController()
const pageController = new RemotePageController(tabsController)
@@ -31,8 +36,8 @@ export class MultiPageAgent extends PageAgentCore {
`Default working language: **${targetLanguage}**`
)
- // include initial tab for controlling
const includeInitialTab = config.includeInitialTab ?? true
+ const experimentalIncludeAllTabs = config.experimentalIncludeAllTabs ?? false
/**
* When the agent is in side-panel and user closed the side-panel.
@@ -50,7 +55,7 @@ export class MultiPageAgent extends PageAgentCore {
customSystemPrompt: systemPrompt,
onBeforeTask: async (agent) => {
- await tabsController.init(agent.task, includeInitialTab)
+ await tabsController.init(agent.task, { includeInitialTab, experimentalIncludeAllTabs })
heartBeatInterval = window.setInterval(() => {
chrome.storage.local.set({
diff --git a/packages/extension/src/agent/RemotePageController.background.ts b/packages/extension/src/agent/RemotePageController.background.ts
index b75c4cb..8fb89ae 100644
--- a/packages/extension/src/agent/RemotePageController.background.ts
+++ b/packages/extension/src/agent/RemotePageController.background.ts
@@ -10,9 +10,7 @@ export function handlePageControlMessage(
): true | undefined {
const PREFIX = '[RemotePageController.background]'
- function debug(...messages: any[]) {
- console.debug(`\x1b[90m${PREFIX}\x1b[0m`, ...messages)
- }
+ const debug = console.debug.bind(console, `\x1b[90m${PREFIX}\x1b[0m`)
const { action, payload, targetTabId } = message
diff --git a/packages/extension/src/agent/RemotePageController.ts b/packages/extension/src/agent/RemotePageController.ts
index 3a35ddd..0c49f1b 100644
--- a/packages/extension/src/agent/RemotePageController.ts
+++ b/packages/extension/src/agent/RemotePageController.ts
@@ -4,9 +4,7 @@ import type { TabsController } from './TabsController'
const PREFIX = '[RemotePageController]'
-function debug(...messages: any[]) {
- console.debug(`\x1b[90m${PREFIX}\x1b[0m`, ...messages)
-}
+const debug = console.debug.bind(console, `\x1b[90m${PREFIX}\x1b[0m`)
function sendMessage(message: {
type: 'PAGE_CONTROL'
diff --git a/packages/extension/src/agent/TabsController.background.ts b/packages/extension/src/agent/TabsController.background.ts
index 39c628a..5b4baff 100644
--- a/packages/extension/src/agent/TabsController.background.ts
+++ b/packages/extension/src/agent/TabsController.background.ts
@@ -5,9 +5,7 @@ import type { TabAction } from './TabsController'
const PREFIX = '[TabsController.background]'
-function debug(...messages: any[]) {
- console.debug(`\x1b[90m${PREFIX}\x1b[0m`, ...messages)
-}
+const debug = console.debug.bind(console, `\x1b[90m${PREFIX}\x1b[0m`)
export function handleTabControlMessage(
message: { type: 'TAB_CONTROL'; action: TabAction; payload: any },
@@ -20,11 +18,10 @@ export function handleTabControlMessage(
case 'get_active_tab': {
debug('get_active_tab')
chrome.tabs
- .query({ active: true, currentWindow: true })
+ .query({ active: true })
.then((tabs) => {
- const tabId = tabs.length > 0 ? tabs[0].id || null : null
- debug('get_active_tab: success', tabId)
- sendResponse({ success: true, tabId })
+ debug('get_active_tab: success', tabs)
+ sendResponse({ success: true, tab: tabs[0] })
})
.catch((error) => {
sendResponse({ error: error instanceof Error ? error.message : String(error) })
@@ -63,7 +60,7 @@ export function handleTabControlMessage(
case 'create_tab_group': {
debug('create_tab_group', payload)
chrome.tabs
- .group({ tabIds: payload.tabIds })
+ .group({ tabIds: payload.tabIds, createProperties: { windowId: payload.windowId } })
.then((groupId) => {
debug('create_tab_group: success', groupId)
sendResponse({ success: true, groupId })
@@ -114,47 +111,59 @@ export function handleTabControlMessage(
return true // async response
}
+ case 'get_window_tabs': {
+ debug('get_window_tabs', payload)
+ chrome.tabs
+ .query({ windowId: payload.windowId })
+ .then((tabs) => {
+ sendResponse({ success: true, tabs })
+ })
+ .catch((error) => {
+ sendResponse({ error: error instanceof Error ? error.message : String(error) })
+ })
+ return true
+ }
+
default:
sendResponse({ error: `Unknown action: ${action}` })
return
}
}
-export function setupTabChangeEvents() {
- console.log('[TabsController.background] setupTabChangeEvents')
+const tabEventPorts = new Set()
+
+function broadcastTabEvent(message: object) {
+ for (const port of tabEventPorts) {
+ port.postMessage(message)
+ }
+}
+
+/**
+ * Port-based tab events: agents connect via `chrome.runtime.connect({ name: 'tab-events' })`
+ * and receive tab change events through the port. Works for both extension pages and content scripts.
+ */
+export function setupTabEventsPort() {
+ chrome.runtime.onConnect.addListener((port) => {
+ if (port.name !== 'tab-events') return
+
+ debug('port connected', port.sender?.tab?.id ?? port.sender?.url)
+ tabEventPorts.add(port)
+
+ port.onDisconnect.addListener(() => {
+ debug('port disconnected')
+ tabEventPorts.delete(port)
+ })
+ })
chrome.tabs.onCreated.addListener((tab) => {
- debug('onCreated', tab)
- chrome.runtime
- .sendMessage({ type: 'TAB_CHANGE', action: 'created', payload: { tab } })
- .catch((error) => {
- debug('onCreated error:', error)
- })
+ broadcastTabEvent({ action: 'created', payload: { tab } })
})
chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
- debug('onRemoved', tabId, removeInfo)
- chrome.runtime
- .sendMessage({
- type: 'TAB_CHANGE',
- action: 'removed',
- payload: { tabId, removeInfo },
- })
- .catch((error) => {
- debug('onRemoved error:', error)
- })
+ broadcastTabEvent({ action: 'removed', payload: { tabId, removeInfo } })
})
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
- debug('onUpdated', tabId, changeInfo)
- chrome.runtime
- .sendMessage({
- type: 'TAB_CHANGE',
- action: 'updated',
- payload: { tabId, changeInfo, tab },
- })
- .catch((error) => {
- debug('onUpdated error:', error)
- })
+ broadcastTabEvent({ action: 'updated', payload: { tabId, changeInfo, tab } })
})
}
diff --git a/packages/extension/src/agent/TabsController.ts b/packages/extension/src/agent/TabsController.ts
index 46fabf6..9d77ad5 100644
--- a/packages/extension/src/agent/TabsController.ts
+++ b/packages/extension/src/agent/TabsController.ts
@@ -2,9 +2,7 @@ import { isContentScriptAllowed } from './RemotePageController'
const PREFIX = '[TabsController]'
-function debug(...messages: any[]) {
- console.debug(`\x1b[90m${PREFIX}\x1b[0m`, ...messages)
-}
+const debug = console.debug.bind(console, `\x1b[90m${PREFIX}\x1b[0m`)
function sendMessage(message: {
type: 'TAB_CONTROL'
@@ -22,46 +20,91 @@ function sendMessage(message: {
* - live in the agent env (extension page or content script)
* - no chrome apis. call sw for tab operations
*/
-export class TabsController extends EventTarget {
+export class TabsController {
currentTabId: number | null = null
+ private disposed = false
+ private port: chrome.runtime.Port | null = null
+ private portRetries = 0
+
+ private windowId: number | null = null
private tabs: TabMeta[] = []
private initialTabId: number | null = null
private tabGroupId: number | null = null
+ private experimentalIncludeAllTabs = false
private task: string = ''
- async init(task: string, includeInitialTab: boolean = true) {
- debug('init', task, includeInitialTab)
+ async init(task: string, options: TabsInitOptions = {}) {
+ const { includeInitialTab = true, experimentalIncludeAllTabs = false } = options
+ debug('init', task, options)
+
+ if (this.disposed) {
+ throw new Error('TabsController already disposed')
+ }
- this.task = task
- this.tabs = []
this.currentTabId = null
+ this.disposed = false
+ this.port = null
+ this.portRetries = 0
+
+ this.windowId = null
+ this.tabs = []
this.tabGroupId = null
this.initialTabId = null
+ this.experimentalIncludeAllTabs = experimentalIncludeAllTabs
+ this.task = task
- const result = await sendMessage({
+ const activeTabResult = await sendMessage({
type: 'TAB_CONTROL',
action: 'get_active_tab',
})
- this.initialTabId = result.tabId
+ this.initialTabId = activeTabResult.tab?.id
+ this.windowId = activeTabResult.tab?.windowId
- if (!this.initialTabId) {
- throw new Error('Failed to get initial tab ID')
+ if (!this.initialTabId || !this.windowId) {
+ if (activeTabResult.error) {
+ throw new Error(activeTabResult.error)
+ } else {
+ throw new Error('Failed to get active tab')
+ }
}
- if (includeInitialTab) {
+ this.connectTabEvents()
+
+ if (experimentalIncludeAllTabs) {
+ const allTabs = await sendMessage({
+ type: 'TAB_CONTROL',
+ action: 'get_window_tabs',
+ payload: { windowId: this.windowId },
+ })
+ for (const tab of allTabs.tabs as chrome.tabs.Tab[]) {
+ if (tab.id && !tab.pinned && isContentScriptAllowed(tab.url)) {
+ this.addTab({
+ id: tab.id,
+ isInitial: tab.id === this.initialTabId,
+ url: tab.url,
+ title: tab.title,
+ status: tab.status,
+ })
+ }
+ }
+ if (this.tabs.find((t) => t.id === this.initialTabId)) {
+ this.currentTabId = this.initialTabId
+ await this.createTabGroup([this.initialTabId])
+ }
+ } else if (includeInitialTab) {
const info = await sendMessage({
type: 'TAB_CONTROL',
action: 'get_tab_info',
payload: { tabId: this.initialTabId },
})
- if (isContentScriptAllowed(info.url)) {
+ if (isContentScriptAllowed(info.url) && !info.pinned) {
this.currentTabId = this.initialTabId
- this.tabs.push({
- id: result.tabId,
+ this.addTab({
+ id: this.initialTabId,
isInitial: true,
url: info.url,
title: info.title,
@@ -73,52 +116,6 @@ export class TabsController extends EventTarget {
}
await this.updateCurrentTabId(this.currentTabId)
-
- const tabChangeHandler = (message: any): void => {
- if (message.type !== 'TAB_CHANGE') {
- // throw new Error(`[TabsController]: Invalid message type: ${message.type}`)
- return
- }
-
- if (message.action === 'created') {
- const tab = message.payload.tab as chrome.tabs.Tab
- if (tab.groupId === this.tabGroupId && tab.id != null) {
- // Tab created in our controlled group
- if (!this.tabs.find((t) => t.id === tab.id)) {
- this.tabs.push({ id: tab.id, isInitial: false })
- }
- this.switchToTab(tab.id)
- }
- } else if (message.action === 'removed') {
- const { tabId } = message.payload as { tabId: number }
- const targetTab = this.tabs.find((t) => t.id === tabId)
- if (targetTab) {
- this.tabs = this.tabs.filter((t) => t.id !== tabId)
- if (this.currentTabId === tabId) {
- const newCurrentTab = this.tabs[this.tabs.length - 1] || null
- if (newCurrentTab) {
- this.switchToTab(newCurrentTab.id)
- } else {
- this.updateCurrentTabId(null)
- }
- }
- }
- } else if (message.action === 'updated') {
- const { tabId, tab } = message.payload as { tabId: number; tab: chrome.tabs.Tab }
- const targetTab = this.tabs.find((t) => t.id === tabId)
- if (targetTab) {
- targetTab.url = tab.url
- targetTab.title = tab.title
- targetTab.status = tab.status
- }
- }
- }
-
- chrome.runtime.onMessage.addListener(tabChangeHandler)
-
- this.addEventListener('dispose', () => {
- chrome.runtime.onMessage.removeListener(tabChangeHandler)
- })
}
async openNewTab(url: string): Promise {
@@ -136,7 +133,7 @@ export class TabsController extends EventTarget {
const tabId = result.tabId as number
- this.tabs.push({
+ this.addTab({
id: tabId,
isInitial: false,
})
@@ -209,7 +206,7 @@ export class TabsController extends EventTarget {
const result = await sendMessage({
type: 'TAB_CONTROL',
action: 'create_tab_group',
- payload: { tabIds },
+ payload: { tabIds, windowId: this.windowId },
})
if (!result?.success) {
@@ -232,6 +229,11 @@ export class TabsController extends EventTarget {
})
}
+ private addTab(meta: TabMeta) {
+ if (this.tabs.find((t) => t.id === meta.id)) return
+ this.tabs.push(meta)
+ }
+
async updateCurrentTabId(tabId: number | null) {
debug('updateCurrentTabId', tabId)
@@ -288,9 +290,77 @@ export class TabsController extends EventTarget {
await waitUntil(() => tab.status === 'complete', 4_000)
}
- dispose() {
- this.dispatchEvent(new Event('dispose'))
+ /**
+ * Connect to background SW via port to receive tab change events.
+ *
+ * @note Port is 1:1 (runtime.connect → background SW has no frames),
+ * so onDisconnect fires exactly once and we can safely reconnect.
+ * Reconnection may miss events during the gap.
+ * TODO: refresh this.tabs from background after reconnect to stay consistent.
+ */
+ private connectTabEvents() {
+ this.port = chrome.runtime.connect({ name: 'tab-events' })
+
+ this.port.onMessage.addListener((message: any) => {
+ if (this.disposed) return
+ this.portRetries = 0
+
+ if (message.action === 'created') {
+ const tab = message.payload.tab as chrome.tabs.Tab
+ const shouldTrack = this.experimentalIncludeAllTabs || tab.groupId === this.tabGroupId
+ if (shouldTrack && tab.id != null) {
+ this.addTab({ id: tab.id, isInitial: false })
+ this.switchToTab(tab.id)
+ }
+ } else if (message.action === 'removed') {
+ const { tabId } = message.payload as { tabId: number }
+ const targetTab = this.tabs.find((t) => t.id === tabId)
+ if (targetTab) {
+ this.tabs = this.tabs.filter((t) => t.id !== tabId)
+ if (this.currentTabId === tabId) {
+ const newCurrentTab = this.tabs[this.tabs.length - 1] || null
+ if (newCurrentTab) {
+ this.switchToTab(newCurrentTab.id)
+ } else {
+ this.updateCurrentTabId(null)
+ }
+ }
+ }
+ } else if (message.action === 'updated') {
+ const { tabId, tab } = message.payload as { tabId: number; tab: chrome.tabs.Tab }
+ const targetTab = this.tabs.find((t) => t.id === tabId)
+ if (targetTab) {
+ targetTab.url = tab.url
+ targetTab.title = tab.title
+ targetTab.status = tab.status
+ }
+ }
+ })
+
+ this.port.onDisconnect.addListener(() => {
+ this.port = null
+ if (this.disposed) return
+ if (this.portRetries >= 7) {
+ console.error(PREFIX, 'tab events port failed after 7 retries, giving up')
+ return
+ }
+ debug('port disconnected, reconnecting...')
+ this.portRetries++
+ this.connectTabEvents()
+ })
}
+
+ dispose() {
+ debug('dispose')
+ this.disposed = true
+ this.port?.disconnect()
+ this.port = null
+ }
+}
+
+export interface TabsInitOptions {
+ includeInitialTab?: boolean
+ experimentalIncludeAllTabs?: boolean
}
export type TabAction =
@@ -302,6 +372,7 @@ export type TabAction =
| 'add_tab_to_group'
| 'close_tab'
| 'get_tab_title'
+ | 'get_window_tabs'
interface TabMeta {
id: number
diff --git a/packages/extension/src/agent/useAgent.ts b/packages/extension/src/agent/useAgent.ts
index f5596a5..c6a31bf 100644
--- a/packages/extension/src/agent/useAgent.ts
+++ b/packages/extension/src/agent/useAgent.ts
@@ -21,6 +21,7 @@ export interface AdvancedConfig {
maxSteps?: number
systemInstruction?: string
experimentalLlmsTxt?: boolean
+ experimentalIncludeAllTabs?: boolean
disableNamedToolChoice?: boolean
}
@@ -125,6 +126,7 @@ export function useAgent(): UseAgentResult {
maxSteps,
systemInstruction,
experimentalLlmsTxt,
+ experimentalIncludeAllTabs,
disableNamedToolChoice,
...llmConfig
}: ExtConfig) => {
@@ -138,6 +140,7 @@ export function useAgent(): UseAgentResult {
maxSteps,
systemInstruction,
experimentalLlmsTxt,
+ experimentalIncludeAllTabs,
disableNamedToolChoice,
}
await chrome.storage.local.set({ advancedConfig })
diff --git a/packages/extension/src/components/ConfigPanel.tsx b/packages/extension/src/components/ConfigPanel.tsx
index f1dd250..d7a2d9f 100644
--- a/packages/extension/src/components/ConfigPanel.tsx
+++ b/packages/extension/src/components/ConfigPanel.tsx
@@ -31,17 +31,20 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
const [model, setModel] = useState(config?.model || DEMO_MODEL)
const [apiKey, setApiKey] = useState(config?.apiKey)
const [language, setLanguage] = useState(config?.language)
- const [maxSteps, setMaxSteps] = useState(config?.maxSteps)
+ const [maxSteps, setMaxSteps] = useState(config?.maxSteps)
const [systemInstruction, setSystemInstruction] = useState(config?.systemInstruction ?? '')
const [experimentalLlmsTxt, setExperimentalLlmsTxt] = useState(
config?.experimentalLlmsTxt ?? false
)
+ const [experimentalIncludeAllTabs, setExperimentalIncludeAllTabs] = useState(
+ config?.experimentalIncludeAllTabs ?? false
+ )
const [disableNamedToolChoice, setDisableNamedToolChoice] = useState(
config?.disableNamedToolChoice ?? false
)
const [advancedOpen, setAdvancedOpen] = useState(false)
const [saving, setSaving] = useState(false)
- const [userAuthToken, setUserAuthToken] = useState('')
+ const [userAuthToken, setUserAuthToken] = useState('')
const [copied, setCopied] = useState(false)
const [showToken, setShowToken] = useState(false)
const [showApiKey, setShowApiKey] = useState(false)
@@ -54,6 +57,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
setMaxSteps(config?.maxSteps)
setSystemInstruction(config?.systemInstruction ?? '')
setExperimentalLlmsTxt(config?.experimentalLlmsTxt ?? false)
+ setExperimentalIncludeAllTabs(config?.experimentalIncludeAllTabs ?? false)
setDisableNamedToolChoice(config?.disableNamedToolChoice ?? false)
}, [config])
@@ -100,6 +104,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
maxSteps: maxSteps || undefined,
systemInstruction: systemInstruction || undefined,
experimentalLlmsTxt,
+ experimentalIncludeAllTabs,
disableNamedToolChoice,
})
} finally {
@@ -285,6 +290,14 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
Experimental llms.txt support
+
+
>
)}
diff --git a/packages/extension/src/components/misc.tsx b/packages/extension/src/components/misc.tsx
index 6de0f19..66bfa48 100644
--- a/packages/extension/src/components/misc.tsx
+++ b/packages/extension/src/components/misc.tsx
@@ -111,6 +111,7 @@ export function EmptyState() {
]}
cursorStyle="underscore"
loop
+ startOnView={false}
typeSpeed={20}
deleteSpeed={10}
pauseDelay={3000}
diff --git a/packages/extension/src/entrypoints/background.ts b/packages/extension/src/entrypoints/background.ts
index 9c26f96..a83fde3 100644
--- a/packages/extension/src/entrypoints/background.ts
+++ b/packages/extension/src/entrypoints/background.ts
@@ -1,12 +1,12 @@
import { handlePageControlMessage } from '@/agent/RemotePageController.background'
-import { handleTabControlMessage, setupTabChangeEvents } from '@/agent/TabsController.background'
+import { handleTabControlMessage, setupTabEventsPort } from '@/agent/TabsController.background'
export default defineBackground(() => {
console.log('[Background] Service Worker started')
// tab change events
- setupTabChangeEvents()
+ setupTabEventsPort()
// generate user auth token
diff --git a/packages/extension/src/entrypoints/content.ts b/packages/extension/src/entrypoints/content.ts
index e06dd1f..1bcfc8a 100644
--- a/packages/extension/src/entrypoints/content.ts
+++ b/packages/extension/src/entrypoints/content.ts
@@ -70,11 +70,15 @@ async function exposeAgentToPage() {
try {
const { task, config } = payload
+ const { systemInstruction, ...agentConfig } = config
// Dispose old instance before creating new one
multiPageAgent?.dispose()
- multiPageAgent = new MultiPageAgent(config)
+ multiPageAgent = new MultiPageAgent({
+ ...agentConfig,
+ instructions: systemInstruction ? { system: systemInstruction } : undefined,
+ })
// events
diff --git a/packages/extension/src/entrypoints/main-world.ts b/packages/extension/src/entrypoints/main-world.ts
index fd93e68..b401beb 100644
--- a/packages/extension/src/entrypoints/main-world.ts
+++ b/packages/extension/src/entrypoints/main-world.ts
@@ -7,12 +7,21 @@ export interface ExecuteConfig {
model: string
apiKey?: string
+ /**
+ * Global system-level instructions for the agent.
+ * Equivalent to `AgentConfig.instructions.system`.
+ */
+ systemInstruction?: string
+
/**
* Whether to include the initial tab (that holds this main world script) in the task.
* @default true
*/
includeInitialTab?: boolean
+ /** Control all unpinned tabs in the window instead of only the tab group. */
+ experimentalIncludeAllTabs?: boolean
+
onStatusChange?: (status: AgentStatus) => void
onActivity?: (activity: AgentActivity) => void
onHistoryUpdate?: (history: HistoricalEvent[]) => void
@@ -86,7 +95,9 @@ export default defineUnlistedScript(() => {
baseURL: config.baseURL,
model: config.model,
apiKey: config.apiKey,
+ systemInstruction: config.systemInstruction,
includeInitialTab: config.includeInitialTab,
+ experimentalIncludeAllTabs: config.experimentalIncludeAllTabs,
},
},
},
diff --git a/packages/llms/package.json b/packages/llms/package.json
index bcf6c4c..befe627 100644
--- a/packages/llms/package.json
+++ b/packages/llms/package.json
@@ -1,6 +1,6 @@
{
"name": "@page-agent/llms",
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"main": "./dist/lib/page-agent-llms.js",
"module": "./dist/lib/page-agent-llms.js",
diff --git a/packages/mcp/package.json b/packages/mcp/package.json
index 23d018c..c184c9a 100644
--- a/packages/mcp/package.json
+++ b/packages/mcp/package.json
@@ -1,7 +1,7 @@
{
"name": "@page-agent/mcp",
"private": false,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"bin": {
"page-agent-mcp": "src/index.js"
@@ -28,8 +28,8 @@
"node": ">=20"
},
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.27.1",
- "ws": "^8.19.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
+ "ws": "^8.20.0",
"zod": "^4.3.5"
}
}
diff --git a/packages/mcp/src/index.js b/packages/mcp/src/index.js
index d88211e..2661b6c 100755
--- a/packages/mcp/src/index.js
+++ b/packages/mcp/src/index.js
@@ -35,11 +35,14 @@ const mcpServer = new McpServer({ name: 'page-agent', version: '1.5.8' })
mcpServer.registerTool(
'execute_task',
{
- description:
- 'Execute a browser automation task described in natural language. ' +
- 'The Page Agent extension will control the browser to complete the task. ' +
- 'Blocks until the task is complete.',
- inputSchema: { task: z.string().describe('Task description in natural language') },
+ description: "Execute a task in user's browser.",
+ inputSchema: {
+ task: z
+ .string()
+ .describe(
+ 'Task description. Give specific instructions for the task. Steps preferable. And the information you want to get after the task is done.'
+ ),
+ },
},
async ({ task }) => {
try {
@@ -50,7 +53,7 @@ mcpServer.registerTool(
{
type: 'text',
text: result.success
- ? `Task completed successfully.\n\n${result.data}`
+ ? `Task completed.\n\n${result.data}`
: `Task failed.\n\n${result.data}`,
},
],
@@ -67,7 +70,7 @@ mcpServer.registerTool(
mcpServer.registerTool(
'get_status',
{
- description: 'Check the current status of the Page Agent hub connection and agent.',
+ description: 'Check the current status of the Page Agent hub.',
},
async () => ({
content: [
diff --git a/packages/page-agent/package.json b/packages/page-agent/package.json
index 4414a28..fb40751 100644
--- a/packages/page-agent/package.json
+++ b/packages/page-agent/package.json
@@ -1,7 +1,7 @@
{
"name": "page-agent",
"private": false,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"main": "./dist/esm/page-agent.js",
"module": "./dist/esm/page-agent.js",
@@ -44,10 +44,10 @@
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
},
"dependencies": {
- "@page-agent/core": "1.6.0",
- "@page-agent/llms": "1.6.0",
- "@page-agent/page-controller": "1.6.0",
- "@page-agent/ui": "1.6.0",
+ "@page-agent/core": "1.7.0",
+ "@page-agent/llms": "1.7.0",
+ "@page-agent/page-controller": "1.7.0",
+ "@page-agent/ui": "1.7.0",
"chalk": "^5.6.2"
},
"peerDependencies": {
diff --git a/packages/page-agent/src/PageAgent.ts b/packages/page-agent/src/PageAgent.ts
index f6e5e48..486396b 100644
--- a/packages/page-agent/src/PageAgent.ts
+++ b/packages/page-agent/src/PageAgent.ts
@@ -4,11 +4,11 @@
*/
import { type AgentConfig, PageAgentCore } from '@page-agent/core'
import { PageController, type PageControllerConfig } from '@page-agent/page-controller'
-import { Panel } from '@page-agent/ui'
+import { Panel, type PanelConfig } from '@page-agent/ui'
export * from '@page-agent/core'
-export type PageAgentConfig = AgentConfig & PageControllerConfig
+export type PageAgentConfig = AgentConfig & PageControllerConfig & Omit
export class PageAgent extends PageAgentCore {
panel: Panel
@@ -23,6 +23,7 @@ export class PageAgent extends PageAgentCore {
this.panel = new Panel(this, {
language: config.language,
+ promptForNextTask: config.promptForNextTask,
})
}
}
diff --git a/packages/page-agent/src/demo.ts b/packages/page-agent/src/demo.ts
index af6d668..516dd6f 100644
--- a/packages/page-agent/src/demo.ts
+++ b/packages/page-agent/src/demo.ts
@@ -17,9 +17,10 @@ const DEMO_MODEL = 'qwen3.5-plus'
const DEMO_BASE_URL = 'https://page-ag-testing-ohftxirgbn.cn-shanghai.fcapp.run'
const DEMO_API_KEY = 'NA'
+const currentScript = document.currentScript as HTMLScriptElement | null
+
// in case document.x is not ready yet
setTimeout(() => {
- const currentScript = document.currentScript as HTMLScriptElement | null
let config: PageAgentConfig
if (currentScript) {
diff --git a/packages/page-controller/package.json b/packages/page-controller/package.json
index 928a859..2b1c31f 100644
--- a/packages/page-controller/package.json
+++ b/packages/page-controller/package.json
@@ -1,6 +1,6 @@
{
"name": "@page-agent/page-controller",
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"main": "./dist/lib/page-controller.js",
"module": "./dist/lib/page-controller.js",
diff --git a/packages/page-controller/src/PageController.ts b/packages/page-controller/src/PageController.ts
index ac83b15..53b827d 100644
--- a/packages/page-controller/src/PageController.ts
+++ b/packages/page-controller/src/PageController.ts
@@ -218,6 +218,7 @@ export class PageController extends EventTarget {
* Clean up all element highlights
*/
async cleanUpHighlights(): Promise {
+ console.log('[PageController] cleanUpHighlights')
dom.cleanUpHighlights()
}
@@ -424,3 +425,5 @@ export class PageController extends EventTarget {
this.mask = null
}
}
+
+export * from './actions'
diff --git a/packages/page-controller/src/actions.ts b/packages/page-controller/src/actions.ts
index dcf6ab4..a218599 100644
--- a/packages/page-controller/src/actions.ts
+++ b/packages/page-controller/src/actions.ts
@@ -4,6 +4,9 @@
*/
import type { InteractiveElementDomNode } from './dom/dom_tree/type'
import {
+ clickPointer,
+ disablePassThrough,
+ enablePassThrough,
getNativeValueSetter,
isHTMLElement,
isInputElement,
@@ -15,6 +18,7 @@ import {
/**
* Get the HTMLElement by index from a selectorMap.
+ * @private Internal method, subject to change at any time.
*/
export function getElementByIndex(
selectorMap: Map,
@@ -41,19 +45,21 @@ let lastClickedElement: HTMLElement | null = null
function blurLastClickedElement() {
if (lastClickedElement) {
+ lastClickedElement.dispatchEvent(new PointerEvent('pointerout', { bubbles: true }))
+ lastClickedElement.dispatchEvent(new PointerEvent('pointerleave', { bubbles: false }))
+ lastClickedElement.dispatchEvent(new MouseEvent('mouseout', { bubbles: true }))
+ lastClickedElement.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }))
lastClickedElement.blur()
- lastClickedElement.dispatchEvent(
- new MouseEvent('mouseout', { bubbles: true, cancelable: true })
- )
- lastClickedElement.dispatchEvent(
- new MouseEvent('mouseleave', { bubbles: false, cancelable: true })
- )
lastClickedElement = null
}
}
/**
- * Simulate a click on the element
+ * Simulate a full click following W3C Pointer Events + UI Events spec order:
+ * pointerover/enter → mouseover/enter → pointerdown → mousedown → [focus] →
+ * pointerup → mouseup → click
+ *
+ * @private Internal method, subject to change at any time.
*/
export async function clickElement(element: HTMLElement) {
blurLastClickedElement()
@@ -61,34 +67,67 @@ export async function clickElement(element: HTMLElement) {
lastClickedElement = element
await scrollIntoViewIfNeeded(element)
- // Scroll the iframe element itself into view if needed
const frame = element.ownerDocument.defaultView?.frameElement
if (frame) await scrollIntoViewIfNeeded(frame)
- await movePointerToElement(element)
- window.dispatchEvent(new CustomEvent('PageAgent::ClickPointer'))
+ const rect = element.getBoundingClientRect()
+ const x = rect.left + rect.width / 2
+ const y = rect.top + rect.height / 2
+
+ await movePointerToElement(element, x, y)
+ await clickPointer()
await waitFor(0.1)
- // hover it
- element.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true, cancelable: true }))
- element.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true }))
+ // Hit-test to find the deepest element at click coordinates, matching
+ // real browser behavior where events target the innermost element.
+ // @note This may hit a element in the blacklist
+ // TODO: This is a temporary workaround. Should have been handled during dom extraction.
+ const doc = element.ownerDocument
+ await enablePassThrough()
+ const hitTarget = doc.elementFromPoint(x, y)
+ await disablePassThrough()
+ const target =
+ hitTarget instanceof HTMLElement && element.contains(hitTarget) ? hitTarget : element
- // dispatch a sequence of events to ensure all listeners are triggered
- element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))
+ const pointerOpts = {
+ bubbles: true,
+ cancelable: true,
+ clientX: x,
+ clientY: y,
+ pointerType: 'mouse',
+ }
+ const mouseOpts = { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0 }
- // focus it to ensure it gets the click event
- element.focus()
+ // Hover — pointer events first, then mouse events (spec order)
+ target.dispatchEvent(new PointerEvent('pointerover', pointerOpts))
+ target.dispatchEvent(new PointerEvent('pointerenter', { ...pointerOpts, bubbles: false }))
+ target.dispatchEvent(new MouseEvent('mouseover', mouseOpts))
+ target.dispatchEvent(new MouseEvent('mouseenter', { ...mouseOpts, bubbles: false }))
- element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }))
- element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
+ // Press
+ target.dispatchEvent(new PointerEvent('pointerdown', pointerOpts))
+ target.dispatchEvent(new MouseEvent('mousedown', mouseOpts))
- // dispatch a click event
- // element.click()
+ // Focus is not part of the standard pointer/mouse event sequence
+ // "undefined and varies between user agents".
+ // We focus the original element (nearest focusable ancestor), not the hit-test target, matching browser behavior.
+ element.focus({ preventScroll: true })
- await waitFor(0.2) // Wait to ensure click event processing completes
+ // Release
+ target.dispatchEvent(new PointerEvent('pointerup', pointerOpts))
+ target.dispatchEvent(new MouseEvent('mouseup', mouseOpts))
+
+ // Click — activation behavior (navigation, form submit, etc.) triggers
+ // via bubbling from target up to the interactive ancestor.
+ target.click()
+
+ await waitFor(0.2)
}
+/**
+ * @private Internal method, subject to change at any time.
+ */
export async function inputTextElement(element: HTMLElement, text: string) {
const isContentEditable = element.isContentEditable
if (!isInputElement(element) && !isTextAreaElement(element) && !isContentEditable) {
@@ -196,6 +235,7 @@ export async function inputTextElement(element: HTMLElement, text: string) {
/**
* @todo browser-use version is very complex and supports menu tags, need to follow up
+ * @private Internal method, subject to change at any time.
*/
export async function selectOptionElement(selectElement: HTMLSelectElement, optionText: string) {
if (!isSelectElement(selectElement)) {
@@ -219,6 +259,9 @@ interface ScrollableElement extends Element {
scrollIntoViewIfNeeded?: (centerIfNeeded?: boolean) => void
}
+/**
+ * @private Internal method, subject to change at any time.
+ */
export async function scrollIntoViewIfNeeded(element: Element) {
const el = element as ScrollableElement
if (typeof el.scrollIntoViewIfNeeded === 'function') {
diff --git a/packages/page-controller/src/dom/dom_tree/index.js b/packages/page-controller/src/dom/dom_tree/index.js
index 782ad58..5f57ff7 100644
--- a/packages/page-controller/src/dom/dom_tree/index.js
+++ b/packages/page-controller/src/dom/dom_tree/index.js
@@ -18,6 +18,7 @@
* @edit improve `sampleRect`, filter out rects with 0 area
* @edit exclude aria-hidden elements
* @edit make sure attributes exist for interactive candidates.
+ * @edit fix "aria-*" attributes check
*/
export default (
@@ -1143,6 +1144,31 @@ export default (
* @param {HTMLElement} element - The element to check.
* @returns {boolean} Whether the element is an interactive candidate.
*/
+
+ // @edit fix "aria-*" attributes check
+ const INTERACTIVE_ARIA_ATTRS = [
+ 'aria-expanded',
+ 'aria-checked',
+ 'aria-selected',
+ 'aria-pressed',
+ 'aria-haspopup',
+ 'aria-controls',
+ 'aria-owns',
+ 'aria-activedescendant',
+ 'aria-valuenow',
+ 'aria-valuetext',
+ 'aria-valuemax',
+ 'aria-valuemin',
+ 'aria-autocomplete',
+ ]
+
+ function hasInteractiveAria(el) {
+ for (let i = 0; i < INTERACTIVE_ARIA_ATTRS.length; i++) {
+ if (el.hasAttribute(INTERACTIVE_ARIA_ATTRS[i])) return true
+ }
+ return false
+ }
+
function isInteractiveCandidate(element) {
if (!element || element.nodeType !== Node.ELEMENT_NODE) return false
@@ -1167,7 +1193,7 @@ export default (
element.hasAttribute('onclick') ||
element.hasAttribute('role') ||
element.hasAttribute('tabindex') ||
- element.hasAttribute('aria-') ||
+ hasInteractiveAria(element) ||
element.hasAttribute('data-action') ||
element.getAttribute('contenteditable') === 'true'
diff --git a/packages/page-controller/src/mask/SimulatorMask.ts b/packages/page-controller/src/mask/SimulatorMask.ts
index eb13eb6..41cbdb4 100644
--- a/packages/page-controller/src/mask/SimulatorMask.ts
+++ b/packages/page-controller/src/mask/SimulatorMask.ts
@@ -5,7 +5,7 @@ import { isPageDark } from './checkDarkMode'
import styles from './SimulatorMask.module.css'
import cursorStyles from './cursor.module.css'
-export class SimulatorMask {
+export class SimulatorMask extends EventTarget {
shown: boolean = false
wrapper = document.createElement('div')
motion: Motion | null = null
@@ -19,6 +19,8 @@ export class SimulatorMask {
#targetCursorY = 0
constructor() {
+ super()
+
this.wrapper.id = 'page-agent-runtime_simulator-mask'
this.wrapper.className = styles.wrapper
this.wrapper.setAttribute('data-browser-use-ignore', 'true')
@@ -74,13 +76,34 @@ export class SimulatorMask {
this.#moveCursorToTarget()
- window.addEventListener('PageAgent::MovePointerTo', (event: Event) => {
+ // global events
+ // @note Mask should be isolated from the rest of the code.
+ // Global events are easier to manage and cleanup.
+
+ const movePointerToListener = (event: Event) => {
const { x, y } = (event as CustomEvent).detail
this.setCursorPosition(x, y)
- })
-
- window.addEventListener('PageAgent::ClickPointer', (event: Event) => {
+ }
+ const clickPointerListener = () => {
this.triggerClickAnimation()
+ }
+ const enablePassThroughListener = () => {
+ this.wrapper.style.pointerEvents = 'none'
+ }
+ const disablePassThroughListener = () => {
+ this.wrapper.style.pointerEvents = 'auto'
+ }
+
+ window.addEventListener('PageAgent::MovePointerTo', movePointerToListener)
+ window.addEventListener('PageAgent::ClickPointer', clickPointerListener)
+ window.addEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)
+ window.addEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)
+
+ this.addEventListener('dispose', () => {
+ window.removeEventListener('PageAgent::MovePointerTo', movePointerToListener)
+ window.removeEventListener('PageAgent::ClickPointer', clickPointerListener)
+ window.removeEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)
+ window.removeEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)
})
}
@@ -177,7 +200,9 @@ export class SimulatorMask {
}
dispose() {
+ console.log('dispose SimulatorMask')
this.motion?.dispose()
this.wrapper.remove()
+ this.dispatchEvent(new Event('dispose'))
}
}
diff --git a/packages/page-controller/src/utils/index.ts b/packages/page-controller/src/utils/index.ts
index 7f651a2..885357d 100644
--- a/packages/page-controller/src/utils/index.ts
+++ b/packages/page-controller/src/utils/index.ts
@@ -48,15 +48,33 @@ export async function waitFor(seconds: number): Promise {
await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
}
-// ======= dom utils =======
+// ======= mask events =======
-export async function movePointerToElement(element: HTMLElement) {
- const rect = element.getBoundingClientRect()
+/**
+ * Move the visual pointer to a position within an element.
+ * @param x - x coordinate in the element's document viewport
+ * @param y - y coordinate in the element's document viewport
+ */
+export async function movePointerToElement(element: HTMLElement, x: number, y: number) {
const offset = getIframeOffset(element)
- const x = rect.left + rect.width / 2 + offset.x
- const y = rect.top + rect.height / 2 + offset.y
- window.dispatchEvent(new CustomEvent('PageAgent::MovePointerTo', { detail: { x, y } }))
+ window.dispatchEvent(
+ new CustomEvent('PageAgent::MovePointerTo', {
+ detail: { x: x + offset.x, y: y + offset.y },
+ })
+ )
await waitFor(0.3)
}
+
+export async function clickPointer() {
+ window.dispatchEvent(new CustomEvent('PageAgent::ClickPointer'))
+}
+
+export async function enablePassThrough() {
+ window.dispatchEvent(new CustomEvent('PageAgent::EnablePassThrough'))
+}
+
+export async function disablePassThrough() {
+ window.dispatchEvent(new CustomEvent('PageAgent::DisablePassThrough'))
+}
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 491ce15..3a3af26 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@page-agent/ui",
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"main": "./dist/lib/page-agent-ui.js",
"module": "./dist/lib/page-agent-ui.js",
diff --git a/packages/ui/src/panel/Panel.ts b/packages/ui/src/panel/Panel.ts
index ab5c430..ae15473 100644
--- a/packages/ui/src/panel/Panel.ts
+++ b/packages/ui/src/panel/Panel.ts
@@ -369,6 +369,7 @@ export class Panel {
}
#createWrapper(): HTMLElement {
+ const taskInputMaxLength = 1000
const wrapper = document.createElement('div')
wrapper.id = 'page-agent-runtime_agent-panel'
wrapper.className = styles.wrapper
@@ -406,7 +407,7 @@ export class Panel {
diff --git a/packages/website/package.json b/packages/website/package.json
index bcb8f46..37e99b1 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
- "version": "1.6.0",
+ "version": "1.7.0",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
@@ -19,13 +19,13 @@
"@types/react-dom": "^19.2.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "lucide-react": "^0.577.0",
- "motion": "^12.37.0",
+ "lucide-react": "^1.7.0",
+ "motion": "^12.38.0",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
- "simple-icons": "^16.12.0",
+ "simple-icons": "^16.14.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
diff --git a/packages/website/src/components/LanguageSwitcher.tsx b/packages/website/src/components/LanguageSwitcher.tsx
index e5af95d..8c73eff 100644
--- a/packages/website/src/components/LanguageSwitcher.tsx
+++ b/packages/website/src/components/LanguageSwitcher.tsx
@@ -8,8 +8,8 @@ export default function LanguageSwitcher() {
const dropdownRef = useRef(null)
const languages = [
- { code: 'zh-CN' as const, label: '中文' },
{ code: 'en-US' as const, label: 'English' },
+ { code: 'zh-CN' as const, label: '中文' },
]
const currentLanguage = languages.find((lang) => lang.code === language) || languages[0]
diff --git a/packages/website/src/constants.ts b/packages/website/src/constants.ts
index 4d7c5f1..d050458 100644
--- a/packages/website/src/constants.ts
+++ b/packages/website/src/constants.ts
@@ -1,8 +1,8 @@
// Demo build (auto-init with demo LLM, for quick testing)
export const CDN_DEMO_URL =
- 'https://cdn.jsdelivr.net/npm/page-agent@1.6.0/dist/iife/page-agent.demo.js'
+ 'https://cdn.jsdelivr.net/npm/page-agent@1.7.0/dist/iife/page-agent.demo.js'
export const CDN_DEMO_CN_URL =
- 'https://registry.npmmirror.com/page-agent/1.6.0/files/dist/iife/page-agent.demo.js'
+ 'https://registry.npmmirror.com/page-agent/1.7.0/files/dist/iife/page-agent.demo.js'
// Demo LLM for website testing (homepage quick trial uses flash)
export const DEMO_MODEL = 'qwen3.5-flash'
diff --git a/packages/website/src/pages/docs/Layout.tsx b/packages/website/src/pages/docs/Layout.tsx
index e9abee8..44292e3 100644
--- a/packages/website/src/pages/docs/Layout.tsx
+++ b/packages/website/src/pages/docs/Layout.tsx
@@ -45,6 +45,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
{ title: isZh ? '知识注入' : 'Instructions', path: '/features/custom-instructions' },
{ title: isZh ? '数据脱敏' : 'Data Masking', path: '/features/data-masking' },
{ title: isZh ? 'Chrome 扩展' : 'Chrome Extension', path: '/features/chrome-extension' },
+ { title: 'MCP Server (Beta)', path: '/features/mcp-server' },
{
title: isZh ? '接入第三方 Agent' : 'Third-party Agent',
path: '/features/third-party-agent',
diff --git a/packages/website/src/pages/docs/advanced/page-agent/page.tsx b/packages/website/src/pages/docs/advanced/page-agent/page.tsx
index 7428223..a0e2471 100644
--- a/packages/website/src/pages/docs/advanced/page-agent/page.tsx
+++ b/packages/website/src/pages/docs/advanced/page-agent/page.tsx
@@ -100,7 +100,7 @@ console.log(result.history) // Full execution history`}
>
AgentConfig
{' '}
- 和{' '}
+ 、 PanelConfig 和{' '}
AgentConfig
{' '}
- and{' '}
+ , PanelConfig, and{' '}
void
onActivity?: (activity: AgentActivity) => void
onHistoryUpdate?: (history: HistoricalEvent[]) => void
@@ -233,6 +235,7 @@ const result = await window.PAGE_AGENT_EXT.execute(
apiKey: 'your-api-key',
model: 'gpt-5.2',
// includeInitialTab: false, // 设为 false 排除初始标签页
+ // experimentalIncludeAllTabs: true, // 控制窗口内所有非固定标签页
onStatusChange: status => console.log('状态变化:', status),
onActivity: activity => console.log('活动:', activity),
onHistoryUpdate: history => console.log('历史更新:', history)
@@ -248,6 +251,7 @@ const result = await window.PAGE_AGENT_EXT.execute(
apiKey: 'your-api-key',
model: 'gpt-5.2',
// includeInitialTab: false, // Set to false to exclude initial tab
+ // experimentalIncludeAllTabs: true, // Control all unpinned tabs in the window
onStatusChange: status => console.log('Status change:', status),
onActivity: activity => console.log('Activity:', activity),
onHistoryUpdate: history => console.log('History update:', history)
diff --git a/packages/website/src/pages/docs/features/mcp-server/page.tsx b/packages/website/src/pages/docs/features/mcp-server/page.tsx
new file mode 100644
index 0000000..b31454f
--- /dev/null
+++ b/packages/website/src/pages/docs/features/mcp-server/page.tsx
@@ -0,0 +1,70 @@
+import BetaNotice from '@/components/BetaNotice'
+import CodeEditor from '@/components/CodeEditor'
+import { Heading } from '@/components/Heading'
+
+export default function McpServerPage() {
+ return (
+
+
MCP Server (Beta)
+
+
+ Use the MCP server to let your local agent send natural-language browser tasks to Page Agent
+ Ext.
+
+
+
+
+ How to use
+
+
+
+
+ 1. Install Page Agent Ext in Chrome.
+
+ 2. Add the MCP server to your local agent client.
+
+ 3. Start the client and approve the Hub connection in the browser when prompted.
+
+ 4. Ask your agent to do something in the browser. The client will call execute_task
+ for you.
+
+
+
+
+
+
+
+
+
+ The Hub
+
+
+
+ The Hub is the control center for communication between Page Agent Ext and external
+ callers.
+
+
+ When the MCP server starts, it opens a local launcher page. The launcher asks the
+ extension to open the Hub tab, and the Hub receives tasks from your local agent. MCP uses
+ this path, but the Hub itself is the extension's general external communication entry
+ point.
+
+ {isZh
+ ? '从 Claude Desktop、Copilot 或其他本地 Agent 直接发起浏览器任务?'
+ : 'Using Claude Desktop, Copilot, or another local agent? Connect it to the extension with the MCP server.'}
+
{[
{
@@ -67,16 +83,16 @@ export default function OneMoreThingSection() {
: 'Run tasks across multiple pages and tabs without being limited to a single page context',
},
{
- title: isZh ? '页面内发起控制' : 'Control from Your Page',
+ title: isZh ? '从页面发起控制' : 'Control from a WebPage',
desc: isZh
? '在页面 JS 中发起任务,驱动整个浏览器完成跨标签操作'
- : 'Trigger tasks from page JS to drive the entire browser across tabs',
+ : 'Trigger tasks from in-page JS to drive the entire browser across tabs',
},
{
- title: isZh ? '外部发起任务' : 'External Triggers',
+ title: isZh ? '外部发起任务' : 'External Caller',
desc: isZh
? '页面 JS、本地 Agent 或云端 Agent 均可通过扩展发起任务'
- : 'Page JS, local agents, or cloud agents can trigger tasks through the extension',
+ : 'Local agents and cloud agents can control user browser through the extension',
},
].map((item) => (