Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15d609aebc | |||
| d66bf8f3cf | |||
| d3b42036af | |||
| e448a43769 | |||
| 349de7bf48 | |||
| eec601e6b2 | |||
| 5b280a4a67 | |||
| 46deaf373f | |||
| e098d5701f | |||
| 21d4be0020 | |||
| 3f6a1856ac | |||
| 816e24a102 | |||
| 80e2a93a8c | |||
| 2f8096883f | |||
| 088f002583 | |||
| 27e9e78636 | |||
| a0c979602e | |||
| 3dc9edb8b5 | |||
| 32b68ab7c8 | |||
| 75a61c05c1 | |||
| bdfa98358b | |||
| dc8b38ccb9 | |||
| e1c288aaeb | |||
| 7a97de2a37 | |||
| 19b03b83ea | |||
| 598b144d06 | |||
| 1cd8a8de60 | |||
| 9beffca985 | |||
| 53db2069ce | |||
| 121104e13d | |||
| 0dacbda9da | |||
| 8bc27414a4 | |||
| 76509d93f0 | |||
| ff31b0c03e | |||
| 6b2ab73d65 | |||
| 2754023a80 | |||
| fa5c65db53 | |||
| 53c2c19d69 | |||
| e5437b445a | |||
| 09bdf9ddaf | |||
| b58d2a09ef | |||
| ce442742ba | |||
| 632f22962e | |||
| 72c3b12fb4 | |||
| 4807d550a3 | |||
| f9722f0619 | |||
| e1557a5d2e | |||
| 06280c2ba5 | |||
| 645a7ceb52 | |||
| a47c72ce84 | |||
| dcc6cd1cf3 | |||
| b4b9f6ce2d | |||
| 14e31e6ec4 | |||
| 69f2478fd7 | |||
| 5fec6846b0 | |||
| fe792f1ceb | |||
| d1d27a76de | |||
| 3e7e99420a |
+27
-22
@@ -1,6 +1,6 @@
|
|||||||
# Contributing to Page-Agent
|
# Contributing to PageAgent
|
||||||
|
|
||||||
Thank you for your interest in contributing to Page-Agent! We welcome contributions from everyone.
|
Thank you for your interest in contributing to PageAgent! We welcome contributions from everyone.
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
@@ -27,11 +27,11 @@ This is a **monorepo** with npm workspaces containing **4 main packages**:
|
|||||||
- **Extension** (`packages/extension/`) - Chrome extension for multi-page tasks and browser-level automation
|
- **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`
|
- **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.
|
> 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 developing. Use alias so that we don't have to pre-build.
|
||||||
- When bundling. Use external and disable ts `paths` alias to leave deps out.
|
> - When bundling. Use external and disable ts `paths` alias to leave deps out.
|
||||||
- When bundling `IIFE` and `Website`. Bundle everything including local packages.
|
> - When bundling `IIFE` and `Website`. Bundle everything including local packages.
|
||||||
|
|
||||||
## 🤝 How to Contribute
|
## 🤝 How to Contribute
|
||||||
|
|
||||||
@@ -90,14 +90,17 @@ We use a simplified monorepo solution with `native npm-workspace + ts reference
|
|||||||
- Keep functions small and focused
|
- Keep functions small and focused
|
||||||
- Add JSDoc for public APIs
|
- Add JSDoc for public APIs
|
||||||
|
|
||||||
### Vibe coding with AI
|
### Vibe Coding with AI
|
||||||
|
|
||||||
- It's **recommended** to heavily rely on AI (aka "vibe coding") when maintaining **demo pages and tests**.
|
> [Vibe coding](https://en.wikipedia.org/wiki/Vibe_coding) = describe what you want in natural language, let AI write the code, and you review the result.
|
||||||
- Be very careful if AI ever touched the core lib!!!
|
|
||||||
|
- 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.
|
||||||
- Review anything AI wrote before make a commit. You are the author of anything you commit. NOT AI.
|
- Review anything AI wrote before make a commit. You are the author of anything you commit. NOT AI.
|
||||||
- Update the [AI instructions](AGENTS.md) when structure changed.
|
|
||||||
|
|
||||||
If your lame AI assistant does not support [AGENTS.md](https://agents.md/). Add an alias for it:
|
If your AI assistant does not support [AGENTS.md](https://agents.md/). Add an alias for it:
|
||||||
|
|
||||||
- claude-code (`CLAUDE.md`)
|
- claude-code (`CLAUDE.md`)
|
||||||
|
|
||||||
@@ -135,10 +138,10 @@ If your lame AI assistant does not support [AGENTS.md](https://agents.md/). Add
|
|||||||
LLM_MODEL_NAME="qwen3:14b"
|
LLM_MODEL_NAME="qwen3:14b"
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ Add `*` to `OLLAMA_ORIGINS` (403). Models < 10B unlikely strong enough. Requires tool_call support. Set context length > 15k (default 4k will NOT work): `$env:OLLAMA_CONTEXT_LENGTH=64000; ollama serve`
|
> @see https://alibaba.github.io/page-agent/docs/features/models#ollama for configuration
|
||||||
|
|
||||||
- Restart the dev server to load new env vars
|
- **Restart the dev server** to load new env vars
|
||||||
- If not provided, the demo will the free testing proxy by default
|
- 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).
|
||||||
|
|
||||||
### Website Development
|
### Website Development
|
||||||
|
|
||||||
@@ -149,12 +152,13 @@ npm start
|
|||||||
### Extension Development
|
### Extension Development
|
||||||
|
|
||||||
```bash
|
```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 dev -w @page-agent/ext
|
||||||
npm run zip -w @page-agent/ext
|
npm run zip -w @page-agent/ext
|
||||||
```
|
```
|
||||||
|
|
||||||
- Load extension in Chrome via `chrome://extensions` -> **Load unpacked**
|
- Update `packages/extension/docs/extension_api.md` for API integration details
|
||||||
- Use `packages/extension/docs/extension_api.md` (EN) or `packages/extension/docs/extension_api_zh.md` (ZH) for API integration details
|
|
||||||
|
|
||||||
### Testing on Other Websites
|
### Testing on Other Websites
|
||||||
|
|
||||||
@@ -172,7 +176,7 @@ npm run zip -w @page-agent/ext
|
|||||||
|
|
||||||
- Click the bookmark on any page to load Page-Agent
|
- Click the bookmark on any page to load Page-Agent
|
||||||
|
|
||||||
> Warning: AK in your local `.env` will be inlined in the iife script.
|
> Warning: AK in your local `.env` will be inlined in the iife script. Be very careful when you distribute the script.
|
||||||
|
|
||||||
### Adding Documentation
|
### Adding Documentation
|
||||||
|
|
||||||
@@ -193,16 +197,17 @@ We especially welcome contributions in:
|
|||||||
|
|
||||||
## 🚫 What We Don't Accept
|
## 🚫 What We Don't Accept
|
||||||
|
|
||||||
- Changes that break existing API compatibility (Discuss first)
|
- Breaking changes and large PRs without prior discussion
|
||||||
- Heavy dependencies to core library
|
- Heavy dependencies to core libs
|
||||||
- Contributions without proper testing
|
- Contributions without proper testing
|
||||||
- Code that doesn't follow project conventions
|
- Code that doesn't follow project conventions
|
||||||
|
- Dependencies or code with licenses incompatible with MIT
|
||||||
|
|
||||||
## 📄 Legal
|
## 📄 Legal
|
||||||
|
|
||||||
By contributing to this project, you agree that your contributions will be licensed under the MIT License.
|
By contributing to this project, you agree that your contributions will be licensed under the MIT License.
|
||||||
|
|
||||||
> You may need to sign a github CLA before you create a PR.
|
> You need to sign a github CLA when you create a PR.
|
||||||
|
|
||||||
## 💬 Questions?
|
## 💬 Questions?
|
||||||
|
|
||||||
@@ -210,4 +215,4 @@ By contributing to this project, you agree that your contributions will be licen
|
|||||||
- Check existing documentation and issues first
|
- Check existing documentation and issues first
|
||||||
- Be respectful and constructive in discussions
|
- Be respectful and constructive in discussions
|
||||||
|
|
||||||
Thank you for helping make Page-Agent better! 🎉
|
Thank you for helping make PageAgent better! 🎉
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
|
|||||||
|
|
||||||
🌐 **English** | [中文](./docs/README-zh.md)
|
🌐 **English** | [中文](./docs/README-zh.md)
|
||||||
|
|
||||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a> | <a href="https://news.ycombinator.com/item?id=47264138" target="_blank">📢 Join HN Discussion</a>
|
||||||
|
|
||||||
<video id="demo-video" src="https://github.com/user-attachments/assets/11aed429-b69c-49d5-8982-fa99b4a0e9a8" controls crossorigin muted></video>
|
<video id="demo-video" src="https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2" controls crossorigin muted></video>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@ Fastest way to try PageAgent with our free Demo LLM:
|
|||||||
|
|
||||||
| Mirrors | URL |
|
| Mirrors | URL |
|
||||||
| ------- | ---------------------------------------------------------------------------------- |
|
| ------- | ---------------------------------------------------------------------------------- |
|
||||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js |
|
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.3/dist/iife/page-agent.demo.js |
|
||||||
| China | https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js |
|
| China | https://registry.npmmirror.com/page-agent/1.5.3/files/dist/iife/page-agent.demo.js |
|
||||||
|
|
||||||
> **⚠️ For technical evaluation only.** This demo CDN uses our free [testing LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api). By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
|
> **⚠️ For technical evaluation only.** This demo CDN uses our free [testing LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api). By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
|
||||||
|
|
||||||
@@ -111,3 +111,11 @@ file and in the node_modules directory after installation.
|
|||||||
---
|
---
|
||||||
|
|
||||||
**⭐ Star this repo if you find PageAgent helpful!**
|
**⭐ Star this repo if you find PageAgent helpful!**
|
||||||
|
|
||||||
|
<a href="https://www.star-history.com/?repos=alibaba%2Fpage-agent&type=date">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=alibaba/page-agent&type=date&theme=dark" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=alibaba/page-agent&type=date" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=alibaba/page-agent&type=date" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|||||||
+59
-1
@@ -5,6 +5,63 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.5.1] - 2026-03-05
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
- **`data-browser-use-ignore` → `data-page-agent-ignore`** - DOM ignore attribute renamed to match the project identity
|
||||||
|
- **Config types restructured** - `PageAgentConfig` split into `AgentConfig` + `PageAgentCoreConfig`; config definitions moved from `config/index.ts` to `types.ts`
|
||||||
|
- **Zod v3/v4 dual support** - Libraries now accept both `zod@^3.25` and `zod@^4.0` as peer dependencies
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **Experimental `llms.txt` support** - Agent can fetch and include a site's `llms.txt` in context. Enable via `experimentalLlmsTxt: true`
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- Default `maxSteps` changed from 20 to 40 for better for complex tasks out of the box
|
||||||
|
- Added 400ms wait between agent steps for page reactions
|
||||||
|
- Increased click wait time (100ms → 200ms) for more reliable interactions
|
||||||
|
- Removed debug `console.log` statements from scroll actions
|
||||||
|
- Reset observations on new task start
|
||||||
|
- Improved logging across packages
|
||||||
|
|
||||||
|
### Extension v0.1.9
|
||||||
|
|
||||||
|
> PageAgent 1.5.1
|
||||||
|
|
||||||
|
- **Advanced config panel** - New collapsible section exposing Max Steps, System Instruction, and experimental `llms.txt` toggle
|
||||||
|
- Streamlined User Auth Token description
|
||||||
|
- Moved testing API notice below auth token section
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.4.0] - 2026-02-27
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Update Terms of Use and Privacy Policy
|
||||||
|
- **Robust tool-call validation** - Action inputs are now validated against tool schemas individually, producing clear error messages (e.g. `Invalid input for action "click_element_by_index"`) instead of unreadable union parse errors
|
||||||
|
- **Primitive action input coercion** - Small models that output `{"click_element_by_index": 2}` instead of `{"click_element_by_index": {"index": 2}}` are now auto-corrected using tool schemas
|
||||||
|
- **Qwen model updates** - Added `qwen3.5-plus` as the default free testing model; disabled `enable_thinking` for Qwen models to avoid incompatible responses
|
||||||
|
- **Updated default LLM endpoint** - Migrated demo and extension to a new testing endpoint with legacy endpoint auto-migration
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- Unified zod imports (`* as z`) across all packages for consistency
|
||||||
|
- Better Zod error formatting with `z.prettifyError()` in LLM client
|
||||||
|
- Exported `InvokeError` and `InvokeErrorType` as values (not just types) from `@page-agent/llms`
|
||||||
|
- Exported `SupportedLanguage` type from `@page-agent/core`
|
||||||
|
|
||||||
|
### Extension v0.1.8
|
||||||
|
|
||||||
|
- **Language setting** - Added language selector (System / English / 中文) in config panel
|
||||||
|
- **UI makeover** - New empty state with breathing glow and typing animation; ai-motion glow overlay while running; refined focus styles
|
||||||
|
- **Testing endpoint notice** - Shows terms of use notice when using the free testing API
|
||||||
|
- **Legacy endpoint migration** - Auto-migrates old Supabase testing endpoint to new endpoint on startup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.3.0] - 2026-02-13
|
## [1.3.0] - 2026-02-13
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
@@ -96,9 +153,10 @@ PageAgent is now ready for production use. The API is stable and breaking change
|
|||||||
- **Ask User Tool** - Agent can ask users for clarification
|
- **Ask User Tool** - Agent can ask users for clarification
|
||||||
- **i18n Support** - English and Chinese localization
|
- **i18n Support** - English and Chinese localization
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
// Version 1.0.0
|
||||||
interface PageAgentConfig {
|
interface PageAgentConfig {
|
||||||
// LLM Configuration (required)
|
// LLM Configuration (required)
|
||||||
baseURL: string
|
baseURL: string
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
||||||
|
|
||||||
<video id="demo-video" src="https://github.com/user-attachments/assets/11aed429-b69c-49d5-8982-fa99b4a0e9a8" controls crossorigin muted></video>
|
<video id="demo-video" src="https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2" controls crossorigin muted></video>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
|
|
||||||
| Mirrors | URL |
|
| Mirrors | URL |
|
||||||
| ------- | ---------------------------------------------------------------------------------- |
|
| ------- | ---------------------------------------------------------------------------------- |
|
||||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js |
|
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.3/dist/iife/page-agent.demo.js |
|
||||||
| China | https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js |
|
| China | https://registry.npmmirror.com/page-agent/1.5.3/files/dist/iife/page-agent.demo.js |
|
||||||
|
|
||||||
> **⚠️ 仅用于技术评估。** 该 Demo CDN 使用了免费的[测试 LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api),使用即表示您同意其[条款](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md)。
|
> **⚠️ 仅用于技术评估。** 该 Demo CDN 使用了免费的[测试 LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api),使用即表示您同意其[条款](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md)。
|
||||||
|
|
||||||
|
|||||||
+19
-17
@@ -1,20 +1,20 @@
|
|||||||
# Terms of Use & Privacy
|
# Terms of Use & Privacy
|
||||||
|
|
||||||
**Last updated:** February 2026
|
**Last updated:** March 2026
|
||||||
|
|
||||||
"We" in this document refers to the maintainers of the open-source Page Agent project (https://github.com/alibaba/page-agent). This document covers the Page Agent software itself and the testing services we provide — **not** any third-party product or service built with it.
|
"We" in this document refers to the maintainers of the open-source Page Agent project (https://github.com/alibaba/page-agent). "The software" refers to PageAgent.js (the JavaScript library) and Page Agent Ext (the browser extension). This document covers the software itself and the testing API we provide — **not** any third-party product or service built with it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Open Source Software Privacy
|
## 1. Open Source Software Privacy
|
||||||
|
|
||||||
Page Agent (PageAgent.js and Page Agent Extension) is a **client-side only** tool with a "Bring Your Own Key" (BYOK) architecture. The software itself does **not** include any backend service. The software does **not** collect or transmit any user data on its own, and the maintainers do **not** have access to your browsing activity, page content, or task instructions through the software.
|
The software is a **client-side only** tool with a "Bring Your Own Key" (BYOK) architecture. The software itself does **not** include any backend service. The software does **not** collect or transmit any user data on its own, and we do **not** have access to your browsing activity, page content, or task instructions through the software.
|
||||||
|
|
||||||
All data transmission occurs **only** between your browser and the LLM provider you configure. You are in full control of which provider receives your data.
|
All data transmission occurs **only** between your browser and the LLM provider you configure. You are in full control of which provider receives your data.
|
||||||
|
|
||||||
- You choose which LLM provider to use
|
- You choose which LLM provider to use
|
||||||
- You may configure your own API endpoint at any time
|
- You may configure your own API endpoint at any time
|
||||||
- The project is open source and can be audited: https://github.com/alibaba/page-agent
|
- The project is open source under the [MIT License](https://github.com/alibaba/page-agent/blob/main/LICENSE) and can be audited at: https://github.com/alibaba/page-agent
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -22,21 +22,23 @@ All data transmission occurs **only** between your browser and the LLM provider
|
|||||||
|
|
||||||
To facilitate easy testing and technical evaluation, we provide a free testing LLM API. This API is used in the project homepage's live demo, the pre-built demo CDN bundles, and the browser extension's default configuration. Users may also use it independently for their own technical evaluation of page-agent.
|
To facilitate easy testing and technical evaluation, we provide a free testing LLM API. This API is used in the project homepage's live demo, the pre-built demo CDN bundles, and the browser extension's default configuration. Users may also use it independently for their own technical evaluation of page-agent.
|
||||||
|
|
||||||
This free testing API is provided **strictly for technical evaluation and R&D purposes only**. It must not be used in any production environment. By using this service, you agree to the following terms:
|
This free testing API is provided **strictly for technical evaluation and R&D purposes only**. It must not be used in any production environment. By using this API, you agree to the following terms:
|
||||||
|
|
||||||
**No Sensitive Data**: You are strictly prohibited from inputting any Personal Identifiable Information (PII), confidential business data, financial/medical records, or using this agent on web pages containing such sensitive information.
|
- **No Sensitive Data**: You are strictly prohibited from inputting any Personal Identifiable Information (PII), confidential business data, financial/medical records, or using this agent on web pages containing such sensitive information.
|
||||||
|
|
||||||
**Zero Data Retention**: We do not store, log, or use your prompts or webpage data (DOM) for model training. All data is processed in-transit and immediately discarded. However, we do temporarily process necessary, identity-agnostic network data (such as IP addresses) solely for rate-limiting and anti-abuse purposes.
|
- **Data Processing**: We do not store or log your prompts, webpage data (HTML), or any submitted content, nor do we use such data for model training. All data is processed in-transit and immediately discarded. We perform in-memory request validation to prevent abuse of the testing API, and temporarily process IP addresses for rate-limiting purposes. No data from these processes is retained. Data is processed through Alibaba Cloud infrastructure, which is subject to its own privacy policy.
|
||||||
|
|
||||||
**Independent Infrastructure**: The `page-agent` open-source software is completely frontend-based with a "Bring Your Own Key" (BYOK) architecture and no built-in backend. To facilitate easy testing, the maintainers have independently purchased public cloud services from Alibaba Cloud China ([aliyun.com](https://www.aliyun.com) FC and BaiLian Qwen models). This project is not affiliated with, nor endorsed by, Alibaba Cloud.
|
- **Independent Infrastructure**: The software is completely frontend-based with a "Bring Your Own Key" (BYOK) architecture and **no built-in backend**. To facilitate easy testing, the maintainers have purchased public cloud services from Alibaba Cloud China ([aliyun.com](https://www.aliyun.com) Function Compute and BaiLian Qwen models). This project is not a product of, nor endorsed by, Alibaba Cloud.
|
||||||
|
|
||||||
**No Guaranteed Availability**: This testing API may be rate-limited, degraded, or discontinued at any time without prior notice.
|
- **No Guaranteed Availability**: This testing API may be rate-limited, degraded, or discontinued at any time without prior notice.
|
||||||
|
|
||||||
**"AS IS" & Limitation of Liability**: This service is provided strictly on an "AS IS" and "AS AVAILABLE" basis, without any warranties. The maintainers bear no liability for any data loss, service interruption, or legal consequences arising from your use of this demo.
|
- **"AS IS" & Limitation of Liability**: This service is provided strictly on an "AS IS" and "AS AVAILABLE" basis, without any warranties. The maintainers bear no liability for any data loss, service interruption, or legal consequences arising from your use of this service.
|
||||||
|
|
||||||
**Recommendation for Real Usage**: For secure and continuous usage, we strongly advise using the default BYOK mode with your own legally compliant commercial LLM API keys, or connecting to local, offline models (e.g., Ollama).
|
- **Recommendation for Real Usage**: For secure and continuous usage, we strongly advise using the BYOK mode with your own legally compliant commercial LLM API keys, or connecting to local, offline models (e.g., Ollama).
|
||||||
|
|
||||||
**Note**: This service processes data via servers located in Mainland China. If you are located in a region with strict data localization laws (such as the EU/EEA), please do not use this demo.
|
**Note**: This free testing LLM API processes data via servers located in Mainland China. If you are located in a region with strict data localization laws (such as the EU/EEA), please do not use this API.
|
||||||
|
|
||||||
|
**Age Requirement**: The software and testing API are not intended for use by individuals under the age of 13 (or the minimum age of digital consent in your jurisdiction).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -49,19 +51,19 @@ The extension performs DOM analysis and automation actions **locally in your bro
|
|||||||
Data is transmitted to external servers **only when you initiate an automation task**. When this occurs:
|
Data is transmitted to external servers **only when you initiate an automation task**. When this occurs:
|
||||||
|
|
||||||
- Your task instructions (natural language commands)
|
- Your task instructions (natural language commands)
|
||||||
- Simplified page structure (cleaned DOM) of all pages under the extension's control
|
- Simplified page structure (cleaned HTML) of all pages under the extension's control
|
||||||
|
|
||||||
are sent to the LLM API endpoint configured in **your settings**.
|
are sent to the LLM API endpoint configured in **your settings**.
|
||||||
|
|
||||||
> **Note:** The DOM cleaning process simplifies page structure for AI readability but **does not guarantee removal of sensitive information** (e.g., visible text, form values, or personal data on the page). Please be mindful of the page content when initiating tasks.
|
> **Note:** The HTML cleaning process simplifies page structure for AI readability but **does not guarantee removal of sensitive information** (e.g., visible text, form values, or personal data on the page). Please be mindful of the page content when initiating tasks.
|
||||||
|
|
||||||
**If you configure a third-party LLM provider** (e.g., OpenAI, Anthropic, or others), data is sent directly to that provider. Their privacy policies apply.
|
**If you configure a third-party LLM provider** (e.g., OpenAI, Anthropic, or others), data is sent directly to that provider. Their privacy policies apply.
|
||||||
|
|
||||||
**If you use the built-in testing API**, the terms in [Section 2](#2-testing-api-and-demo-disclaimer--terms-of-use) apply. By using the extension with the default testing API, you agree to those terms.
|
**If you use the testing API**, the terms in [Section 2](#2-testing-api-and-demo-disclaimer--terms-of-use) apply. By using the extension with the default testing API, you agree to those terms.
|
||||||
|
|
||||||
### Data Storage
|
### Data Storage
|
||||||
|
|
||||||
- **Local storage only**: Your configuration (API endpoint, API key, model selection) is stored in your browser via `chrome.storage.local`
|
- **Local storage only**: Your configuration (API endpoint, API key, model selection) is stored in your browser via `chrome.storage.local` (or equivalent browser storage APIs)
|
||||||
- **No cloud sync**: Configuration is not synced to any external server
|
- **No cloud sync**: Configuration is not synced to any external server
|
||||||
- **No analytics**: The extension does not include any analytics or tracking code
|
- **No analytics**: The extension does not include any analytics or tracking code
|
||||||
|
|
||||||
@@ -76,7 +78,7 @@ are sent to the LLM API endpoint configured in **your settings**.
|
|||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
We may update these terms as the project evolves.
|
We may update these terms at our discretion.
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
|
|||||||
Generated
+120
-151
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
@@ -21,10 +21,10 @@
|
|||||||
"@commitlint/cli": "^20.4.2",
|
"@commitlint/cli": "^20.4.2",
|
||||||
"@commitlint/config-conventional": "^20.4.2",
|
"@commitlint/config-conventional": "^20.4.2",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@microsoft/api-extractor": "^7.57.3",
|
"@microsoft/api-extractor": "^7.57.6",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.3.0",
|
"@types/node": "^25.3.3",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
@@ -35,16 +35,16 @@
|
|||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"eslint-plugin-react-x": "^2.13.0",
|
"eslint-plugin-react-x": "^2.13.0",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.4.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.4",
|
"lint-staged": "^16.3.1",
|
||||||
"prettier": "^3.8.0",
|
"prettier": "^3.8.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.56.1",
|
"typescript-eslint": "^8.56.1",
|
||||||
"unplugin-dts": "^1.0.0-beta.6",
|
"unplugin-dts": "^1.0.0-beta.6",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-bundle-analyzer": "^1.3.6",
|
"vite-bundle-analyzer": "^1.3.6",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.2"
|
"vite-plugin-css-injected-by-js": "^4.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||||
@@ -1685,19 +1685,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/api-extractor": {
|
"node_modules/@microsoft/api-extractor": {
|
||||||
"version": "7.57.3",
|
"version": "7.57.6",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.3.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.6.tgz",
|
||||||
"integrity": "sha512-2+k2FNp+6zug/VmpK0wZFPQu7cKMihjHBP1iUuZs6Ep5P9uR1hD4dR5Ss7z/MiBkwVmKnUm6Pojhkz/c431SMw==",
|
"integrity": "sha512-0rFv/D8Grzw1Mjs2+8NGUR+o4h9LVm5zKRtMeWnpdB5IMJF4TeHCL1zR5LMCIudkOvyvjbhMG5Wjs0B5nqsrRQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/api-extractor-model": "7.33.1",
|
"@microsoft/api-extractor-model": "7.33.4",
|
||||||
"@microsoft/tsdoc": "~0.16.0",
|
"@microsoft/tsdoc": "~0.16.0",
|
||||||
"@microsoft/tsdoc-config": "~0.18.0",
|
"@microsoft/tsdoc-config": "~0.18.1",
|
||||||
"@rushstack/node-core-library": "5.20.1",
|
"@rushstack/node-core-library": "5.20.3",
|
||||||
"@rushstack/rig-package": "0.7.1",
|
"@rushstack/rig-package": "0.7.2",
|
||||||
"@rushstack/terminal": "0.22.1",
|
"@rushstack/terminal": "0.22.3",
|
||||||
"@rushstack/ts-command-line": "5.3.1",
|
"@rushstack/ts-command-line": "5.3.3",
|
||||||
"diff": "~8.0.2",
|
"diff": "~8.0.2",
|
||||||
"lodash": "~4.17.23",
|
"lodash": "~4.17.23",
|
||||||
"minimatch": "10.2.1",
|
"minimatch": "10.2.1",
|
||||||
@@ -1711,15 +1711,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/api-extractor-model": {
|
"node_modules/@microsoft/api-extractor-model": {
|
||||||
"version": "7.33.1",
|
"version": "7.33.4",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.1.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.4.tgz",
|
||||||
"integrity": "sha512-KX0LI6xzI0gcBOXXmr5mnnbdhsK2W93pqvJo8OgJgWvRRh+wMEp0Ccj38h1XKeJ29E1tuAZKSUOfHUQ1WA8fZg==",
|
"integrity": "sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/tsdoc": "~0.16.0",
|
"@microsoft/tsdoc": "~0.16.0",
|
||||||
"@microsoft/tsdoc-config": "~0.18.0",
|
"@microsoft/tsdoc-config": "~0.18.1",
|
||||||
"@rushstack/node-core-library": "5.20.1"
|
"@rushstack/node-core-library": "5.20.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/api-extractor/node_modules/lru-cache": {
|
"node_modules/@microsoft/api-extractor/node_modules/lru-cache": {
|
||||||
@@ -1766,35 +1766,18 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/tsdoc-config": {
|
"node_modules/@microsoft/tsdoc-config": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz",
|
||||||
"integrity": "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==",
|
"integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/tsdoc": "0.16.0",
|
"@microsoft/tsdoc": "0.16.0",
|
||||||
"ajv": "~8.12.0",
|
"ajv": "~8.18.0",
|
||||||
"jju": "~1.4.0",
|
"jju": "~1.4.0",
|
||||||
"resolve": "~1.22.2"
|
"resolve": "~1.22.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/tsdoc-config/node_modules/ajv": {
|
|
||||||
"version": "8.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -2948,13 +2931,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rushstack/node-core-library": {
|
"node_modules/@rushstack/node-core-library": {
|
||||||
"version": "5.20.1",
|
"version": "5.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.20.3.tgz",
|
||||||
"integrity": "sha512-QvxZyh+RsTJ77JpQkS9K9lJujh6lj5WyMxieT0bdACtwqxEkGB9zCuSMX5UlXRweaIgSpu1ztdHmhV07fKUpMg==",
|
"integrity": "sha512-95JgEPq2k7tHxhF9/OJnnyHDXfC9cLhhta0An/6MlkDsX2A6dTzDrTUG18vx4vjc280V0fi0xDH9iQczpSuWsw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "~8.13.0",
|
"ajv": "~8.18.0",
|
||||||
"ajv-draft-04": "~1.0.0",
|
"ajv-draft-04": "~1.0.0",
|
||||||
"ajv-formats": "~3.0.1",
|
"ajv-formats": "~3.0.1",
|
||||||
"fs-extra": "~11.3.0",
|
"fs-extra": "~11.3.0",
|
||||||
@@ -2972,23 +2955,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rushstack/node-core-library/node_modules/ajv": {
|
|
||||||
"version": "8.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
|
|
||||||
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.4.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
|
"node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -3041,9 +3007,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rushstack/rig-package": {
|
"node_modules/@rushstack/rig-package": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.2.tgz",
|
||||||
"integrity": "sha512-hLwDnp4yMcAd/gcUol8NPWNctpIXzVOgMyhZ8DagnEJls9TOZd0xF//5hS+YTiX7/+4rLfBra+NoB3rtFxjDdA==",
|
"integrity": "sha512-9XbFWuqMYcHUso4mnETfhGVUSaADBRj6HUAAEYk50nMPn8WRICmBuCphycQGNB3duIR6EEZX3Xj3SYc2XiP+9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3052,13 +3018,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rushstack/terminal": {
|
"node_modules/@rushstack/terminal": {
|
||||||
"version": "0.22.1",
|
"version": "0.22.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.22.3.tgz",
|
||||||
"integrity": "sha512-Mdtu0VN7v31O5Zcno8ZZH5kQHF13Ez7WN9Aio7nFJVcR36i4bkERionYrWgBDQJ0JdVPLKGecZER/xRU5IvGLw==",
|
"integrity": "sha512-gHC9pIMrUPzAbBiI4VZMU7Q+rsCzb8hJl36lFIulIzoceKotyKL3Rd76AZ2CryCTKEg+0bnTj406HE5YY5OQvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rushstack/node-core-library": "5.20.1",
|
"@rushstack/node-core-library": "5.20.3",
|
||||||
"@rushstack/problem-matcher": "0.2.1",
|
"@rushstack/problem-matcher": "0.2.1",
|
||||||
"supports-color": "~8.1.1"
|
"supports-color": "~8.1.1"
|
||||||
},
|
},
|
||||||
@@ -3072,13 +3038,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rushstack/ts-command-line": {
|
"node_modules/@rushstack/ts-command-line": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.3.tgz",
|
||||||
"integrity": "sha512-mid/JIZSJafwy3x9e4v0wVLuAqSSYYErEHV0HXPALYLSBN13YNkR5caOk0hf97lSRKrxhtvQjGaDKSEelR3sMg==",
|
"integrity": "sha512-c+ltdcvC7ym+10lhwR/vWiOhsrm/bP3By2VsFcs5qTKv+6tTmxgbVrtJ5NdNjANiV5TcmOZgUN+5KYQ4llsvEw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rushstack/terminal": "0.22.1",
|
"@rushstack/terminal": "0.22.3",
|
||||||
"@types/argparse": "1.0.38",
|
"@types/argparse": "1.0.38",
|
||||||
"argparse": "~1.0.9",
|
"argparse": "~1.0.9",
|
||||||
"string-argv": "~0.3.1"
|
"string-argv": "~0.3.1"
|
||||||
@@ -3822,9 +3788,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.3.0",
|
"version": "25.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
|
||||||
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
|
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4309,9 +4275,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.17.1",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6436,13 +6402,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/framer-motion": {
|
"node_modules/framer-motion": {
|
||||||
"version": "12.34.3",
|
"version": "12.34.5",
|
||||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.3.tgz",
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.5.tgz",
|
||||||
"integrity": "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q==",
|
"integrity": "sha512-Z2dQ+o7BsfpJI3+u0SQUNCrN+ajCKJen1blC4rCHx1Ta2EOHs+xKJegLT2aaD9iSMbU3OoX+WabQXkloUbZmJQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"motion-dom": "^12.34.3",
|
"motion-dom": "^12.34.5",
|
||||||
"motion-utils": "^12.29.2",
|
"motion-utils": "^12.29.2",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
@@ -6688,9 +6654,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "17.3.0",
|
"version": "17.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
|
||||||
"integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
|
"integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -7775,19 +7741,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lint-staged": {
|
"node_modules/lint-staged": {
|
||||||
"version": "16.2.7",
|
"version": "16.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.1.tgz",
|
||||||
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
|
"integrity": "sha512-bqvvquXzFBAlSbluugR4KXAe4XnO/QZcKVszpkBtqLWa2KEiVy8n6Xp38OeUbv/gOJOX4Vo9u5pFt/ADvbm42Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^14.0.2",
|
"commander": "^14.0.3",
|
||||||
"listr2": "^9.0.5",
|
"listr2": "^9.0.5",
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
"nano-spawn": "^2.0.0",
|
|
||||||
"pidtree": "^0.6.0",
|
|
||||||
"string-argv": "^0.3.2",
|
"string-argv": "^0.3.2",
|
||||||
"yaml": "^2.8.1"
|
"tinyexec": "^1.0.2",
|
||||||
|
"yaml": "^2.8.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"lint-staged": "bin/lint-staged.js"
|
"lint-staged": "bin/lint-staged.js"
|
||||||
@@ -7962,9 +7927,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "0.575.0",
|
"version": "0.576.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.575.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.576.0.tgz",
|
||||||
"integrity": "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg==",
|
"integrity": "sha512-koNxU14BXrxUfZQ9cUaP0ES1uyPZKYDjk31FQZB6dQ/x+tXk979sVAn9ppZ/pVeJJyOxVM8j1E+8QEuSc02Vug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -8156,13 +8121,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/motion": {
|
"node_modules/motion": {
|
||||||
"version": "12.34.3",
|
"version": "12.34.5",
|
||||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.3.tgz",
|
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.5.tgz",
|
||||||
"integrity": "sha512-xZIkBGO7v/Uvm+EyaqYd+9IpXu0sZqLywVlGdCFrrMiaO9JI4Kx51mO9KlHSWwll+gZUVY5OJsWgYI5FywJ/tw==",
|
"integrity": "sha512-N06NLJ9IeBHeielRqIvYvjPfXuRdyTxa+9++BgpGa+hY2D7TcMkI6QzV3jaRuv0aZRXgMa7cPy9YcBUBisPzAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"framer-motion": "^12.34.3",
|
"framer-motion": "^12.34.5",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -8183,9 +8148,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/motion-dom": {
|
"node_modules/motion-dom": {
|
||||||
"version": "12.34.3",
|
"version": "12.34.5",
|
||||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.3.tgz",
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.5.tgz",
|
||||||
"integrity": "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==",
|
"integrity": "sha512-k33CsnxO2K3gBRMUZT+vPmc4Utlb5menKdG0RyVNLtlqRaaJPRWlE9fXl8NTtfZ5z3G8TDvqSu0MENLqSTaHZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -8710,19 +8675,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pidtree": {
|
|
||||||
"version": "0.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
|
||||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"pidtree": "bin/pidtree.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pino": {
|
"node_modules/pino": {
|
||||||
"version": "9.7.0",
|
"version": "9.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
|
||||||
@@ -9560,9 +9512,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/simple-icons": {
|
"node_modules/simple-icons": {
|
||||||
"version": "16.9.0",
|
"version": "16.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.10.0.tgz",
|
||||||
"integrity": "sha512-aKst2C7cLkFyaiQ/Crlwxt9xYOpGPk05XuJZ0ZTJNNCzHCKYrGWz2ebJSi5dG8CmTCxUF/BGs6A8uyJn/EQxqw==",
|
"integrity": "sha512-62kuxaG3pE+cFNerudUtwb9BLmudzayHrlHkvU9gf8Nxcj7VYOm9dh3WNbGaFk60aQtfnRyzViZaouFG2B45kg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10554,9 +10506,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-css-injected-by-js": {
|
"node_modules/vite-plugin-css-injected-by-js": {
|
||||||
"version": "3.5.2",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-4.0.1.tgz",
|
||||||
"integrity": "sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==",
|
"integrity": "sha512-WfyRojojQyAO/KzWf+efcXpTPv6zJPXaRmr9EYq5a4v5I3PWCR7kR01hiri2lW6+rHm3a57kpwsf+iahIJi1Qw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -11161,27 +11113,31 @@
|
|||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@page-agent/core",
|
"name": "@page-agent/core",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.5.3",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/extension": {
|
"packages/extension": {
|
||||||
"name": "@page-agent/ext",
|
"name": "@page-agent/ext",
|
||||||
"version": "0.1.8",
|
"version": "0.1.15",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.5.3",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.5.3",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.5.3",
|
||||||
"ai-motion": "^0.4.8",
|
"ai-motion": "^0.4.8",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
"zod": "^4.3.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-hover-card": "^1.1.15",
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
@@ -11198,44 +11154,57 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.576.0",
|
||||||
"motion": "^12.34.3",
|
"motion": "^12.34.5",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.9.0",
|
"simple-icons": "^16.10.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"wxt": "^0.20.18"
|
"wxt": "^0.20.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/llms": {
|
"packages/llms": {
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-agent": {
|
"packages/page-agent": {
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.5.3",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.5.3",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.5.3",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-controller": {
|
"packages/page-controller": {
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ai-motion": "^0.4.8"
|
"ai-motion": "^0.4.8"
|
||||||
@@ -11243,12 +11212,12 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"packages/website": {
|
"packages/website": {
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
@@ -11259,13 +11228,13 @@
|
|||||||
"@types/react-dom": "^19.2.1",
|
"@types/react-dom": "^19.2.1",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.576.0",
|
||||||
"motion": "^12.34.3",
|
"motion": "^12.34.5",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.9.0",
|
"simple-icons": "^16.10.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
|
|||||||
+6
-6
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
"@commitlint/cli": "^20.4.2",
|
"@commitlint/cli": "^20.4.2",
|
||||||
"@commitlint/config-conventional": "^20.4.2",
|
"@commitlint/config-conventional": "^20.4.2",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@microsoft/api-extractor": "^7.57.3",
|
"@microsoft/api-extractor": "^7.57.6",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.3.0",
|
"@types/node": "^25.3.3",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
@@ -52,15 +52,15 @@
|
|||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"eslint-plugin-react-x": "^2.13.0",
|
"eslint-plugin-react-x": "^2.13.0",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.4.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.4",
|
"lint-staged": "^16.3.1",
|
||||||
"prettier": "^3.8.0",
|
"prettier": "^3.8.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.56.1",
|
"typescript-eslint": "^8.56.1",
|
||||||
"unplugin-dts": "^1.0.0-beta.6",
|
"unplugin-dts": "^1.0.0-beta.6",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.2",
|
"vite-plugin-css-injected-by-js": "^4.0.1",
|
||||||
"vite-bundle-analyzer": "^1.3.6"
|
"vite-bundle-analyzer": "^1.3.6"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/core",
|
"name": "@page-agent/core",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/esm/page-agent-core.js",
|
"main": "./dist/esm/page-agent-core.js",
|
||||||
"module": "./dist/esm/page-agent-core.js",
|
"module": "./dist/esm/page-agent-core.js",
|
||||||
@@ -44,8 +44,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/page-controller": "1.5.3"
|
||||||
"@page-agent/page-controller": "1.4.0"
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,13 @@
|
|||||||
import { InvokeError, LLM, type Tool } from '@page-agent/llms'
|
import { InvokeError, LLM, type Tool } from '@page-agent/llms'
|
||||||
import type { BrowserState, PageController } from '@page-agent/page-controller'
|
import type { BrowserState, PageController } from '@page-agent/page-controller'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import { type PageAgentConfig, type SupportedLanguage } from './config'
|
|
||||||
import { DEFAULT_MAX_STEPS } from './config/constants'
|
|
||||||
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
||||||
import { tools } from './tools'
|
import { tools } from './tools'
|
||||||
import type {
|
import type {
|
||||||
AgentActivity,
|
AgentActivity,
|
||||||
|
AgentConfig,
|
||||||
AgentReflection,
|
AgentReflection,
|
||||||
AgentStatus,
|
AgentStatus,
|
||||||
AgentStepEvent,
|
AgentStepEvent,
|
||||||
@@ -21,13 +20,13 @@ import type {
|
|||||||
MacroToolInput,
|
MacroToolInput,
|
||||||
MacroToolResult,
|
MacroToolResult,
|
||||||
} from './types'
|
} from './types'
|
||||||
import { assert, normalizeResponse, uid, waitFor } from './utils'
|
import { assert, fetchLlmsTxt, normalizeResponse, uid, waitFor } from './utils'
|
||||||
|
|
||||||
export { type PageAgentConfig }
|
|
||||||
export type { SupportedLanguage }
|
|
||||||
export { tool, type PageAgentTool } from './tools'
|
export { tool, type PageAgentTool } from './tools'
|
||||||
export type * from './types'
|
export type * from './types'
|
||||||
|
|
||||||
|
export type PageAgentCoreConfig = AgentConfig & { pageController: PageController }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI agent for browser automation.
|
* AI agent for browser automation.
|
||||||
*
|
*
|
||||||
@@ -60,7 +59,7 @@ export type * from './types'
|
|||||||
*/
|
*/
|
||||||
export class PageAgentCore extends EventTarget {
|
export class PageAgentCore extends EventTarget {
|
||||||
readonly id = uid()
|
readonly id = uid()
|
||||||
readonly config: PageAgentConfig & { maxSteps: number }
|
readonly config: PageAgentCoreConfig & { maxSteps: number }
|
||||||
readonly tools: typeof tools
|
readonly tools: typeof tools
|
||||||
/** PageController for DOM operations */
|
/** PageController for DOM operations */
|
||||||
readonly pageController: PageController
|
readonly pageController: PageController
|
||||||
@@ -94,10 +93,10 @@ export class PageAgentCore extends EventTarget {
|
|||||||
browserState: null as BrowserState | null,
|
browserState: null as BrowserState | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(config: PageAgentConfig & { pageController: PageController }) {
|
constructor(config: PageAgentCoreConfig) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.config = { ...config, maxSteps: config.maxSteps || DEFAULT_MAX_STEPS }
|
this.config = { ...config, maxSteps: config.maxSteps ?? 40 }
|
||||||
|
|
||||||
this.#llm = new LLM(this.config)
|
this.#llm = new LLM(this.config)
|
||||||
this.tools = new Map(tools)
|
this.tools = new Map(tools)
|
||||||
@@ -222,6 +221,7 @@ export class PageAgentCore extends EventTarget {
|
|||||||
this.history = []
|
this.history = []
|
||||||
this.#setStatus('running')
|
this.#setStatus('running')
|
||||||
this.#emitHistoryChange()
|
this.#emitHistoryChange()
|
||||||
|
this.#observations = []
|
||||||
|
|
||||||
// Reset internal states
|
// Reset internal states
|
||||||
this.#states = { totalWaitTime: 0, lastURL: '', browserState: null }
|
this.#states = { totalWaitTime: 0, lastURL: '', browserState: null }
|
||||||
@@ -342,6 +342,8 @@ export class PageAgentCore extends EventTarget {
|
|||||||
await onAfterTask?.(this, result)
|
await onAfterTask?.(this, result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await waitFor(0.4) // @TODO: configurable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,14 +462,13 @@ export class PageAgentCore extends EventTarget {
|
|||||||
* Get instructions from config
|
* Get instructions from config
|
||||||
*/
|
*/
|
||||||
async #getInstructions(): Promise<string> {
|
async #getInstructions(): Promise<string> {
|
||||||
const { instructions } = this.config
|
const { instructions, experimentalLlmsTxt } = this.config
|
||||||
if (!instructions) return ''
|
|
||||||
|
|
||||||
const systemInstructions = instructions.system?.trim()
|
const systemInstructions = instructions?.system?.trim()
|
||||||
let pageInstructions: string | undefined
|
let pageInstructions: string | undefined
|
||||||
|
|
||||||
const url = this.#states.browserState?.url || ''
|
const url = this.#states.browserState?.url || ''
|
||||||
if (instructions.getPageInstructions && url) {
|
if (instructions?.getPageInstructions && url) {
|
||||||
try {
|
try {
|
||||||
pageInstructions = instructions.getPageInstructions(url)?.trim()
|
pageInstructions = instructions.getPageInstructions(url)?.trim()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -477,7 +478,10 @@ export class PageAgentCore extends EventTarget {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!systemInstructions && !pageInstructions) return ''
|
|
||||||
|
const llmsTxt = experimentalLlmsTxt && url ? await fetchLlmsTxt(url) : undefined
|
||||||
|
|
||||||
|
if (!systemInstructions && !pageInstructions && !llmsTxt) return ''
|
||||||
|
|
||||||
let result = '<instructions>\n'
|
let result = '<instructions>\n'
|
||||||
|
|
||||||
@@ -489,6 +493,10 @@ export class PageAgentCore extends EventTarget {
|
|||||||
result += `<page_instructions>\n${pageInstructions}\n</page_instructions>\n`
|
result += `<page_instructions>\n${pageInstructions}\n</page_instructions>\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (llmsTxt) {
|
||||||
|
result += `<llms_txt>\n${llmsTxt}\n</llms_txt>\n`
|
||||||
|
}
|
||||||
|
|
||||||
result += '</instructions>\n\n'
|
result += '</instructions>\n\n'
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const DEFAULT_MAX_STEPS = 20
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
import type { LLMConfig } from '@page-agent/llms'
|
|
||||||
import type { PageControllerConfig } from '@page-agent/page-controller'
|
|
||||||
|
|
||||||
import type { PageAgentCore } from '../PageAgentCore'
|
|
||||||
import type { PageAgentTool } from '../tools'
|
|
||||||
import type { ExecutionResult, HistoricalEvent } from '../types'
|
|
||||||
|
|
||||||
export type { LLMConfig }
|
|
||||||
|
|
||||||
/** Supported UI languages */
|
|
||||||
export type SupportedLanguage = 'en-US' | 'zh-CN'
|
|
||||||
|
|
||||||
export interface AgentConfig {
|
|
||||||
// theme?: 'light' | 'dark'
|
|
||||||
language?: SupportedLanguage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of steps the agent can take per task.
|
|
||||||
* @default 20
|
|
||||||
*/
|
|
||||||
maxSteps?: number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom tools to extend PageAgent capabilities
|
|
||||||
* @experimental
|
|
||||||
* @note You can also override or remove internal tools by using the same name.
|
|
||||||
* @see PageAgentTool
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // override internal tool
|
|
||||||
* import { tool } from 'page-agent'
|
|
||||||
* const customTools = {
|
|
||||||
* ask_user: tool({
|
|
||||||
* description:
|
|
||||||
* 'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
|
|
||||||
* inputSchema: zod.object({
|
|
||||||
* question: zod.string(),
|
|
||||||
* }),
|
|
||||||
* execute: async function (this: PageAgent, input) {
|
|
||||||
* const answer = await do_some_thing(input.question)
|
|
||||||
* return "✅ Received user answer: " + answer
|
|
||||||
* },
|
|
||||||
* })
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // remove internal tool
|
|
||||||
* const customTools = {
|
|
||||||
* ask_user: null // never ask user questions
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
customTools?: Record<string, PageAgentTool | null>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructions to guide the agent's behavior
|
|
||||||
*/
|
|
||||||
instructions?: {
|
|
||||||
/**
|
|
||||||
* Global system-level instructions, applied to all tasks
|
|
||||||
*/
|
|
||||||
system?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamic page-level instructions callback
|
|
||||||
* Called before each step to get instructions for the current page
|
|
||||||
* @param url - Current page URL (window.location.href)
|
|
||||||
* @returns Instructions string, or undefined/null to skip
|
|
||||||
*/
|
|
||||||
getPageInstructions?: (url: string) => string | undefined | null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lifecycle hooks for task execution.
|
|
||||||
* @experimental API may change in future versions.
|
|
||||||
*
|
|
||||||
* All hooks receive the agent instance as first parameter.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called before each step execution.
|
|
||||||
* @experimental
|
|
||||||
* @param agent - The PageAgentCore instance
|
|
||||||
* @param stepCount - Current step number (0-indexed)
|
|
||||||
*/
|
|
||||||
onBeforeStep?: (agent: PageAgentCore, stepCount: number) => Promise<void> | void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after each step execution.
|
|
||||||
* @experimental
|
|
||||||
* @param agent - The PageAgentCore instance
|
|
||||||
* @param history - Current history of events
|
|
||||||
*/
|
|
||||||
onAfterStep?: (agent: PageAgentCore, history: HistoricalEvent[]) => Promise<void> | void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called before task execution starts.
|
|
||||||
* @experimental
|
|
||||||
* @param agent - The PageAgentCore instance
|
|
||||||
*/
|
|
||||||
onBeforeTask?: (agent: PageAgentCore) => Promise<void> | void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after task execution completes (success or failure).
|
|
||||||
* @experimental
|
|
||||||
* @param agent - The PageAgentCore instance
|
|
||||||
* @param result - The execution result
|
|
||||||
*/
|
|
||||||
onAfterTask?: (agent: PageAgentCore, result: ExecutionResult) => Promise<void> | void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the agent is disposed.
|
|
||||||
* @experimental
|
|
||||||
* @note This hook can block the disposal process if it's async.
|
|
||||||
* @param agent - The PageAgentCore instance
|
|
||||||
* @param reason - Optional reason for disposal
|
|
||||||
*/
|
|
||||||
onDispose?: (agent: PageAgentCore, reason?: string) => void
|
|
||||||
|
|
||||||
// page behavior hooks
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @experimental
|
|
||||||
* Enable the experimental script execution tool that allows executing generated JavaScript code on the page.
|
|
||||||
* @note Can cause unpredictable side effects.
|
|
||||||
* @note May bypass some safe guards and data-masking mechanisms.
|
|
||||||
*/
|
|
||||||
experimentalScriptExecutionTool?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform page content before sending to LLM.
|
|
||||||
* Called after DOM extraction and simplification, before LLM invocation.
|
|
||||||
* Use cases: inspect extraction results, modify page info, mask sensitive data.
|
|
||||||
*
|
|
||||||
* @param content - Simplified page content that will be sent to LLM
|
|
||||||
* @returns Transformed content
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // Mask phone numbers
|
|
||||||
* transformPageContent: async (content) => {
|
|
||||||
* return content.replace(/1[3-9]\d{9}/g, '***********')
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
transformPageContent?: (content: string) => Promise<string> | string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Completely override the default system prompt.
|
|
||||||
* @experimental Use with caution - incorrect prompts may break agent behavior.
|
|
||||||
*/
|
|
||||||
customSystemPrompt?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PageAgentConfig = LLMConfig & AgentConfig & PageControllerConfig
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Internal tools for PageAgent.
|
* Internal tools for PageAgent.
|
||||||
* @note Adapted from browser-use
|
* @note Adapted from browser-use
|
||||||
*/
|
*/
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import type { PageAgentCore } from '../PageAgentCore'
|
import type { PageAgentCore } from '../PageAgentCore'
|
||||||
import { waitFor } from '../utils'
|
import { waitFor } from '../utils'
|
||||||
|
|||||||
@@ -1,3 +1,159 @@
|
|||||||
|
import type { LLMConfig } from '@page-agent/llms'
|
||||||
|
|
||||||
|
// @note circular dependency but okay
|
||||||
|
import type { PageAgentCore } from './PageAgentCore'
|
||||||
|
import type { PageAgentTool } from './tools'
|
||||||
|
|
||||||
|
/** Supported UI languages */
|
||||||
|
export type SupportedLanguage = 'en-US' | 'zh-CN'
|
||||||
|
|
||||||
|
export interface AgentConfig extends LLMConfig {
|
||||||
|
language?: SupportedLanguage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of steps the agent can take per task.
|
||||||
|
* @default 40
|
||||||
|
*/
|
||||||
|
maxSteps?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom tools to extend PageAgent capabilities
|
||||||
|
* @experimental
|
||||||
|
* @note You can also override or remove internal tools by using the same name.
|
||||||
|
* @see PageAgentTool
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // override internal tool
|
||||||
|
* import { z } from 'zod/v4'
|
||||||
|
* import { tool } from 'page-agent'
|
||||||
|
* const customTools = {
|
||||||
|
* ask_user: tool({
|
||||||
|
* description:
|
||||||
|
* 'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
|
||||||
|
* inputSchema: z.object({
|
||||||
|
* question: z.string(),
|
||||||
|
* }),
|
||||||
|
* execute: async function (this: PageAgent, input) {
|
||||||
|
* const answer = await do_some_thing(input.question)
|
||||||
|
* return "✅ Received user answer: " + answer
|
||||||
|
* },
|
||||||
|
* })
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // remove internal tool
|
||||||
|
* const customTools = {
|
||||||
|
* ask_user: null // never ask user questions
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
customTools?: Record<string, PageAgentTool | null>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructions to guide the agent's behavior
|
||||||
|
*/
|
||||||
|
instructions?: {
|
||||||
|
/**
|
||||||
|
* Global system-level instructions, applied to all tasks
|
||||||
|
*/
|
||||||
|
system?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic page-level instructions callback
|
||||||
|
* Called before each step to get instructions for the current page
|
||||||
|
* @param url - Current page URL (window.location.href)
|
||||||
|
* @returns Instructions string, or undefined/null to skip
|
||||||
|
*/
|
||||||
|
getPageInstructions?: (url: string) => string | undefined | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifecycle hooks for task execution.
|
||||||
|
* @experimental API may change in future versions.
|
||||||
|
*
|
||||||
|
* All hooks receive the agent instance as first parameter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before each step execution.
|
||||||
|
* @experimental
|
||||||
|
* @param agent - The PageAgentCore instance
|
||||||
|
* @param stepCount - Current step number (0-indexed)
|
||||||
|
*/
|
||||||
|
onBeforeStep?: (agent: PageAgentCore, stepCount: number) => Promise<void> | void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after each step execution.
|
||||||
|
* @experimental
|
||||||
|
* @param agent - The PageAgentCore instance
|
||||||
|
* @param history - Current history of events
|
||||||
|
*/
|
||||||
|
onAfterStep?: (agent: PageAgentCore, history: HistoricalEvent[]) => Promise<void> | void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before task execution starts.
|
||||||
|
* @experimental
|
||||||
|
* @param agent - The PageAgentCore instance
|
||||||
|
*/
|
||||||
|
onBeforeTask?: (agent: PageAgentCore) => Promise<void> | void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after task execution completes (success or failure).
|
||||||
|
* @experimental
|
||||||
|
* @param agent - The PageAgentCore instance
|
||||||
|
* @param result - The execution result
|
||||||
|
*/
|
||||||
|
onAfterTask?: (agent: PageAgentCore, result: ExecutionResult) => Promise<void> | void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the agent is disposed.
|
||||||
|
* @experimental
|
||||||
|
* @note This hook can block the disposal process if it's async.
|
||||||
|
* @param agent - The PageAgentCore instance
|
||||||
|
* @param reason - Optional reason for disposal
|
||||||
|
*/
|
||||||
|
onDispose?: (agent: PageAgentCore, reason?: string) => void
|
||||||
|
|
||||||
|
// page behavior hooks
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
* Enable the experimental script execution tool that allows executing generated JavaScript code on the page.
|
||||||
|
* @note Can cause unpredictable side effects.
|
||||||
|
* @note May bypass some safe guards and data-masking mechanisms.
|
||||||
|
*/
|
||||||
|
experimentalScriptExecutionTool?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
* Fetch /llms.txt from current site origin and include as context.
|
||||||
|
* Only fetched once per origin per task.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
experimentalLlmsTxt?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform page content before sending to LLM.
|
||||||
|
* Called after DOM extraction and simplification, before LLM invocation.
|
||||||
|
* Use cases: inspect extraction results, modify page info, mask sensitive data.
|
||||||
|
*
|
||||||
|
* @param content - Simplified page content that will be sent to LLM
|
||||||
|
* @returns Transformed content
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Mask phone numbers
|
||||||
|
* transformPageContent: async (content) => {
|
||||||
|
* return content.replace(/1[3-9]\d{9}/g, '***********')
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
transformPageContent?: (content: string) => Promise<string> | string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completely override the default system prompt.
|
||||||
|
* @experimental Use with caution - incorrect prompts may break agent behavior.
|
||||||
|
*/
|
||||||
|
customSystemPrompt?: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent reflection state - the reflection-before-action model
|
* Agent reflection state - the reflection-before-action model
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { InvokeError, InvokeErrorType } from '@page-agent/llms'
|
import { InvokeError, InvokeErrorType } from '@page-agent/llms'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import type { PageAgentTool } from '../tools'
|
import type { PageAgentTool } from '../tools'
|
||||||
|
|
||||||
|
const log = console.log.bind(console, chalk.yellow('[autoFixer]'))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize LLM response and fix common format issues.
|
* Normalize LLM response and fix common format issues.
|
||||||
*
|
*
|
||||||
@@ -34,7 +36,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
|||||||
|
|
||||||
// case: sometimes the model only returns the action level
|
// case: sometimes the model only returns the action level
|
||||||
if (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {
|
if (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {
|
||||||
console.log(chalk.yellow(`[normalizeResponse] #1: fixing tool_call`))
|
log(`#1: fixing tool_call`)
|
||||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -47,13 +49,13 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
|||||||
|
|
||||||
// case: sometimes the content json includes upper level wrapper
|
// case: sometimes the content json includes upper level wrapper
|
||||||
if (resolvedArguments?.name === 'AgentOutput') {
|
if (resolvedArguments?.name === 'AgentOutput') {
|
||||||
console.log(chalk.yellow(`[normalizeResponse] #2: fixing tool_call`))
|
log(`#2: fixing tool_call`)
|
||||||
resolvedArguments = safeJsonParse(resolvedArguments.arguments)
|
resolvedArguments = safeJsonParse(resolvedArguments.arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
// case: sometimes even 2-levels of wrapping
|
// case: sometimes even 2-levels of wrapping
|
||||||
if (resolvedArguments?.type === 'function') {
|
if (resolvedArguments?.type === 'function') {
|
||||||
console.log(chalk.yellow(`[normalizeResponse] #3: fixing tool_call`))
|
log(`#3: fixing tool_call`)
|
||||||
resolvedArguments = safeJsonParse(resolvedArguments.function.arguments)
|
resolvedArguments = safeJsonParse(resolvedArguments.function.arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
|||||||
!resolvedArguments?.next_goal &&
|
!resolvedArguments?.next_goal &&
|
||||||
!resolvedArguments?.thinking
|
!resolvedArguments?.thinking
|
||||||
) {
|
) {
|
||||||
console.log(chalk.yellow(`[normalizeResponse] #4: fixing tool_call`))
|
log(`#4: fixing tool_call`)
|
||||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +92,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
|||||||
|
|
||||||
// fix incomplete formats
|
// fix incomplete formats
|
||||||
if (!resolvedArguments.action) {
|
if (!resolvedArguments.action) {
|
||||||
console.log(chalk.yellow(`[normalizeResponse] #5: fixing tool_call`))
|
log(`#5: fixing tool_call`)
|
||||||
resolvedArguments.action = { name: 'wait', input: { seconds: 1 } }
|
resolvedArguments.action = { name: 'wait', input: { seconds: 1 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,9 +151,7 @@ function validateAction(action: any, tools: Map<string, PageAgentTool>): any {
|
|||||||
(k) => !(schema.shape as Record<string, z.ZodType>)[k].safeParse(undefined).success
|
(k) => !(schema.shape as Record<string, z.ZodType>)[k].safeParse(undefined).success
|
||||||
)
|
)
|
||||||
if (requiredKey) {
|
if (requiredKey) {
|
||||||
console.log(
|
log(`coercing primitive action input for "${toolName}"`)
|
||||||
chalk.yellow(`[normalizeResponse] coercing primitive action input for "${toolName}"`)
|
|
||||||
)
|
|
||||||
value = { [requiredKey]: value }
|
value = { [requiredKey]: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
|
|
||||||
export { normalizeResponse } from './autoFixer'
|
export * from './autoFixer'
|
||||||
|
|
||||||
export async function waitFor(seconds: number): Promise<void> {
|
export async function waitFor(seconds: number): Promise<void> {
|
||||||
await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
|
await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
|
||||||
@@ -57,6 +57,35 @@ export function uid() {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const llmsTxtCache = new Map<string, string | null>()
|
||||||
|
|
||||||
|
/** Fetch /llms.txt for a URL's origin. Cached per origin, `null` = tried and not found. */
|
||||||
|
export async function fetchLlmsTxt(url: string): Promise<string | null> {
|
||||||
|
const origin = new URL(url).origin
|
||||||
|
if (llmsTxtCache.has(origin)) return llmsTxtCache.get(origin)!
|
||||||
|
|
||||||
|
const endpoint = `${origin}/llms.txt`
|
||||||
|
let result: string | null = null
|
||||||
|
try {
|
||||||
|
console.log(chalk.gray(`[llms.txt] Fetching ${endpoint}`))
|
||||||
|
const res = await fetch(endpoint, { signal: AbortSignal.timeout(3000) })
|
||||||
|
if (res.ok) {
|
||||||
|
result = await res.text()
|
||||||
|
console.log(chalk.green(`[llms.txt] Found (${result.length} chars)`))
|
||||||
|
if (result.length > 1000) {
|
||||||
|
console.log(chalk.yellow(`[llms.txt] Truncating to 1000 chars`))
|
||||||
|
result = truncate(result, 1000)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.debug(chalk.gray(`[llms.txt] ${res.status} for ${endpoint}`))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.debug(chalk.gray(`[llms.txt] not found for ${endpoint}`), e)
|
||||||
|
}
|
||||||
|
llmsTxtCache.set(origin, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple assertion function that throws an error if the condition is falsy
|
* Simple assertion function that throws an error if the condition is falsy
|
||||||
* @param condition - The condition to assert
|
* @param condition - The condition to assert
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default defineConfig({
|
|||||||
external: [
|
external: [
|
||||||
'chalk',
|
'chalk',
|
||||||
'zod',
|
'zod',
|
||||||
|
'zod/v4',
|
||||||
// all the internal packages
|
// all the internal packages
|
||||||
/^@page-agent\//,
|
/^@page-agent\//,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
AI-powered browser automation. Control web pages with natural language.
|
|
||||||
|
|
||||||
Page Agent Ext — AI-Powered Browser Automation
|
|
||||||
|
|
||||||
🌟 What is Page Agent Ext?
|
|
||||||
|
|
||||||
Page Agent Ext brings AI-powered automation to your browser. Built on the open-source Page Agent framework, it lets you control web pages across multiple tabs using natural language — no scripting required.
|
|
||||||
|
|
||||||
🌟 Key Features:
|
|
||||||
|
|
||||||
- Natural Language Control — Command your browser in plain language, no code needed
|
|
||||||
- Cross-Tab Automation — Seamlessly operate across multiple tabs and pages
|
|
||||||
- Smart HTML Cleaning — Intelligently extracts and simplifies page structure for accurate AI understanding
|
|
||||||
- Bring Your Own LLM — Use OpenAI, Anthropic, or any compatible API with full data control
|
|
||||||
- Privacy-First — Zero data collection; all data flows directly to your chosen LLM provider
|
|
||||||
- Open Source — MIT licensed, built on the Page Agent framework with full transparency
|
|
||||||
|
|
||||||
🌟 How It Works & Privacy:
|
|
||||||
|
|
||||||
Page Agent Ext performs DOM analysis locally in your browser. When you initiate a task, sanitized page structure is sent to the LLM API you configure. Your data is never collected or stored by us.
|
|
||||||
|
|
||||||
- Your API Key — Configure your own LLM API (OpenAI, Anthropic, etc.). Data goes directly to your provider
|
|
||||||
- Test API — A free test endpoint is available for evaluation; we recommend your own key for regular use
|
|
||||||
|
|
||||||
Terms of Use & Privacy: https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md
|
|
||||||
|
|
||||||
🌟 Open Source:
|
|
||||||
|
|
||||||
This project is MIT licensed. Review the code, verify privacy claims, or extend it for your needs:
|
|
||||||
https://github.com/alibaba/page-agent
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
AI 驱动的浏览器自动化助手,用自然语言控制网页。
|
|
||||||
|
|
||||||
Page Agent Ext — AI 驱动的浏览器自动化助手
|
|
||||||
|
|
||||||
🌟 什么是 Page Agent Ext?
|
|
||||||
|
|
||||||
Page Agent Ext 为浏览器带来 AI 自动化能力。基于开源的 Page Agent 框架,你可以用自然语言跨标签页控制网页,无需编写任何脚本。
|
|
||||||
|
|
||||||
🌟 核心特性:
|
|
||||||
|
|
||||||
- 自然语言控制 — 用日常语言指挥浏览器,无需编程
|
|
||||||
- 跨标签页操作 — 在多个标签页之间无缝切换和操控
|
|
||||||
- HTML 智能清洗 — 智能提取和精简页面结构,让 AI 准确理解网页内容
|
|
||||||
- 使用你自己的模型 — 支持 OpenAI、Anthropic 或任何兼容 API,数据完全自主可控
|
|
||||||
- 隐私优先 — 零数据收集,所有数据直接发送到你配置的 LLM 服务商
|
|
||||||
- 开源透明 — MIT 协议,基于 Page Agent 开源框架,代码完全公开
|
|
||||||
|
|
||||||
🌟 工作原理与隐私:
|
|
||||||
|
|
||||||
Page Agent Ext 在浏览器本地进行 DOM 分析。当你发起任务时,经清洗的页面结构会发送到你配置的 LLM API。我们不会收集或存储你的任何数据。
|
|
||||||
|
|
||||||
- 你的 API Key — 配置你自己的 LLM API(OpenAI、Anthropic 等),数据直接发送到你的服务商
|
|
||||||
- 测试 API — 提供免费测试端点供体验,日常使用建议配置自己的 Key
|
|
||||||
|
|
||||||
使用条款与隐私:https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md
|
|
||||||
|
|
||||||
🌟 开源项目:
|
|
||||||
|
|
||||||
MIT 协议开源。查看源码、验证隐私承诺,或按需扩展:
|
|
||||||
https://github.com/alibaba/page-agent
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ext",
|
"name": "@page-agent/ext",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.8",
|
"version": "0.1.15",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
@@ -24,13 +24,13 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.576.0",
|
||||||
"motion": "^12.34.3",
|
"motion": "^12.34.5",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.9.0",
|
"simple-icons": "^16.10.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
@@ -38,12 +38,14 @@
|
|||||||
"wxt": "^0.20.18"
|
"wxt": "^0.20.18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.5.3",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.5.3",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.5.3",
|
||||||
"ai-motion": "^0.4.8",
|
"ai-motion": "^0.4.8",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
"zod": "^4.3.5"
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type PageAgentConfig, PageAgentCore } from '@page-agent/core'
|
import { type AgentConfig, PageAgentCore } from '@page-agent/core'
|
||||||
|
|
||||||
import { RemotePageController } from './RemotePageController'
|
import { RemotePageController } from './RemotePageController'
|
||||||
import { TabsController } from './TabsController'
|
import { TabsController } from './TabsController'
|
||||||
@@ -17,7 +17,7 @@ function detectLanguage(): 'en-US' | 'zh-CN' {
|
|||||||
* - can be used from a side panel or a content script
|
* - can be used from a side panel or a content script
|
||||||
*/
|
*/
|
||||||
export class MultiPageAgent extends PageAgentCore {
|
export class MultiPageAgent extends PageAgentCore {
|
||||||
constructor(config: Omit<PageAgentConfig, 'pageController'> & { includeInitialTab?: boolean }) {
|
constructor(config: AgentConfig & { includeInitialTab?: boolean }) {
|
||||||
// multi page controller
|
// multi page controller
|
||||||
const tabsController = new TabsController()
|
const tabsController = new TabsController()
|
||||||
const pageController = new RemotePageController(tabsController)
|
const pageController = new RemotePageController(tabsController)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* - switch_to_tab: Switch to an existing tab
|
* - switch_to_tab: Switch to an existing tab
|
||||||
* - close_tab: Close a tab (optionally switch to another)
|
* - close_tab: Close a tab (optionally switch to another)
|
||||||
*/
|
*/
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import type { TabsController } from './TabsController'
|
import type { TabsController } from './TabsController'
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,13 @@ import { DEMO_CONFIG, migrateLegacyEndpoint } from './constants'
|
|||||||
/** Language preference: undefined means follow system */
|
/** Language preference: undefined means follow system */
|
||||||
export type LanguagePreference = SupportedLanguage | undefined
|
export type LanguagePreference = SupportedLanguage | undefined
|
||||||
|
|
||||||
export interface ExtConfig extends LLMConfig {
|
export interface AdvancedConfig {
|
||||||
|
maxSteps?: number
|
||||||
|
systemInstruction?: string
|
||||||
|
experimentalLlmsTxt?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExtConfig extends LLMConfig, AdvancedConfig {
|
||||||
language?: LanguagePreference
|
language?: LanguagePreference
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +46,10 @@ export function useAgent(): UseAgentResult {
|
|||||||
const [config, setConfig] = useState<ExtConfig | null>(null)
|
const [config, setConfig] = useState<ExtConfig | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
chrome.storage.local.get(['llmConfig', 'language']).then((result) => {
|
chrome.storage.local.get(['llmConfig', 'language', 'advancedConfig']).then((result) => {
|
||||||
let llmConfig = (result.llmConfig as LLMConfig) ?? DEMO_CONFIG
|
let llmConfig = (result.llmConfig as LLMConfig) ?? DEMO_CONFIG
|
||||||
const language = (result.language as SupportedLanguage) || undefined
|
const language = (result.language as SupportedLanguage) || undefined
|
||||||
|
const advancedConfig = (result.advancedConfig as AdvancedConfig) ?? {}
|
||||||
|
|
||||||
// Auto-migrate legacy testing endpoints
|
// Auto-migrate legacy testing endpoints
|
||||||
const migrated = migrateLegacyEndpoint(llmConfig)
|
const migrated = migrateLegacyEndpoint(llmConfig)
|
||||||
@@ -53,14 +60,18 @@ export function useAgent(): UseAgentResult {
|
|||||||
chrome.storage.local.set({ llmConfig: DEMO_CONFIG })
|
chrome.storage.local.set({ llmConfig: DEMO_CONFIG })
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig({ ...llmConfig, language })
|
setConfig({ ...llmConfig, ...advancedConfig, language })
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!config) return
|
if (!config) return
|
||||||
|
|
||||||
const agent = new MultiPageAgent(config)
|
const { systemInstruction, ...agentConfig } = config
|
||||||
|
const agent = new MultiPageAgent({
|
||||||
|
...agentConfig,
|
||||||
|
instructions: systemInstruction ? { system: systemInstruction } : undefined,
|
||||||
|
})
|
||||||
agentRef.current = agent
|
agentRef.current = agent
|
||||||
|
|
||||||
const handleStatusChange = (e: Event) => {
|
const handleStatusChange = (e: Event) => {
|
||||||
@@ -106,15 +117,26 @@ export function useAgent(): UseAgentResult {
|
|||||||
agentRef.current?.stop()
|
agentRef.current?.stop()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const configure = useCallback(async ({ language, ...llmConfig }: ExtConfig) => {
|
const configure = useCallback(
|
||||||
await chrome.storage.local.set({ llmConfig })
|
async ({
|
||||||
if (language) {
|
language,
|
||||||
await chrome.storage.local.set({ language })
|
maxSteps,
|
||||||
} else {
|
systemInstruction,
|
||||||
await chrome.storage.local.remove('language')
|
experimentalLlmsTxt,
|
||||||
}
|
...llmConfig
|
||||||
setConfig({ ...llmConfig, language })
|
}: ExtConfig) => {
|
||||||
}, [])
|
await chrome.storage.local.set({ llmConfig })
|
||||||
|
if (language) {
|
||||||
|
await chrome.storage.local.set({ language })
|
||||||
|
} else {
|
||||||
|
await chrome.storage.local.remove('language')
|
||||||
|
}
|
||||||
|
const advancedConfig: AdvancedConfig = { maxSteps, systemInstruction, experimentalLlmsTxt }
|
||||||
|
await chrome.storage.local.set({ advancedConfig })
|
||||||
|
setConfig({ ...llmConfig, ...advancedConfig, language })
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
import { Copy, CornerUpLeft, Eye, EyeOff, HatGlasses, Home, Loader2, Scale } from 'lucide-react'
|
import {
|
||||||
|
ChevronDown,
|
||||||
|
Copy,
|
||||||
|
CornerUpLeft,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
|
HatGlasses,
|
||||||
|
Home,
|
||||||
|
Loader2,
|
||||||
|
Scale,
|
||||||
|
} from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { siGithub } from 'simple-icons'
|
import { siGithub } from 'simple-icons'
|
||||||
|
|
||||||
@@ -6,6 +16,7 @@ import { DEMO_API_KEY, DEMO_BASE_URL, DEMO_MODEL, isTestingEndpoint } from '@/ag
|
|||||||
import type { ExtConfig, LanguagePreference } from '@/agent/useAgent'
|
import type { ExtConfig, LanguagePreference } from '@/agent/useAgent'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Switch } from '@/components/ui/switch'
|
||||||
|
|
||||||
interface ConfigPanelProps {
|
interface ConfigPanelProps {
|
||||||
config: ExtConfig | null
|
config: ExtConfig | null
|
||||||
@@ -18,18 +29,26 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
const [baseURL, setBaseURL] = useState(config?.baseURL || DEMO_BASE_URL)
|
const [baseURL, setBaseURL] = useState(config?.baseURL || DEMO_BASE_URL)
|
||||||
const [model, setModel] = useState(config?.model || DEMO_MODEL)
|
const [model, setModel] = useState(config?.model || DEMO_MODEL)
|
||||||
const [language, setLanguage] = useState<LanguagePreference>(config?.language)
|
const [language, setLanguage] = useState<LanguagePreference>(config?.language)
|
||||||
|
const [maxSteps, setMaxSteps] = useState<number | undefined>(config?.maxSteps)
|
||||||
|
const [systemInstruction, setSystemInstruction] = useState(config?.systemInstruction ?? '')
|
||||||
|
const [experimentalLlmsTxt, setExperimentalLlmsTxt] = useState(
|
||||||
|
config?.experimentalLlmsTxt ?? false
|
||||||
|
)
|
||||||
|
const [advancedOpen, setAdvancedOpen] = useState(false)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [userAuthToken, setUserAuthToken] = useState<string>('')
|
const [userAuthToken, setUserAuthToken] = useState<string>('')
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
const [showToken, setShowToken] = useState(false)
|
const [showToken, setShowToken] = useState(false)
|
||||||
const [showApiKey, setShowApiKey] = useState(false)
|
const [showApiKey, setShowApiKey] = useState(false)
|
||||||
|
|
||||||
// Update local state when config prop changes
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setApiKey(config?.apiKey || DEMO_API_KEY)
|
setApiKey(config?.apiKey || DEMO_API_KEY)
|
||||||
setBaseURL(config?.baseURL || DEMO_BASE_URL)
|
setBaseURL(config?.baseURL || DEMO_BASE_URL)
|
||||||
setModel(config?.model || DEMO_MODEL)
|
setModel(config?.model || DEMO_MODEL)
|
||||||
setLanguage(config?.language)
|
setLanguage(config?.language)
|
||||||
|
setMaxSteps(config?.maxSteps)
|
||||||
|
setSystemInstruction(config?.systemInstruction ?? '')
|
||||||
|
setExperimentalLlmsTxt(config?.experimentalLlmsTxt ?? false)
|
||||||
}, [config])
|
}, [config])
|
||||||
|
|
||||||
// Poll for user auth token every second until found
|
// Poll for user auth token every second until found
|
||||||
@@ -67,7 +86,15 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
await onSave({ apiKey, baseURL, model, language })
|
await onSave({
|
||||||
|
apiKey,
|
||||||
|
baseURL,
|
||||||
|
model,
|
||||||
|
language,
|
||||||
|
maxSteps: maxSteps || undefined,
|
||||||
|
systemInstruction: systemInstruction || undefined,
|
||||||
|
experimentalLlmsTxt,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
@@ -87,28 +114,11 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Testing API notice */}
|
|
||||||
{isTestingEndpoint(baseURL) && (
|
|
||||||
<div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[10px] text-muted-foreground leading-relaxed">
|
|
||||||
<Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" />
|
|
||||||
You are using the free testing API. By using this service you agree to the{' '}
|
|
||||||
<a
|
|
||||||
href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline hover:text-foreground"
|
|
||||||
>
|
|
||||||
Terms of Use & Privacy Policy
|
|
||||||
</a>
|
|
||||||
. No sensitive data. No guaranteed availability.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* User Auth Token Section */}
|
{/* User Auth Token Section */}
|
||||||
<div className="flex flex-col gap-1.5 p-3 bg-muted/50 rounded-md border">
|
<div className="flex flex-col gap-1.5 p-3 bg-muted/50 rounded-md border">
|
||||||
<label className="text-xs font-medium text-muted-foreground">User Auth Token</label>
|
<label className="text-xs font-medium text-muted-foreground">User Auth Token</label>
|
||||||
<p className="text-[10px] text-muted-foreground mb-1">
|
<p className="text-[10px] text-muted-foreground mb-1">
|
||||||
Add this token to a website's localStorage to give it authorization to call this extension
|
Give a website the ability to call this extension.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Input
|
<Input
|
||||||
@@ -153,6 +163,23 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Testing API notice */}
|
||||||
|
{isTestingEndpoint(baseURL) && (
|
||||||
|
<div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[11px] text-muted-foreground leading-relaxed">
|
||||||
|
<Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" />
|
||||||
|
You are using the free testing API. By using this service you agree to the{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline hover:text-foreground"
|
||||||
|
>
|
||||||
|
Terms of Use & Privacy Policy
|
||||||
|
</a>
|
||||||
|
. No sensitive data. No guaranteed availability.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="text-xs text-muted-foreground">Model</label>
|
<label className="text-xs text-muted-foreground">Model</label>
|
||||||
<Input
|
<Input
|
||||||
@@ -197,6 +224,52 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Advanced Config */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setAdvancedOpen(!advancedOpen)}
|
||||||
|
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground cursor-pointer mt-1 font-bold"
|
||||||
|
>
|
||||||
|
Advanced
|
||||||
|
<ChevronDown
|
||||||
|
className="size-3 transition-transform"
|
||||||
|
style={{ transform: advancedOpen ? 'rotate(0deg)' : 'rotate(90deg)' }}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{advancedOpen && (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label className="text-xs text-muted-foreground">Max Steps</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="40"
|
||||||
|
min={1}
|
||||||
|
max={200}
|
||||||
|
value={maxSteps ?? ''}
|
||||||
|
onChange={(e) => setMaxSteps(e.target.value ? Number(e.target.value) : undefined)}
|
||||||
|
className="text-xs h-8 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [-moz-appearance:textfield]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label className="text-xs text-muted-foreground">System Instruction</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="Additional instructions for the agent..."
|
||||||
|
value={systemInstruction}
|
||||||
|
onChange={(e) => setSystemInstruction(e.target.value)}
|
||||||
|
rows={3}
|
||||||
|
className="text-xs rounded-md border border-input bg-background px-3 py-2 resize-y min-h-[60px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label className="flex items-center justify-between cursor-pointer">
|
||||||
|
<span className="text-xs text-muted-foreground">Experimental llms.txt support</span>
|
||||||
|
<Switch checked={experimentalLlmsTxt} onCheckedChange={setExperimentalLlmsTxt} />
|
||||||
|
</label>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex gap-2 mt-2">
|
<div className="flex gap-2 mt-2">
|
||||||
<Button variant="outline" onClick={onClose} className="flex-1 h-8 text-xs cursor-pointer">
|
<Button variant="outline" onClick={onClose} className="flex-1 h-8 text-xs cursor-pointer">
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AlertTriangle, RotateCcw } from 'lucide-react'
|
import { AlertTriangle, Eraser, RotateCcw } from 'lucide-react'
|
||||||
import { Component, type ErrorInfo, type ReactNode } from 'react'
|
import { Component, type ErrorInfo, type ReactNode } from 'react'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -27,6 +27,11 @@ export class ErrorBoundary extends Component<Props, State> {
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleResetConfig = async () => {
|
||||||
|
await chrome.storage.local.remove(['llmConfig', 'language', 'advancedConfig'])
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.hasError) {
|
if (!this.state.hasError) {
|
||||||
return this.props.children
|
return this.props.children
|
||||||
@@ -39,10 +44,16 @@ export class ErrorBoundary extends Component<Props, State> {
|
|||||||
<p className="text-sm text-muted-foreground mb-4 max-w-xs">
|
<p className="text-sm text-muted-foreground mb-4 max-w-xs">
|
||||||
{this.state.error?.message || 'An unexpected error occurred'}
|
{this.state.error?.message || 'An unexpected error occurred'}
|
||||||
</p>
|
</p>
|
||||||
<Button variant="outline" size="sm" onClick={this.handleReload}>
|
<div className="flex gap-2">
|
||||||
<RotateCcw className="size-3.5 mr-2" />
|
<Button variant="outline" size="sm" onClick={this.handleResetConfig}>
|
||||||
Reload Panel
|
<Eraser className="size-3.5 mr-2" />
|
||||||
</Button>
|
Reset Config
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" onClick={this.handleReload}>
|
||||||
|
<RotateCcw className="size-3.5 mr-2" />
|
||||||
|
Reload Panel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,20 +43,24 @@ export function MotionOverlay({ active }: { active: boolean }) {
|
|||||||
const motionRef = useRef<Motion | null>(null)
|
const motionRef = useRef<Motion | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light'
|
try {
|
||||||
const motion = new Motion({
|
const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light'
|
||||||
mode,
|
const motion = new Motion({
|
||||||
borderWidth: 4,
|
mode,
|
||||||
borderRadius: 14,
|
borderWidth: 4,
|
||||||
glowWidth: mode === 'dark' ? 120 : 60,
|
borderRadius: 14,
|
||||||
styles: { position: 'absolute', inset: '0' },
|
glowWidth: mode === 'dark' ? 120 : 60,
|
||||||
})
|
styles: { position: 'absolute', inset: '0' },
|
||||||
motionRef.current = motion
|
})
|
||||||
containerRef.current!.appendChild(motion.element)
|
motionRef.current = motion
|
||||||
motion.autoResize(containerRef.current!)
|
containerRef.current!.appendChild(motion.element)
|
||||||
|
motion.autoResize(containerRef.current!)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[MotionOverlay] Motion unavailable:', e)
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
motion.dispose()
|
motionRef.current?.dispose()
|
||||||
motionRef.current = null
|
motionRef.current = null
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
zip: {
|
||||||
|
artifactTemplate: 'page-agent-ext-{{version}}-{{browser}}.zip',
|
||||||
|
},
|
||||||
manifest: {
|
manifest: {
|
||||||
default_locale: 'en',
|
default_locale: 'en',
|
||||||
name: '__MSG_extName__',
|
name: '__MSG_extName__',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-agent-llms.js",
|
"main": "./dist/lib/page-agent-llms.js",
|
||||||
"module": "./dist/lib/page-agent-llms.js",
|
"module": "./dist/lib/page-agent-llms.js",
|
||||||
@@ -37,7 +37,12 @@
|
|||||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* OpenAI Client implementation
|
* OpenAI Client implementation
|
||||||
*/
|
*/
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import { InvokeError, InvokeErrorType } from './errors'
|
import { InvokeError, InvokeErrorType } from './errors'
|
||||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Core types for LLM integration
|
* Core types for LLM integration
|
||||||
*/
|
*/
|
||||||
import type * as z from 'zod'
|
import type * as z from 'zod/v4'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message format - OpenAI standard (industry standard)
|
* Message format - OpenAI standard (industry standard)
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
* Utility functions for LLM integration
|
* Utility functions for LLM integration
|
||||||
*/
|
*/
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod/v4'
|
||||||
|
|
||||||
import type { Tool } from './types'
|
import type { Tool } from './types'
|
||||||
|
|
||||||
function debug(message: string) {
|
const debug = console.debug.bind(console, chalk.gray('[LLM]'))
|
||||||
console.debug(chalk.gray('[LLM]'), message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Zod schema to OpenAI tool format
|
* Convert Zod schema to OpenAI tool format
|
||||||
@@ -75,6 +73,11 @@ export function modelPatch(body: Record<string, any>) {
|
|||||||
} else if (modelName.startsWith('gpt-51')) {
|
} else if (modelName.startsWith('gpt-51')) {
|
||||||
debug('Applying GPT-51 patch: disable reasoning')
|
debug('Applying GPT-51 patch: disable reasoning')
|
||||||
body.reasoning_effort = 'none'
|
body.reasoning_effort = 'none'
|
||||||
|
} else if (modelName.startsWith('gpt-54')) {
|
||||||
|
debug(
|
||||||
|
'Applying GPT-5.4 patch: skip reasoning_effort because chat/completions rejects it with function tools'
|
||||||
|
)
|
||||||
|
delete body.reasoning_effort
|
||||||
} else if (modelName.startsWith('gpt-5-mini')) {
|
} else if (modelName.startsWith('gpt-5-mini')) {
|
||||||
debug('Applying GPT-5-mini patch: set reasoning effort to low, temperature to 1')
|
debug('Applying GPT-5-mini patch: set reasoning effort to low, temperature to 1')
|
||||||
body.reasoning_effort = 'low'
|
body.reasoning_effort = 'low'
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: ['chalk', 'zod'],
|
external: ['chalk', 'zod', 'zod/v4'],
|
||||||
},
|
},
|
||||||
minify: false,
|
minify: false,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "page-agent",
|
"name": "page-agent",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/esm/page-agent.js",
|
"main": "./dist/esm/page-agent.js",
|
||||||
"module": "./dist/esm/page-agent.js",
|
"module": "./dist/esm/page-agent.js",
|
||||||
@@ -44,11 +44,16 @@
|
|||||||
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.4.0",
|
"@page-agent/core": "1.5.3",
|
||||||
"@page-agent/llms": "1.4.0",
|
"@page-agent/llms": "1.5.3",
|
||||||
"@page-agent/page-controller": "1.4.0",
|
"@page-agent/page-controller": "1.5.3",
|
||||||
"@page-agent/ui": "1.4.0",
|
"@page-agent/ui": "1.5.3",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
* Copyright (C) 2025 Alibaba Group Holding Limited
|
* Copyright (C) 2025 Alibaba Group Holding Limited
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
import { type PageAgentConfig, PageAgentCore, type PageAgentTool, tool } from '@page-agent/core'
|
import { type AgentConfig, PageAgentCore } from '@page-agent/core'
|
||||||
import { PageController } from '@page-agent/page-controller'
|
import { PageController, type PageControllerConfig } from '@page-agent/page-controller'
|
||||||
import { Panel } from '@page-agent/ui'
|
import { Panel } from '@page-agent/ui'
|
||||||
|
|
||||||
export type { PageAgentConfig, PageAgentTool }
|
export * from '@page-agent/core'
|
||||||
export { tool }
|
|
||||||
|
export type PageAgentConfig = AgentConfig & PageControllerConfig
|
||||||
|
|
||||||
export class PageAgent extends PageAgentCore {
|
export class PageAgent extends PageAgentCore {
|
||||||
panel: Panel
|
panel: Panel
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default defineConfig({
|
|||||||
external: [
|
external: [
|
||||||
'chalk',
|
'chalk',
|
||||||
'zod',
|
'zod',
|
||||||
|
'zod/v4',
|
||||||
// all the internal packages
|
// all the internal packages
|
||||||
/^@page-agent\//,
|
/^@page-agent\//,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-controller.js",
|
"main": "./dist/lib/page-controller.js",
|
||||||
"module": "./dist/lib/page-controller.js",
|
"module": "./dist/lib/page-controller.js",
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export async function clickElement(element: HTMLElement) {
|
|||||||
// dispatch a click event
|
// dispatch a click event
|
||||||
// element.click()
|
// element.click()
|
||||||
|
|
||||||
await waitFor(0.1) // Wait to ensure click event processing completes
|
await waitFor(0.2) // Wait to ensure click event processing completes
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
@@ -181,11 +181,6 @@ export async function scrollVertically(
|
|||||||
// Element-specific scrolling if element is provided
|
// Element-specific scrolling if element is provided
|
||||||
if (element) {
|
if (element) {
|
||||||
const targetElement = element
|
const targetElement = element
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Starting direct container scroll for element:',
|
|
||||||
targetElement.tagName
|
|
||||||
)
|
|
||||||
|
|
||||||
let currentElement = targetElement as HTMLElement | null
|
let currentElement = targetElement as HTMLElement | null
|
||||||
let scrollSuccess = false
|
let scrollSuccess = false
|
||||||
let scrolledElement: HTMLElement | null = null
|
let scrolledElement: HTMLElement | null = null
|
||||||
@@ -198,19 +193,6 @@ export async function scrollVertically(
|
|||||||
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY)
|
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY)
|
||||||
const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight
|
const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Checking element:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'hasScrollableY:',
|
|
||||||
hasScrollableY,
|
|
||||||
'canScrollVertically:',
|
|
||||||
canScrollVertically,
|
|
||||||
'scrollHeight:',
|
|
||||||
currentElement.scrollHeight,
|
|
||||||
'clientHeight:',
|
|
||||||
currentElement.clientHeight
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasScrollableY && canScrollVertically) {
|
if (hasScrollableY && canScrollVertically) {
|
||||||
const beforeScroll = currentElement.scrollTop
|
const beforeScroll = currentElement.scrollTop
|
||||||
const maxScroll = currentElement.scrollHeight - currentElement.clientHeight
|
const maxScroll = currentElement.scrollHeight - currentElement.clientHeight
|
||||||
@@ -228,27 +210,10 @@ export async function scrollVertically(
|
|||||||
const afterScroll = currentElement.scrollTop
|
const afterScroll = currentElement.scrollTop
|
||||||
const actualScrollDelta = afterScroll - beforeScroll
|
const actualScrollDelta = afterScroll - beforeScroll
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Scroll attempt:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'before:',
|
|
||||||
beforeScroll,
|
|
||||||
'after:',
|
|
||||||
afterScroll,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Math.abs(actualScrollDelta) > 0.5) {
|
if (Math.abs(actualScrollDelta) > 0.5) {
|
||||||
scrollSuccess = true
|
scrollSuccess = true
|
||||||
scrolledElement = currentElement
|
scrolledElement = currentElement
|
||||||
scrollDelta = actualScrollDelta
|
scrollDelta = actualScrollDelta
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Successfully scrolled container:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,11 +309,6 @@ export async function scrollHorizontally(
|
|||||||
// Element-specific scrolling if element is provided
|
// Element-specific scrolling if element is provided
|
||||||
if (element) {
|
if (element) {
|
||||||
const targetElement = element
|
const targetElement = element
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Starting direct container scroll for element:',
|
|
||||||
targetElement.tagName
|
|
||||||
)
|
|
||||||
|
|
||||||
let currentElement = targetElement as HTMLElement | null
|
let currentElement = targetElement as HTMLElement | null
|
||||||
let scrollSuccess = false
|
let scrollSuccess = false
|
||||||
let scrolledElement: HTMLElement | null = null
|
let scrolledElement: HTMLElement | null = null
|
||||||
@@ -361,19 +321,6 @@ export async function scrollHorizontally(
|
|||||||
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX)
|
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX)
|
||||||
const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth
|
const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Checking element:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'hasScrollableX:',
|
|
||||||
hasScrollableX,
|
|
||||||
'canScrollHorizontally:',
|
|
||||||
canScrollHorizontally,
|
|
||||||
'scrollWidth:',
|
|
||||||
currentElement.scrollWidth,
|
|
||||||
'clientWidth:',
|
|
||||||
currentElement.clientWidth
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasScrollableX && canScrollHorizontally) {
|
if (hasScrollableX && canScrollHorizontally) {
|
||||||
const beforeScroll = currentElement.scrollLeft
|
const beforeScroll = currentElement.scrollLeft
|
||||||
const maxScroll = currentElement.scrollWidth - currentElement.clientWidth
|
const maxScroll = currentElement.scrollWidth - currentElement.clientWidth
|
||||||
@@ -391,27 +338,10 @@ export async function scrollHorizontally(
|
|||||||
const afterScroll = currentElement.scrollLeft
|
const afterScroll = currentElement.scrollLeft
|
||||||
const actualScrollDelta = afterScroll - beforeScroll
|
const actualScrollDelta = afterScroll - beforeScroll
|
||||||
|
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Scroll attempt:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'before:',
|
|
||||||
beforeScroll,
|
|
||||||
'after:',
|
|
||||||
afterScroll,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Math.abs(actualScrollDelta) > 0.5) {
|
if (Math.abs(actualScrollDelta) > 0.5) {
|
||||||
scrollSuccess = true
|
scrollSuccess = true
|
||||||
scrolledElement = currentElement
|
scrolledElement = currentElement
|
||||||
scrollDelta = actualScrollDelta
|
scrollDelta = actualScrollDelta
|
||||||
console.log(
|
|
||||||
'[SCROLL DEBUG] Successfully scrolled container:',
|
|
||||||
currentElement.tagName,
|
|
||||||
'delta:',
|
|
||||||
actualScrollDelta
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1446,7 +1446,7 @@ export default (
|
|||||||
/**
|
/**
|
||||||
* @edit add `data-browser-use-ignore` attribute
|
* @edit add `data-browser-use-ignore` attribute
|
||||||
*/
|
*/
|
||||||
if (node.dataset?.browserUseIgnore === 'true') {
|
if (node.dataset?.browserUseIgnore === 'true' || node.dataset?.pageAgentIgnore === 'true') {
|
||||||
return null // Skip this node and its children
|
return null // Skip this node and its children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,7 @@ import cursorStyles from './cursor.module.css'
|
|||||||
export class SimulatorMask {
|
export class SimulatorMask {
|
||||||
shown: boolean = false
|
shown: boolean = false
|
||||||
wrapper = document.createElement('div')
|
wrapper = document.createElement('div')
|
||||||
motion = new Motion({
|
motion: Motion | null = null
|
||||||
mode: isPageDark() ? 'dark' : 'light',
|
|
||||||
styles: {
|
|
||||||
position: 'absolute',
|
|
||||||
inset: '0',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
#cursor = document.createElement('div')
|
#cursor = document.createElement('div')
|
||||||
|
|
||||||
@@ -28,9 +22,19 @@ export class SimulatorMask {
|
|||||||
this.wrapper.id = 'page-agent-runtime_simulator-mask'
|
this.wrapper.id = 'page-agent-runtime_simulator-mask'
|
||||||
this.wrapper.className = styles.wrapper
|
this.wrapper.className = styles.wrapper
|
||||||
this.wrapper.setAttribute('data-browser-use-ignore', 'true')
|
this.wrapper.setAttribute('data-browser-use-ignore', 'true')
|
||||||
|
this.wrapper.setAttribute('data-page-agent-ignore', 'true')
|
||||||
|
|
||||||
this.wrapper.appendChild(this.motion.element)
|
try {
|
||||||
this.motion.autoResize(this.wrapper)
|
const motion = new Motion({
|
||||||
|
mode: isPageDark() ? 'dark' : 'light',
|
||||||
|
styles: { position: 'absolute', inset: '0' },
|
||||||
|
})
|
||||||
|
this.motion = motion
|
||||||
|
this.wrapper.appendChild(motion.element)
|
||||||
|
motion.autoResize(this.wrapper)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[SimulatorMask] Motion overlay unavailable:', e)
|
||||||
|
}
|
||||||
|
|
||||||
// Capture all mouse, keyboard, and wheel events
|
// Capture all mouse, keyboard, and wheel events
|
||||||
this.wrapper.addEventListener('click', (e) => {
|
this.wrapper.addEventListener('click', (e) => {
|
||||||
@@ -144,8 +148,8 @@ export class SimulatorMask {
|
|||||||
if (this.shown) return
|
if (this.shown) return
|
||||||
|
|
||||||
this.shown = true
|
this.shown = true
|
||||||
this.motion.start()
|
this.motion?.start()
|
||||||
this.motion.fadeIn()
|
this.motion?.fadeIn()
|
||||||
|
|
||||||
this.wrapper.style.display = 'block'
|
this.wrapper.style.display = 'block'
|
||||||
|
|
||||||
@@ -162,8 +166,8 @@ export class SimulatorMask {
|
|||||||
if (!this.shown) return
|
if (!this.shown) return
|
||||||
|
|
||||||
this.shown = false
|
this.shown = false
|
||||||
this.motion.fadeOut()
|
this.motion?.fadeOut()
|
||||||
this.motion.pause()
|
this.motion?.pause()
|
||||||
|
|
||||||
this.#cursor.classList.remove(cursorStyles.clicking)
|
this.#cursor.classList.remove(cursorStyles.clicking)
|
||||||
|
|
||||||
@@ -173,7 +177,7 @@ export class SimulatorMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.motion.dispose()
|
this.motion?.dispose()
|
||||||
this.wrapper.remove()
|
this.wrapper.remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/lib/page-agent-ui.js",
|
"main": "./dist/lib/page-agent-ui.js",
|
||||||
"module": "./dist/lib/page-agent-ui.js",
|
"module": "./dist/lib/page-agent-ui.js",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const enUS = {
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
expand: 'Expand history',
|
expand: 'Expand history',
|
||||||
collapse: 'Collapse history',
|
collapse: 'Collapse history',
|
||||||
step: 'Step {{number}} · {{time}}{{duration}}',
|
step: 'Step {{number}}',
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
clicking: 'Clicking element [{{index}}]...',
|
clicking: 'Clicking element [{{index}}]...',
|
||||||
@@ -63,7 +63,7 @@ const zhCN = {
|
|||||||
close: '关闭',
|
close: '关闭',
|
||||||
expand: '展开历史',
|
expand: '展开历史',
|
||||||
collapse: '收起历史',
|
collapse: '收起历史',
|
||||||
step: '步骤 {{number}} · {{time}}{{duration}}',
|
step: '步骤 {{number}}',
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
clicking: '正在点击元素 [{{index}}]...',
|
clicking: '正在点击元素 [{{index}}]...',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { I18n, type SupportedLanguage } from '../i18n'
|
import { I18n, type SupportedLanguage } from '../i18n'
|
||||||
import { truncate } from '../utils'
|
import { truncate } from '../utils'
|
||||||
import { createCard, createReflectionLines, formatTime } from './cards'
|
import { createCard, createReflectionLines } from './cards'
|
||||||
import type { AgentActivity, PanelAgentAdapter } from './types'
|
import type { AgentActivity, PanelAgentAdapter } from './types'
|
||||||
|
|
||||||
import styles from './Panel.module.css'
|
import styles from './Panel.module.css'
|
||||||
@@ -187,7 +187,6 @@ export class Panel {
|
|||||||
tempCard.innerHTML = createCard({
|
tempCard.innerHTML = createCard({
|
||||||
icon: '❓',
|
icon: '❓',
|
||||||
content: `Question: ${question}`,
|
content: `Question: ${question}`,
|
||||||
meta: formatTime(this.#config.language ?? 'en-US'),
|
|
||||||
type: 'question',
|
type: 'question',
|
||||||
})
|
})
|
||||||
const cardElement = tempCard.firstElementChild as HTMLElement
|
const cardElement = tempCard.firstElementChild as HTMLElement
|
||||||
@@ -374,6 +373,7 @@ export class Panel {
|
|||||||
wrapper.id = 'page-agent-runtime_agent-panel'
|
wrapper.id = 'page-agent-runtime_agent-panel'
|
||||||
wrapper.className = styles.wrapper
|
wrapper.className = styles.wrapper
|
||||||
wrapper.setAttribute('data-browser-use-ignore', 'true')
|
wrapper.setAttribute('data-browser-use-ignore', 'true')
|
||||||
|
wrapper.setAttribute('data-page-agent-ignore', 'true')
|
||||||
|
|
||||||
wrapper.innerHTML = `
|
wrapper.innerHTML = `
|
||||||
<div class="${styles.background}"></div>
|
<div class="${styles.background}"></div>
|
||||||
@@ -591,15 +591,12 @@ export class Panel {
|
|||||||
/** Create cards for a history event */
|
/** Create cards for a history event */
|
||||||
#createHistoryCards(event: PanelAgentAdapter['history'][number]): string[] {
|
#createHistoryCards(event: PanelAgentAdapter['history'][number]): string[] {
|
||||||
const cards: string[] = []
|
const cards: string[] = []
|
||||||
const time = formatTime(this.#config.language ?? 'en-US')
|
|
||||||
const meta =
|
const meta =
|
||||||
event.type === 'step' && event.stepIndex !== undefined
|
event.type === 'step' && event.stepIndex !== undefined
|
||||||
? this.#i18n.t('ui.panel.step', {
|
? this.#i18n.t('ui.panel.step', {
|
||||||
number: (event.stepIndex + 1).toString(),
|
number: (event.stepIndex + 1).toString(),
|
||||||
time,
|
|
||||||
duration: '',
|
|
||||||
})
|
})
|
||||||
: time
|
: undefined
|
||||||
|
|
||||||
if (event.type === 'step') {
|
if (event.type === 'step') {
|
||||||
// Reflection card
|
// Reflection card
|
||||||
@@ -636,7 +633,7 @@ export class Panel {
|
|||||||
/** Create cards for an action */
|
/** Create cards for an action */
|
||||||
#createActionCards(
|
#createActionCards(
|
||||||
action: { name: string; input: unknown; output: string },
|
action: { name: string; input: unknown; output: string },
|
||||||
meta: string
|
meta?: string
|
||||||
): string[] {
|
): string[] {
|
||||||
const cards: string[] = []
|
const cards: string[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -32,16 +32,6 @@ export function createCard({ icon, content, meta, type }: CardOptions): string {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Format timestamp for cards */
|
|
||||||
export function formatTime(locale: string = 'en-US'): string {
|
|
||||||
return new Date().toLocaleTimeString(locale, {
|
|
||||||
hour12: false,
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
second: '2-digit',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create reflection lines from reflection object */
|
/** Create reflection lines from reflection object */
|
||||||
export function createReflectionLines(reflection: {
|
export function createReflectionLines(reflection: {
|
||||||
evaluation_previous_goal?: string
|
evaluation_previous_goal?: string
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Located in `src/components/ui/`:
|
|||||||
src/
|
src/
|
||||||
├── pages/
|
├── pages/
|
||||||
│ ├── home/
|
│ ├── home/
|
||||||
│ │ ├── HomeContent.tsx # Homepage sections
|
│ │ ├── index.tsx # Homepage
|
||||||
│ │ └── ...Section.tsx
|
│ │ └── ...Section.tsx
|
||||||
│ └── docs/
|
│ └── docs/
|
||||||
│ ├── index.tsx # Docs route switch
|
│ ├── index.tsx # Docs route switch
|
||||||
|
|||||||
@@ -45,13 +45,24 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root">
|
||||||
|
<div id="sk">
|
||||||
|
<p class="sk-text" id="sk-text">Loading...</p>
|
||||||
|
<style>
|
||||||
|
#sk{display:flex;align-items:center;justify-content:center;min-height:100vh;background:linear-gradient(135deg,#eff6ff,#f5f3ff)}
|
||||||
|
@media(prefers-color-scheme:dark){#sk{background:linear-gradient(135deg,#111827,#1f2937)}}
|
||||||
|
.sk-text{font:400 14px/1 system-ui,sans-serif;color:#94a3b8;animation:skf 2s ease-in-out infinite}
|
||||||
|
@keyframes skf{0%,100%{opacity:.6}50%{opacity:.3}}
|
||||||
|
</style>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script type="module" src="./src/main.tsx"></script>
|
<script type="module" src="./src/main.tsx"></script>
|
||||||
<script>
|
<script>
|
||||||
// Dynamically update html lang attribute based on i18n detection
|
|
||||||
const updateHtmlLang = () => {
|
const updateHtmlLang = () => {
|
||||||
const lang = localStorage.getItem('i18nextLng') || navigator.language || 'zh-CN'
|
const lang = localStorage.getItem('i18nextLng') || navigator.language || 'zh-CN'
|
||||||
document.documentElement.lang = lang
|
document.documentElement.lang = lang
|
||||||
|
const el = document.getElementById('sk-text')
|
||||||
|
if (el) el.textContent = lang.startsWith('zh') ? '加载中...' : 'Loading...'
|
||||||
}
|
}
|
||||||
updateHtmlLang()
|
updateHtmlLang()
|
||||||
window.addEventListener('storage', updateHtmlLang)
|
window.addEventListener('storage', updateHtmlLang)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.0",
|
"version": "1.5.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 0.0.0.0",
|
"dev": "vite --host 0.0.0.0",
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
"@types/react-dom": "^19.2.1",
|
"@types/react-dom": "^19.2.1",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.576.0",
|
||||||
"motion": "^12.34.3",
|
"motion": "^12.34.5",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.9.0",
|
"simple-icons": "^16.10.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
|
|||||||
+14
-6
@@ -5,10 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
import { Badge } from './badge'
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Types
|
// Types
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -35,6 +34,8 @@ export interface APIReferenceProps {
|
|||||||
description?: React.ReactNode
|
description?: React.ReactNode
|
||||||
/** Property definitions */
|
/** Property definitions */
|
||||||
properties: PropDefinition[]
|
properties: PropDefinition[]
|
||||||
|
/** Display variant: 'properties' for fields, 'methods' for methods */
|
||||||
|
variant?: 'properties' | 'methods'
|
||||||
/** Additional CSS classes */
|
/** Additional CSS classes */
|
||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,14 @@ export interface APIReferenceProps {
|
|||||||
// Component
|
// Component
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export function APIReference({ title, description, properties, className }: APIReferenceProps) {
|
export function APIReference({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
properties,
|
||||||
|
variant = 'properties',
|
||||||
|
className,
|
||||||
|
}: APIReferenceProps) {
|
||||||
|
const isMethodsVariant = variant === 'methods'
|
||||||
return (
|
return (
|
||||||
<div className={cn('my-6', className)}>
|
<div className={cn('my-6', className)}>
|
||||||
{title && (
|
{title && (
|
||||||
@@ -58,10 +66,10 @@ export function APIReference({ title, description, properties, className }: APIR
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-gray-50 dark:bg-gray-800/50">
|
<tr className="bg-gray-50 dark:bg-gray-800/50">
|
||||||
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
|
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
|
||||||
Property
|
{isMethodsVariant ? 'Method' : 'Property'}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
|
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
|
||||||
Type
|
{isMethodsVariant ? 'Return Type' : 'Type'}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300 hidden md:table-cell">
|
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300 hidden md:table-cell">
|
||||||
Default
|
Default
|
||||||
@@ -120,7 +128,7 @@ function PropRow({ name, type, required, defaultValue, description, status }: Pr
|
|||||||
|
|
||||||
{/* Type */}
|
{/* Type */}
|
||||||
<td className="px-4 py-3 align-top">
|
<td className="px-4 py-3 align-top">
|
||||||
<code className="font-mono text-xs text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded whitespace-nowrap">
|
<code className="font-mono text-xs text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded wrap-break-word">
|
||||||
{type}
|
{type}
|
||||||
</code>
|
</code>
|
||||||
</td>
|
</td>
|
||||||
@@ -42,7 +42,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`group relative ${containerClasses} rounded-xl border shadow-2xl overflow-hidden ${className}`}
|
className={`group relative ${containerClasses} rounded-xl border shadow-2xl my-4 overflow-hidden ${className}`}
|
||||||
>
|
>
|
||||||
{/* 编辑器顶部栏 */}
|
{/* 编辑器顶部栏 */}
|
||||||
{showHeader && (
|
{showHeader && (
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Demo build (auto-init with demo LLM, for quick testing)
|
// Demo build (auto-init with demo LLM, for quick testing)
|
||||||
export const CDN_DEMO_URL =
|
export const CDN_DEMO_URL =
|
||||||
'https://cdn.jsdelivr.net/npm/page-agent@1.4.0/dist/iife/page-agent.demo.js'
|
'https://cdn.jsdelivr.net/npm/page-agent@1.5.3/dist/iife/page-agent.demo.js'
|
||||||
export const CDN_DEMO_CN_URL =
|
export const CDN_DEMO_CN_URL =
|
||||||
'https://registry.npmmirror.com/page-agent/1.4.0/files/dist/iife/page-agent.demo.js'
|
'https://registry.npmmirror.com/page-agent/1.5.3/files/dist/iife/page-agent.demo.js'
|
||||||
|
|
||||||
// Demo LLM for website testing (homepage quick trial uses flash)
|
// Demo LLM for website testing (homepage quick trial uses flash)
|
||||||
export const DEMO_MODEL = 'qwen3.5-flash'
|
export const DEMO_MODEL = 'qwen3.5-flash'
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import FeaturesSection from './home/FeaturesSection'
|
|
||||||
import HeroSection from './home/HeroSection'
|
|
||||||
import OneMoreThingSection from './home/OneMoreThingSection'
|
|
||||||
import ScenariosSection from './home/ScenariosSection'
|
|
||||||
|
|
||||||
export default function HomePage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<HeroSection />
|
|
||||||
<FeaturesSection />
|
|
||||||
<ScenariosSection />
|
|
||||||
<OneMoreThingSection />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -34,10 +34,6 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
|||||||
title: isZh ? '故障排查' : 'Troubleshooting',
|
title: isZh ? '故障排查' : 'Troubleshooting',
|
||||||
path: '/introduction/troubleshooting',
|
path: '/introduction/troubleshooting',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '🚧 ' + (isZh ? '最佳实践' : 'Best Practices'),
|
|
||||||
path: '/integration/best-practices',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +46,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
|||||||
{ title: isZh ? 'Chrome 扩展' : 'Chrome Extension', path: '/features/chrome-extension' },
|
{ title: isZh ? 'Chrome 扩展' : 'Chrome Extension', path: '/features/chrome-extension' },
|
||||||
{
|
{
|
||||||
title: isZh ? '接入第三方 Agent' : 'Third-party Agent',
|
title: isZh ? '接入第三方 Agent' : 'Third-party Agent',
|
||||||
path: '/integration/third-party-agent',
|
path: '/features/third-party-agent',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -59,10 +55,11 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
|||||||
items: [
|
items: [
|
||||||
{ title: 'PageAgent', path: '/advanced/page-agent' },
|
{ title: 'PageAgent', path: '/advanced/page-agent' },
|
||||||
{ title: 'PageAgentCore', path: '/advanced/page-agent-core' },
|
{ title: 'PageAgentCore', path: '/advanced/page-agent-core' },
|
||||||
|
{ title: 'PageController', path: '/advanced/page-controller' },
|
||||||
{ title: isZh ? '自定义 UI' : 'Custom UI', path: '/advanced/custom-ui' },
|
{ title: isZh ? '自定义 UI' : 'Custom UI', path: '/advanced/custom-ui' },
|
||||||
{
|
{
|
||||||
title: '🚧 ' + (isZh ? '安全与权限' : 'Security & Permissions'),
|
title: '🚧 ' + (isZh ? '安全与权限' : 'Security & Permissions'),
|
||||||
path: '/integration/security-permissions',
|
path: '/advanced/security-permissions',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { APIDivider, APIReference } from '@/components/APIReference'
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
import { Heading } from '@/components/Heading'
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIDivider, APIReference } from '@/components/ui/api-reference'
|
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function CustomUIDocs() {
|
export default function CustomUIDocs() {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { Link } from 'wouter'
|
||||||
|
|
||||||
|
import { APIDivider, APIReference, TypeRef } from '@/components/APIReference'
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
import { Heading } from '@/components/Heading'
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIDivider, APIReference, TypeRef } from '@/components/ui/api-reference'
|
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function PageAgentCoreDocs() {
|
export default function PageAgentCoreDocs() {
|
||||||
@@ -50,19 +52,13 @@ const agent = new PageAgentCore({
|
|||||||
baseURL: 'https://api.openai.com/v1',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
apiKey: 'your-api-key',
|
apiKey: 'your-api-key',
|
||||||
model: 'gpt-5.2',
|
model: 'gpt-5.2',
|
||||||
language: 'en-US',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen to events for UI display
|
// Listen to events for UI display
|
||||||
|
|
||||||
agent.addEventListener('statuschange', () => {
|
agent.addEventListener('statuschange', () => {
|
||||||
console.log('Status:', agent.status)
|
console.log('Status:', agent.status)
|
||||||
})
|
})
|
||||||
|
|
||||||
agent.addEventListener('historychange', () => {
|
|
||||||
console.log('History:', agent.history)
|
|
||||||
})
|
|
||||||
|
|
||||||
agent.addEventListener('activity', (e) => {
|
agent.addEventListener('activity', (e) => {
|
||||||
const activity = (e as CustomEvent).detail
|
const activity = (e as CustomEvent).detail
|
||||||
console.log('Activity:', activity.type)
|
console.log('Activity:', activity.type)
|
||||||
@@ -75,14 +71,54 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
<APIDivider title={isZh ? '配置' : 'Configuration'} />
|
<APIDivider title={isZh ? '配置' : 'Configuration'} />
|
||||||
|
|
||||||
{/* LLM Configuration */}
|
{/* Configuration */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<Heading id="llmconfig">LLMConfig</Heading>
|
<Heading id="configuration">PageAgentCoreConfig</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '配置与大语言模型的连接参数。支持 OpenAI 兼容的 API。'
|
? 'PageAgentCoreConfig = AgentConfig & { pageController: PageController }。AgentConfig 包含以下配置项:'
|
||||||
: 'Configure connection parameters for the language model. Supports OpenAI-compatible APIs.'}
|
: 'PageAgentCoreConfig = AgentConfig & { pageController: PageController }. AgentConfig contains the following options:'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* PageController */}
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
|
||||||
|
PageController
|
||||||
|
</h3>
|
||||||
|
<APIReference
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'pageController',
|
||||||
|
type: 'PageController',
|
||||||
|
required: true,
|
||||||
|
description: isZh ? (
|
||||||
|
<>
|
||||||
|
<Link
|
||||||
|
href="/advanced/page-controller"
|
||||||
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
PageController
|
||||||
|
</Link>{' '}
|
||||||
|
实例,用于 DOM 操作和元素交互。
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Link
|
||||||
|
href="/advanced/page-controller"
|
||||||
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
PageController
|
||||||
|
</Link>{' '}
|
||||||
|
instance for DOM operations and element interaction.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* LLM Config */}
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
|
||||||
|
{isZh ? 'LLM 配置' : 'LLM Config'}
|
||||||
|
</h3>
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
@@ -110,7 +146,6 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
{
|
{
|
||||||
name: 'temperature',
|
name: 'temperature',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
defaultValue: '0',
|
|
||||||
description: isZh
|
description: isZh
|
||||||
? '模型温度参数,控制输出随机性'
|
? '模型温度参数,控制输出随机性'
|
||||||
: 'Model temperature, controls output randomness',
|
: 'Model temperature, controls output randomness',
|
||||||
@@ -130,16 +165,11 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Agent Configuration */}
|
{/* Agent Config */}
|
||||||
<section className="mb-10">
|
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
|
||||||
<Heading id="agentconfig">AgentConfig</Heading>
|
{isZh ? 'Agent 配置' : 'Agent Config'}
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
</h3>
|
||||||
{isZh
|
|
||||||
? '配置 Agent 的行为、生命周期钩子和扩展能力。'
|
|
||||||
: 'Configure agent behavior, lifecycle hooks, and extension capabilities.'}
|
|
||||||
</p>
|
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
@@ -151,7 +181,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
{
|
{
|
||||||
name: 'maxSteps',
|
name: 'maxSteps',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
defaultValue: '20',
|
defaultValue: '40',
|
||||||
description: isZh ? '每个任务的最大步骤数' : 'Maximum number of steps per task',
|
description: isZh ? '每个任务的最大步骤数' : 'Maximum number of steps per task',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,8 +196,8 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
name: 'instructions',
|
name: 'instructions',
|
||||||
type: 'InstructionsConfig',
|
type: 'InstructionsConfig',
|
||||||
description: isZh
|
description: isZh
|
||||||
? '指导 Agent 行为的指令配置'
|
? '指导 Agent 行为的指令配置,见下方类型定义'
|
||||||
: 'Instructions to guide agent behavior',
|
: 'Instructions to guide agent behavior, see type definition below',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'transformPageContent',
|
name: 'transformPageContent',
|
||||||
@@ -177,7 +207,15 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
: 'Transform page content before sending to LLM, useful for data masking',
|
: 'Transform page content before sending to LLM, useful for data masking',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'experimentalScriptExecutionTool',
|
name: 'customSystemPrompt',
|
||||||
|
type: 'string',
|
||||||
|
status: 'experimental',
|
||||||
|
description: isZh
|
||||||
|
? '完全覆盖默认系统提示词。谨慎使用。'
|
||||||
|
: 'Completely override the default system prompt. Use with caution.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'experimentalScript\nExecutionTool',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: 'false',
|
defaultValue: 'false',
|
||||||
status: 'experimental',
|
status: 'experimental',
|
||||||
@@ -185,134 +223,58 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
? '启用实验性 JavaScript 执行工具'
|
? '启用实验性 JavaScript 执行工具'
|
||||||
: 'Enable experimental JavaScript execution tool',
|
: 'Enable experimental JavaScript execution tool',
|
||||||
},
|
},
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold mt-6 mb-3">InstructionsConfig</h3>
|
|
||||||
<APIReference
|
|
||||||
properties={[
|
|
||||||
{
|
{
|
||||||
name: 'system',
|
name: 'experimentalLlmsTxt',
|
||||||
type: 'string',
|
type: 'boolean',
|
||||||
|
defaultValue: 'false',
|
||||||
|
status: 'experimental',
|
||||||
description: isZh
|
description: isZh
|
||||||
? '全局系统级指令,应用于所有任务'
|
? '从当前站点根目录获取 /llms.txt 并作为上下文提供给 LLM'
|
||||||
: 'Global system-level instructions, applied to all tasks',
|
: 'Fetch /llms.txt from site origin and include as LLM context',
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'getPageInstructions',
|
|
||||||
type: '(url: string) => string | undefined | null',
|
|
||||||
description: isZh
|
|
||||||
? '动态页面级指令回调,在每个步骤前调用'
|
|
||||||
: 'Dynamic page-level instructions callback, called before each step',
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Lifecycle Hooks */}
|
{/* Lifecycle Hooks */}
|
||||||
<section className="mb-10">
|
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
|
||||||
<Heading id="lifecycle-hooks">{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}</Heading>
|
{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}
|
||||||
|
<span className="ml-2 text-xs font-normal text-amber-600 dark:text-amber-400">
|
||||||
|
experimental
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-4">
|
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-4">
|
||||||
<p className="text-amber-800 dark:text-amber-200 text-sm">
|
<p className="text-amber-800 dark:text-amber-200 text-sm">
|
||||||
<strong>⚠️ {isZh ? '警告' : 'Warning'}:</strong>{' '}
|
|
||||||
{isZh
|
{isZh
|
||||||
? '这些接口高度实验性,可能在未来版本中发生变化。建议优先使用事件系统(Events)来监听 Agent 状态。'
|
? '这些接口高度实验性,可能在未来版本中发生变化。'
|
||||||
: 'These APIs are highly experimental and may change in future versions. Prefer using the Events system for monitoring agent state.'}
|
: 'These APIs are highly experimental and may change in future versions. '}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
|
||||||
{isZh
|
|
||||||
? '所有生命周期钩子都接收 agent 实例作为第一个参数,便于在回调中访问 Agent 状态和方法。'
|
|
||||||
: 'All lifecycle hooks receive the agent instance as first parameter, making it easy to access agent state and methods in callbacks.'}
|
|
||||||
</p>
|
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
name: 'onBeforeStep',
|
name: 'onBeforeStep',
|
||||||
type: '(agent: PageAgentCore, stepCount: number) => void | Promise<void>',
|
type: '(agent, stepCount) => void | Promise<void>',
|
||||||
description: isZh ? '每个步骤执行前调用' : 'Called before each step execution',
|
description: isZh ? '每个步骤执行前调用' : 'Called before each step execution',
|
||||||
status: 'experimental',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'onAfterStep',
|
name: 'onAfterStep',
|
||||||
type: '(agent: PageAgentCore, history: HistoricalEvent[]) => void | Promise<void>',
|
type: '(agent, history) => void | Promise<void>',
|
||||||
description: isZh ? '每个步骤执行后调用' : 'Called after each step execution',
|
description: isZh ? '每个步骤执行后调用' : 'Called after each step execution',
|
||||||
status: 'experimental',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'onBeforeTask',
|
name: 'onBeforeTask',
|
||||||
type: '(agent: PageAgentCore) => void | Promise<void>',
|
type: '(agent) => void | Promise<void>',
|
||||||
description: isZh ? '任务开始前调用' : 'Called before task starts',
|
description: isZh ? '任务开始前调用' : 'Called before task starts',
|
||||||
status: 'experimental',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'onAfterTask',
|
name: 'onAfterTask',
|
||||||
type: '(agent: PageAgentCore, result: ExecutionResult) => void | Promise<void>',
|
type: '(agent, result) => void | Promise<void>',
|
||||||
description: isZh ? '任务结束后调用' : 'Called after task ends',
|
description: isZh ? '任务结束后调用' : 'Called after task ends',
|
||||||
status: 'experimental',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'onDispose',
|
name: 'onDispose',
|
||||||
type: '(agent: PageAgentCore, reason?: string) => void',
|
type: '(agent, reason?) => void',
|
||||||
description: isZh ? 'Agent 销毁时调用' : 'Called when agent is disposed',
|
description: isZh ? 'Agent 销毁时调用' : 'Called when agent is disposed',
|
||||||
status: 'experimental',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* PageController Configuration */}
|
|
||||||
<section className="mb-10">
|
|
||||||
<Heading id="pagecontrollerconfig">PageControllerConfig</Heading>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
|
||||||
{isZh
|
|
||||||
? '配置 DOM 提取、元素交互和视觉反馈。'
|
|
||||||
: 'Configure DOM extraction, element interaction, and visual feedback.'}
|
|
||||||
</p>
|
|
||||||
<APIReference
|
|
||||||
properties={[
|
|
||||||
{
|
|
||||||
name: 'pageController',
|
|
||||||
type: 'PageController',
|
|
||||||
status: 'experimental',
|
|
||||||
description: isZh
|
|
||||||
? '自定义 PageController 实例。如不提供,将创建默认实例。'
|
|
||||||
: 'Custom PageController instance. If not provided, a default one will be created.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'enableMask',
|
|
||||||
type: 'boolean',
|
|
||||||
defaultValue: 'true',
|
|
||||||
description: isZh
|
|
||||||
? '启用视觉遮罩覆盖层,阻止用户在自动化期间操作'
|
|
||||||
: 'Enable visual mask overlay that blocks user interaction during automation',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'viewportExpansion',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: '0',
|
|
||||||
description: isZh
|
|
||||||
? '视口扩展像素数,-1 表示提取整个页面'
|
|
||||||
: 'Viewport expansion in pixels, -1 means extract entire page',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'interactiveBlacklist',
|
|
||||||
type: '(Element | (() => Element))[]',
|
|
||||||
description: isZh ? '要排除的交互元素列表' : 'Elements to exclude from interaction',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'interactiveWhitelist',
|
|
||||||
type: '(Element | (() => Element))[]',
|
|
||||||
description: isZh
|
|
||||||
? '要强制包含的交互元素列表'
|
|
||||||
: 'Elements to force include for interaction',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'includeAttributes',
|
|
||||||
type: 'string[]',
|
|
||||||
description: isZh
|
|
||||||
? '在 DOM 提取中包含的额外属性'
|
|
||||||
: 'Additional attributes to include in DOM extraction',
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -369,16 +331,24 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
|
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
|
||||||
<APIReference
|
<APIReference
|
||||||
|
variant="methods"
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
name: 'execute(task: string)',
|
name: 'execute(task)',
|
||||||
type: 'Promise<ExecutionResult>',
|
type: 'Promise<ExecutionResult>',
|
||||||
description: isZh
|
description: isZh
|
||||||
? '执行任务并返回结果。包含 success、data 和 history 字段。'
|
? '执行任务并返回结果。包含 success、data 和 history 字段。'
|
||||||
: 'Execute a task and return result. Contains success, data, and history fields.',
|
: 'Execute a task and return result. Contains success, data, and history fields.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dispose(reason?: string)',
|
name: 'stop()',
|
||||||
|
type: 'void',
|
||||||
|
description: isZh
|
||||||
|
? '停止当前任务。Agent 仍可复用。'
|
||||||
|
: 'Stop the current task. Agent remains reusable.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dispose()',
|
||||||
type: 'void',
|
type: 'void',
|
||||||
description: isZh
|
description: isZh
|
||||||
? '销毁 Agent 并清理资源'
|
? '销毁 Agent 并清理资源'
|
||||||
@@ -443,11 +413,8 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`interface ExecutionResult {
|
code={`interface ExecutionResult {
|
||||||
/** Whether the task completed successfully */
|
|
||||||
success: boolean
|
success: boolean
|
||||||
/** Result description from the agent */
|
|
||||||
data: string
|
data: string
|
||||||
/** Full execution history */
|
|
||||||
history: HistoricalEvent[]
|
history: HistoricalEvent[]
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -467,50 +434,21 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<APIDivider title={isZh ? '无头模式' : 'Headless Mode'} />
|
{/* InstructionsConfig */}
|
||||||
|
|
||||||
{/* Headless Usage */}
|
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<Heading id="headless-mode">{isZh ? '无头模式' : 'Headless Mode'}</Heading>
|
<Heading id="instructionsconfig">InstructionsConfig</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
|
||||||
{isZh
|
|
||||||
? '在非 DOM 环境中,你必须实现自定义的 PageController(例如远程操作页面或 Puppeteer)。'
|
|
||||||
: 'In non-DOM environments, you must implement a custom PageController (e.g., remote page control or Puppeteer).'}
|
|
||||||
</p>
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`import { PageAgentCore } from '@page-agent/core'
|
code={`interface InstructionsConfig {
|
||||||
import type { PageController } from '@page-agent/page-controller'
|
/** Global system-level instructions, applied to all tasks */
|
||||||
|
system?: string
|
||||||
|
|
||||||
class MyRemotePageController implements PageController {
|
/**
|
||||||
// Implement required methods for DOM extraction and interaction
|
* Dynamic page-level instructions callback.
|
||||||
}
|
* Called before each step to get instructions for the current page.
|
||||||
|
*/
|
||||||
const agent = new PageAgentCore({
|
getPageInstructions?: (url: string) => string | undefined
|
||||||
pageController: new MyRemotePageController(),
|
}`}
|
||||||
baseURL: 'https://api.openai.com/v1',
|
|
||||||
apiKey: 'your-api-key',
|
|
||||||
model: 'gpt-5.2',
|
|
||||||
language: 'en-US',
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen to events for UI display
|
|
||||||
|
|
||||||
agent.addEventListener('statuschange', () => {
|
|
||||||
console.log('Status:', agent.status)
|
|
||||||
})
|
|
||||||
|
|
||||||
agent.addEventListener('historychange', () => {
|
|
||||||
console.log('History:', agent.history)
|
|
||||||
})
|
|
||||||
|
|
||||||
agent.addEventListener('activity', (e) => {
|
|
||||||
const activity = (e as CustomEvent).detail
|
|
||||||
console.log('Activity:', activity.type)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Execute task
|
|
||||||
const result = await agent.execute('Fill in the form with test data')`}
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Link } from 'wouter'
|
|||||||
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
import { Heading } from '@/components/Heading'
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIReference, TypeRef } from '@/components/ui/api-reference'
|
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function PageAgentDocs() {
|
export default function PageAgentDocs() {
|
||||||
@@ -14,8 +13,8 @@ export default function PageAgentDocs() {
|
|||||||
|
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent 是带有内置 UI 面板的完整 Agent 类。它继承自 PageAgentCore,并自动创建交互面板。'
|
? 'PageAgent 是带有内置 UI 面板的完整 Agent 类。它继承自 PageAgentCore,并自动创建交互面板和 PageController。'
|
||||||
: 'PageAgent is the complete Agent class with built-in UI panel. It extends PageAgentCore and automatically creates an interactive panel.'}
|
: 'PageAgent is the complete Agent class with built-in UI panel. It extends PageAgentCore and automatically creates an interactive panel and PageController.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* When to use */}
|
{/* When to use */}
|
||||||
@@ -29,6 +28,11 @@ export default function PageAgentDocs() {
|
|||||||
: 'In most cases, you should use PageAgent. It provides a complete out-of-the-box experience:'}
|
: 'In most cases, you should use PageAgent. It provides a complete out-of-the-box experience:'}
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2 mb-6">
|
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2 mb-6">
|
||||||
|
<li>
|
||||||
|
{isZh
|
||||||
|
? '自动创建 PageController,处理 DOM 提取和元素操作'
|
||||||
|
: 'Automatically creates PageController for DOM extraction and element actions'}
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{isZh
|
{isZh
|
||||||
? '内置 UI 面板,显示任务进度、Agent 思考过程和操作结果'
|
? '内置 UI 面板,显示任务进度、Agent 思考过程和操作结果'
|
||||||
@@ -74,9 +78,8 @@ console.log(result.history) // Full execution history`}
|
|||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`class PageAgent extends PageAgentCore {
|
code={`class PageAgent extends PageAgentCore {
|
||||||
/** The UI panel instance */
|
|
||||||
panel: Panel
|
panel: Panel
|
||||||
|
pageController: PageController
|
||||||
constructor(config: PageAgentConfig)
|
constructor(config: PageAgentConfig)
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -90,7 +93,21 @@ console.log(result.history) // Full execution history`}
|
|||||||
>
|
>
|
||||||
PageAgentCore
|
PageAgentCore
|
||||||
</Link>
|
</Link>
|
||||||
,所有核心方法和事件都可用。详细的 API 参考请查看 PageAgentCore 文档。
|
,所有核心方法和事件都可用。配置项合并了{' '}
|
||||||
|
<Link
|
||||||
|
href="/advanced/page-agent-core#configuration"
|
||||||
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
AgentConfig
|
||||||
|
</Link>{' '}
|
||||||
|
和{' '}
|
||||||
|
<Link
|
||||||
|
href="/advanced/page-controller#configuration"
|
||||||
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
PageControllerConfig
|
||||||
|
</Link>
|
||||||
|
。
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -101,71 +118,34 @@ console.log(result.history) // Full execution history`}
|
|||||||
>
|
>
|
||||||
PageAgentCore
|
PageAgentCore
|
||||||
</Link>
|
</Link>
|
||||||
. All core methods and events are available. See PageAgentCore docs for detailed API
|
. All core methods and events are available. Config merges{' '}
|
||||||
reference.
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Configuration */}
|
|
||||||
<section className="mb-10">
|
|
||||||
<Heading id="configuration">{isZh ? '配置' : 'Configuration'}</Heading>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
|
||||||
{isZh
|
|
||||||
? 'PageAgent 使用与 PageAgentCore 相同的配置接口。'
|
|
||||||
: 'PageAgent uses the same configuration interface as PageAgentCore.'}
|
|
||||||
</p>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
|
||||||
{isZh ? (
|
|
||||||
<>
|
|
||||||
完整配置请参考{' '}
|
|
||||||
<Link
|
<Link
|
||||||
href="/advanced/page-agent-core"
|
href="/advanced/page-agent-core#configuration"
|
||||||
className="text-blue-600 dark:text-blue-400 hover:underline"
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
>
|
>
|
||||||
PageAgentCore 配置文档
|
AgentConfig
|
||||||
</Link>
|
|
||||||
。
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
See{' '}
|
|
||||||
<Link
|
|
||||||
href="/advanced/page-agent-core"
|
|
||||||
className="text-blue-600 dark:text-blue-400 hover:underline"
|
|
||||||
>
|
|
||||||
PageAgentCore configuration docs
|
|
||||||
</Link>{' '}
|
</Link>{' '}
|
||||||
for complete reference.
|
and{' '}
|
||||||
|
<Link
|
||||||
|
href="/advanced/page-controller#configuration"
|
||||||
|
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
PageControllerConfig
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Panel Property */}
|
{/* Panel */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<Heading id="panel-property">{isZh ? 'Panel 属性' : 'Panel Property'}</Heading>
|
<Heading id="panel">{isZh ? 'UI 面板' : 'UI Panel'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性访问它来控制 UI:'
|
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性控制 UI:'
|
||||||
: 'PageAgent automatically creates a Panel instance. You can access it via the panel property to control the UI:'}
|
: 'PageAgent automatically creates a Panel instance. You can control the UI via the panel property:'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<APIReference
|
|
||||||
properties={[
|
|
||||||
{
|
|
||||||
name: 'panel',
|
|
||||||
type: 'Panel',
|
|
||||||
required: true,
|
|
||||||
description: isZh
|
|
||||||
? '内置的 UI 面板实例,用于显示任务进度和接收用户输入。'
|
|
||||||
: 'The built-in UI panel instance for displaying task progress and receiving user input.',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? 'Panel 方法' : 'Panel Methods'}</h3>
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`// Show/hide the panel
|
code={`// Show/hide the panel
|
||||||
@@ -212,16 +192,16 @@ agent.panel.dispose()`}
|
|||||||
</tr>
|
</tr>
|
||||||
<tr className="bg-white dark:bg-gray-900">
|
<tr className="bg-white dark:bg-gray-900">
|
||||||
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
|
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
|
||||||
{isZh ? 'Headless 模式' : 'Headless Mode'}
|
{isZh ? '自动创建 PageController' : 'Auto-creates PageController'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
|
|
||||||
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400">✓</td>
|
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400">✓</td>
|
||||||
|
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="bg-white dark:bg-gray-900">
|
<tr className="bg-white dark:bg-gray-900">
|
||||||
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
|
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
|
||||||
{isZh ? '自定义 PageController' : 'Custom PageController'}
|
{isZh ? 'Headless 模式' : 'Headless Mode'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400">✓</td>
|
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
|
||||||
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400">✓</td>
|
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400">✓</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="bg-white dark:bg-gray-900">
|
<tr className="bg-white dark:bg-gray-900">
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
import { Link } from 'wouter'
|
||||||
|
|
||||||
|
import { APIDivider, APIReference } from '@/components/APIReference'
|
||||||
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
|
export default function PageControllerDocs() {
|
||||||
|
const { isZh } = useLanguage()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className="text-4xl font-bold mb-6">PageController</h1>
|
||||||
|
|
||||||
|
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||||
|
{isZh
|
||||||
|
? 'PageController 负责 DOM 提取和元素交互,独立于 LLM。它将页面状态结构化为 LLM 可消费的格式,并执行元素级操作。'
|
||||||
|
: 'PageController handles DOM extraction and element interaction, independent of LLM. It structures page state into LLM-consumable format and executes element-level actions.'}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Basic Usage */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="basic-usage">{isZh ? '基本用法' : 'Basic Usage'}</Heading>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
{isZh
|
||||||
|
? 'PageAgent 接受 PageController 配置项:'
|
||||||
|
: 'PageAgent accepts PageController options:'}
|
||||||
|
</p>
|
||||||
|
<CodeEditor
|
||||||
|
language="typescript"
|
||||||
|
code={`import { PageAgent } from 'page-agent'
|
||||||
|
|
||||||
|
const agent = new PageAgent({
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
|
||||||
|
// PageController options
|
||||||
|
enableMask: true,
|
||||||
|
viewportExpansion: 0,
|
||||||
|
})`}
|
||||||
|
/>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mt-4">
|
||||||
|
{isZh
|
||||||
|
? 'PageAgentCore 接受 PageController 实例:'
|
||||||
|
: 'PageAgentCore accepts a PageController instance:'}
|
||||||
|
</p>
|
||||||
|
<CodeEditor
|
||||||
|
language="typescript"
|
||||||
|
code={`import { PageAgentCore } from '@page-agent/core'
|
||||||
|
import { PageController } from '@page-agent/page-controller'
|
||||||
|
|
||||||
|
const pageController = new PageController({
|
||||||
|
enableMask: true,
|
||||||
|
viewportExpansion: -1, // extract full page
|
||||||
|
})
|
||||||
|
|
||||||
|
const agent = new PageAgentCore({
|
||||||
|
pageController,
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
})`}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<APIDivider title={isZh ? '配置' : 'Configuration'} />
|
||||||
|
|
||||||
|
{/* Configuration */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="configuration">PageControllerConfig</Heading>
|
||||||
|
<APIReference
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'enableMask',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: 'false',
|
||||||
|
description: isZh
|
||||||
|
? '启用视觉遮罩覆盖层,在自动化期间阻止用户操作页面。通过 PageAgent 创建时默认为 true。'
|
||||||
|
: 'Enable visual mask overlay that blocks user interaction during automation. Defaults to true when created via PageAgent.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'viewportExpansion',
|
||||||
|
type: 'number',
|
||||||
|
defaultValue: '0',
|
||||||
|
description: isZh
|
||||||
|
? '向视口外扩展提取的像素数。设为 -1 表示提取整个页面。'
|
||||||
|
: 'Pixels to expand extraction beyond viewport. Set to -1 to extract the entire page.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'interactiveBlacklist',
|
||||||
|
type: '(Element | (() => Element))[]',
|
||||||
|
description: isZh
|
||||||
|
? '要排除的交互元素列表。支持元素引用或返回元素的函数(延迟求值)。'
|
||||||
|
: 'Elements to exclude from interaction. Supports element references or functions returning elements (lazy evaluation).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'interactiveWhitelist',
|
||||||
|
type: '(Element | (() => Element))[]',
|
||||||
|
description: isZh
|
||||||
|
? '要强制包含的交互元素列表。支持元素引用或返回元素的函数。'
|
||||||
|
: 'Elements to force include for interaction. Supports element references or functions returning elements.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'includeAttributes',
|
||||||
|
type: 'string[]',
|
||||||
|
description: isZh
|
||||||
|
? '在 DOM 提取中包含的额外 HTML 属性(如 data-testid)。默认已包含常见属性如 role, aria-label 等。'
|
||||||
|
: 'Additional HTML attributes to include in DOM extraction (e.g. data-testid). Common attributes like role, aria-label are included by default.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<APIDivider title={isZh ? '方法' : 'Methods'} />
|
||||||
|
|
||||||
|
{/* Methods */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '状态查询' : 'State Queries'}</h3>
|
||||||
|
<APIReference
|
||||||
|
variant="methods"
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'getBrowserState()',
|
||||||
|
type: 'Promise<BrowserState>',
|
||||||
|
description: isZh
|
||||||
|
? '获取结构化的浏览器状态(URL、标题、简化 HTML 等),自动调用 updateTree() 刷新 DOM。这是 Agent 在每步使用的主要方法。'
|
||||||
|
: 'Get structured browser state (URL, title, simplified HTML, etc.), automatically calls updateTree() to refresh DOM. This is the primary method the agent uses each step.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updateTree()',
|
||||||
|
type: 'Promise<string>',
|
||||||
|
description: isZh
|
||||||
|
? '刷新 DOM 树并返回简化 HTML。通常不需要手动调用 —— getBrowserState() 会自动调用。'
|
||||||
|
: 'Refresh DOM tree and return simplified HTML. Usually not needed manually — getBrowserState() calls it automatically.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'getCurrentUrl()',
|
||||||
|
type: 'Promise<string>',
|
||||||
|
description: isZh ? '获取当前页面 URL。' : 'Get current page URL.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '元素操作' : 'Element Actions'}</h3>
|
||||||
|
<APIReference
|
||||||
|
variant="methods"
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'clickElement(index)',
|
||||||
|
type: 'Promise<ActionResult>',
|
||||||
|
description: isZh
|
||||||
|
? '按索引点击元素。索引来自简化 HTML 中的 [N] 标记。'
|
||||||
|
: 'Click element by index. Index comes from [N] markers in simplified HTML.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'inputText(index, text)',
|
||||||
|
type: 'Promise<ActionResult>',
|
||||||
|
description: isZh ? '向输入框元素填入文本。' : 'Input text into a form element.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'selectOption(index, optionText)',
|
||||||
|
type: 'Promise<ActionResult>',
|
||||||
|
description: isZh ? '在下拉框中选择选项。' : 'Select option in a dropdown element.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scroll(options)',
|
||||||
|
type: 'Promise<ActionResult>',
|
||||||
|
description: isZh
|
||||||
|
? '垂直滚动页面或指定元素。'
|
||||||
|
: 'Scroll page or specific element vertically.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scrollHorizontally(options)',
|
||||||
|
type: 'Promise<ActionResult>',
|
||||||
|
description: isZh
|
||||||
|
? '水平滚动页面或指定元素。'
|
||||||
|
: 'Scroll page or specific element horizontally.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '遮罩控制' : 'Mask Control'}</h3>
|
||||||
|
<APIReference
|
||||||
|
variant="methods"
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'showMask()',
|
||||||
|
type: 'Promise<void>',
|
||||||
|
description: isZh
|
||||||
|
? '显示视觉遮罩。需要 enableMask: true。'
|
||||||
|
: 'Show visual mask overlay. Requires enableMask: true.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hideMask()',
|
||||||
|
type: 'Promise<void>',
|
||||||
|
description: isZh ? '隐藏视觉遮罩。' : 'Hide visual mask overlay.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '生命周期' : 'Lifecycle'}</h3>
|
||||||
|
<APIReference
|
||||||
|
variant="methods"
|
||||||
|
properties={[
|
||||||
|
{
|
||||||
|
name: 'dispose()',
|
||||||
|
type: 'void',
|
||||||
|
description: isZh
|
||||||
|
? '清理所有资源(DOM 高亮、遮罩等)。Agent 销毁时自动调用。'
|
||||||
|
: 'Clean up all resources (DOM highlights, mask, etc.). Called automatically when agent disposes.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<APIDivider title={isZh ? '类型定义' : 'Type Definitions'} />
|
||||||
|
|
||||||
|
{/* BrowserState */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="browser-state">BrowserState</Heading>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
{isZh
|
||||||
|
? 'getBrowserState() 返回的结构化浏览器状态,直接用于构建 LLM prompt。'
|
||||||
|
: 'Structured browser state returned by getBrowserState(), used directly to build LLM prompts.'}
|
||||||
|
</p>
|
||||||
|
<CodeEditor
|
||||||
|
language="typescript"
|
||||||
|
code={`interface BrowserState {
|
||||||
|
url: string
|
||||||
|
title: string
|
||||||
|
header: string // page info + scroll position
|
||||||
|
content: string // simplified HTML of interactive elements
|
||||||
|
footer: string // scroll hint
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ActionResult */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="action-result">ActionResult</Heading>
|
||||||
|
<CodeEditor
|
||||||
|
language="typescript"
|
||||||
|
code={`interface ActionResult {
|
||||||
|
success: boolean
|
||||||
|
message: string
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Custom PageController */}
|
||||||
|
<section className="mb-10">
|
||||||
|
<Heading id="custom-implementation">
|
||||||
|
{isZh ? '自定义实现' : 'Custom Implementation'}
|
||||||
|
</Heading>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
{isZh
|
||||||
|
? '在非浏览器环境(如 Puppeteer、Playwright),你可以实现自定义 PageController。需要实现 Agent 使用的核心方法:'
|
||||||
|
: 'In non-browser environments (e.g. Puppeteer, Playwright), you can implement a custom PageController. Implement the core methods used by the agent:'}
|
||||||
|
</p>
|
||||||
|
<CodeEditor
|
||||||
|
language="typescript"
|
||||||
|
code={`import { PageAgentCore } from '@page-agent/core'
|
||||||
|
import type { PageController } from '@page-agent/page-controller'
|
||||||
|
|
||||||
|
class PuppeteerPageController implements PageController {
|
||||||
|
async getBrowserState() { /* ... */ }
|
||||||
|
async clickElement(index: number) { /* ... */ }
|
||||||
|
async inputText(index: number, text: string) { /* ... */ }
|
||||||
|
async scroll(options: { down: boolean; numPages: number }) { /* ... */ }
|
||||||
|
// ... other methods
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = new PageAgentCore({
|
||||||
|
pageController: new PuppeteerPageController(),
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
})`}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import BetaNotice from '@/components/BetaNotice'
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
import { Heading } from '@/components/Heading'
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
@@ -12,115 +11,100 @@ export default function CustomTools() {
|
|||||||
|
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||||
{isZh
|
{isZh
|
||||||
? '通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义严格的输入接口,让 AI 安全调用你的业务逻辑。'
|
? '通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义输入接口,让 AI 安全调用你的业务逻辑。'
|
||||||
: 'Extend AI Agent capabilities by registering custom tools. Use Zod to define strict input schemas for safe business logic calls.'}
|
: 'Extend AI Agent capabilities by registering custom tools. Define input schemas with Zod for safe business logic invocation.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<section>
|
<section>
|
||||||
<Heading id="tool-registration" className="text-2xl font-bold mb-4">
|
<Heading id="zod-version" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '工具注册' : 'Tool Registration'}
|
{isZh ? 'Zod 版本' : 'Zod Version'}
|
||||||
</Heading>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '每个自定义工具需要定义四个核心属性:name、description、input schema 和 execute 函数。'
|
? 'Page Agent 使用 Zod 定义工具的输入 schema。支持 Zod 3 (>=3.25.0) 和 Zod 4,请从 zod/v4 子路径导入。不支持 Zod Mini。'
|
||||||
: 'Each custom tool requires four core properties: name, description, input schema, and execute function.'}
|
: 'Page Agent uses Zod for tool input schemas. Both Zod 3 (>=3.25.0) and Zod 4 are supported. Always import from the zod/v4 subpath. Zod Mini is not supported.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
code={`import * as zod from 'zod'
|
code={`// Zod 3 (>=3.25.0) or Zod 4
|
||||||
import { PageAgent, tool } from 'page-agent'
|
import { z } from 'zod/v4'`}
|
||||||
|
|
||||||
// override internal tool
|
|
||||||
const customTools = {
|
|
||||||
ask_user: tool({
|
|
||||||
description:
|
|
||||||
'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
|
|
||||||
inputSchema: zod.object({
|
|
||||||
question: zod.string(),
|
|
||||||
}),
|
|
||||||
execute: async function (this: PageAgent, input) {
|
|
||||||
const answer = await do_some_thing(input.question)
|
|
||||||
return "✅ Received user answer: " + answer
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove internal tool
|
|
||||||
const customTools = {
|
|
||||||
scroll: null, // never scroll
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageAgent = new PageAgent({customTools})
|
|
||||||
`}
|
|
||||||
language="javascript"
|
language="javascript"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<Heading id="page-filter" className="text-2xl font-bold mb-4">
|
<Heading id="define-tools" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '页面过滤器' : 'Page Filter'}
|
{isZh ? '定义工具' : 'Define Tools'}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<BetaNotice />
|
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '通过 pageFilter 属性控制工具在哪些页面可见,提升安全性和用户体验。'
|
? '使用 tool() 辅助函数定义自定义工具,每个工具包含 description、inputSchema 和 execute 三个属性。'
|
||||||
: 'Control tool visibility on specific pages via the pageFilter property to enhance security and UX.'}
|
: 'Use the tool() helper to define custom tools with description, inputSchema, and execute.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
code={`pageAgent.registerTool({
|
code={`import { z } from 'zod/v4'
|
||||||
name: 'approveOrder',
|
import { PageAgent, tool } from 'page-agent'
|
||||||
description: '审批订单',
|
|
||||||
input: z.object({
|
const pageAgent = new PageAgent({
|
||||||
orderId: z.string(),
|
customTools: {
|
||||||
approved: z.boolean()
|
|
||||||
}),
|
//
|
||||||
execute: async (params) => {
|
add_to_cart: tool({
|
||||||
// 审批逻辑
|
description: 'Add a product to the shopping cart by its product ID.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
productId: z.string(),
|
||||||
|
quantity: z.number().min(1).default(1),
|
||||||
|
}),
|
||||||
|
execute: async function (input) {
|
||||||
|
await fetch('/api/cart', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(input),
|
||||||
|
})
|
||||||
|
return \`Added \${input.quantity}x \${input.productId} to cart.\`
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
//
|
||||||
|
search_knowledge_base: tool({
|
||||||
|
description: 'Search the internal knowledge base and return relevant articles.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
query: z.string(),
|
||||||
|
limit: z.number().max(10).default(3),
|
||||||
|
}),
|
||||||
|
execute: async function (input) {
|
||||||
|
const res = await fetch(
|
||||||
|
\`/api/kb?q=\${encodeURIComponent(input.query)}&limit=\${input.limit}\`
|
||||||
|
)
|
||||||
|
const articles = await res.json()
|
||||||
|
return JSON.stringify(articles)
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
// 可选:页面过滤器
|
|
||||||
pageFilter: {
|
|
||||||
// 只在订单管理页面显示
|
|
||||||
include: ['/admin/orders', '/admin/orders/*'],
|
|
||||||
// 排除特定页面
|
|
||||||
exclude: ['/admin/orders/archived']
|
|
||||||
}
|
|
||||||
})`}
|
})`}
|
||||||
language="javascript"
|
language="javascript"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<Heading id="best-practices" className="text-2xl font-bold mb-4">
|
<Heading id="override-remove" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '最佳实践' : 'Best Practices'}
|
{isZh ? '覆盖与移除内置工具' : 'Override & Remove Built-in Tools'}
|
||||||
</Heading>
|
</Heading>
|
||||||
<div className="space-y-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
{isZh
|
||||||
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2">
|
? '使用相同的名称可以覆盖内置工具的行为,设置为 null 则完全移除该工具。'
|
||||||
{isZh ? '⚡ 性能优化' : '⚡ Performance Optimization'}
|
: 'Use the same name to override a built-in tool, or set it to null to remove it entirely.'}
|
||||||
</h3>
|
</p>
|
||||||
<ul className="text-gray-600 dark:text-gray-300 space-y-1 text-sm">
|
|
||||||
<li>
|
<CodeEditor
|
||||||
{isZh
|
code={`const pageAgent = new PageAgent({
|
||||||
? '• 使用 pageFilter 减少不必要的工具加载'
|
customTools: {
|
||||||
: '• Use pageFilter to reduce unnecessary tool loading'}
|
scroll: null, // remove scroll tool
|
||||||
</li>
|
execute_javascript: null, // remove script execution
|
||||||
<li>
|
},
|
||||||
{isZh
|
})`}
|
||||||
? '• 在 execute 函数中实现适当的缓存机制'
|
language="javascript"
|
||||||
: '• Implement appropriate caching in execute functions'}
|
/>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{isZh
|
|
||||||
? '• 避免在工具中执行耗时的同步操作'
|
|
||||||
: '• Avoid long-running sync operations in tools'}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ const BASELINE = new Set([
|
|||||||
'gemini-3-flash',
|
'gemini-3-flash',
|
||||||
'deepseek-3.2',
|
'deepseek-3.2',
|
||||||
'qwen3.5-plus',
|
'qwen3.5-plus',
|
||||||
|
'qwen3.5-flash',
|
||||||
])
|
])
|
||||||
|
|
||||||
// Models grouped by brand, newest first
|
// Models grouped by brand, newest first
|
||||||
const MODEL_GROUPS: Record<string, string[]> = {
|
const MODEL_GROUPS: Record<string, string[]> = {
|
||||||
OpenAI: ['gpt-5.2', 'gpt-5.1', 'gpt-5', 'gpt-5-mini', 'gpt-4.1', 'gpt-4.1-mini'],
|
|
||||||
Qwen: [
|
Qwen: [
|
||||||
'qwen3.5-plus',
|
'qwen3.5-plus',
|
||||||
'qwen3.5-flash',
|
'qwen3.5-flash',
|
||||||
@@ -23,6 +23,7 @@ const MODEL_GROUPS: Record<string, string[]> = {
|
|||||||
'qwen-3-plus',
|
'qwen-3-plus',
|
||||||
'qwen3:14b (ollama)',
|
'qwen3:14b (ollama)',
|
||||||
],
|
],
|
||||||
|
OpenAI: ['gpt-5.2', 'gpt-5.1', 'gpt-5', 'gpt-5-mini', 'gpt-4.1', 'gpt-4.1-mini'],
|
||||||
DeepSeek: ['deepseek-3.2'],
|
DeepSeek: ['deepseek-3.2'],
|
||||||
Google: ['gemini-3-pro', 'gemini-3-flash', 'gemini-2.5'],
|
Google: ['gemini-3-pro', 'gemini-3-flash', 'gemini-2.5'],
|
||||||
Anthropic: [
|
Anthropic: [
|
||||||
@@ -247,16 +248,34 @@ LLM_MODEL_NAME="qwen3:14b"`}
|
|||||||
{isZh
|
{isZh
|
||||||
? '确保上下文长度大于输入 token 数,否则 Ollama 会静默截断 prompt。普通页面约需 15k token,随步骤增加。默认 4k 上下文长度无法正常工作'
|
? '确保上下文长度大于输入 token 数,否则 Ollama 会静默截断 prompt。普通页面约需 15k token,随步骤增加。默认 4k 上下文长度无法正常工作'
|
||||||
: 'Ensure context length exceeds input tokens, or Ollama will silently truncate prompts. ~15k tokens for a typical page, increases with steps. Default 4k context length will NOT work'}
|
: 'Ensure context length exceeds input tokens, or Ollama will silently truncate prompts. ~15k tokens for a typical page, increases with steps. Default 4k context length will NOT work'}
|
||||||
<ul className="text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5">
|
|
||||||
<li>
|
|
||||||
<code className="text-xs bg-gray-200 dark:bg-gray-700 px-1 rounded">
|
|
||||||
$env:OLLAMA_CONTEXT_LENGTH=64000; ollama serve
|
|
||||||
</code>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
||||||
|
{isZh ? '建议启动参数' : 'Recommended Startup'}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||||
|
{isZh
|
||||||
|
? '启动 Ollama 时建议配置以下环境变量:扩大上下文窗口、允许跨域访问、监听所有网络接口。'
|
||||||
|
: 'Start Ollama with these environment variables: larger context window, allow cross-origin access, and listen on all interfaces.'}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-xs font-medium text-gray-500 dark:text-gray-400">macOS / Linux</p>
|
||||||
|
<CodeEditor
|
||||||
|
code={`OLLAMA_CONTEXT_LENGTH=64000 OLLAMA_HOST=0.0.0.0:11434 OLLAMA_ORIGINS="*" ollama serve`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p className="text-xs font-medium text-gray-500 dark:text-gray-400 pt-2">
|
||||||
|
Windows (PowerShell)
|
||||||
|
</p>
|
||||||
|
<CodeEditor
|
||||||
|
code={`$env:OLLAMA_CONTEXT_LENGTH=64000; $env:OLLAMA_HOST="0.0.0.0:11434"; $env:OLLAMA_ORIGINS="*"; ollama serve`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
-21
@@ -105,27 +105,6 @@ const pageAgentTool = {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4 mb-6">
|
|
||||||
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-100 mb-2">
|
|
||||||
{isZh ? '⚠️ 注意事项' : '⚠️ Notes'}
|
|
||||||
</h3>
|
|
||||||
<ul className="text-yellow-800 dark:text-yellow-200 space-y-1 text-sm">
|
|
||||||
<li>
|
|
||||||
•{' '}
|
|
||||||
{isZh
|
|
||||||
? '确保目标网站允许自动化操作'
|
|
||||||
: 'Ensure target website allows automated operations'}
|
|
||||||
</li>
|
|
||||||
<li>• {isZh ? '实现适当的频率限制' : 'Implement appropriate rate limiting'}</li>
|
|
||||||
<li>
|
|
||||||
•{' '}
|
|
||||||
{isZh
|
|
||||||
? '敏感操作建议要求人工确认'
|
|
||||||
: 'Recommend human confirmation for sensitive operations'}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -6,15 +6,15 @@ import CustomUIDocs from './advanced/custom-ui/page'
|
|||||||
import PageAgentCoreDocs from './advanced/page-agent-core/page'
|
import PageAgentCoreDocs from './advanced/page-agent-core/page'
|
||||||
// Advanced
|
// Advanced
|
||||||
import PageAgentDocs from './advanced/page-agent/page'
|
import PageAgentDocs from './advanced/page-agent/page'
|
||||||
|
import PageControllerDocs from './advanced/page-controller/page'
|
||||||
|
import SecurityPermissions from './advanced/security-permissions/page'
|
||||||
// Features
|
// Features
|
||||||
import ChromeExtension from './features/chrome-extension/page'
|
import ChromeExtension from './features/chrome-extension/page'
|
||||||
import Instructions from './features/custom-instructions/page'
|
import Instructions from './features/custom-instructions/page'
|
||||||
import CustomTools from './features/custom-tools/page'
|
import CustomTools from './features/custom-tools/page'
|
||||||
import DataMasking from './features/data-masking/page'
|
import DataMasking from './features/data-masking/page'
|
||||||
import Models from './features/models/page'
|
import Models from './features/models/page'
|
||||||
import BestPractices from './integration/best-practices/page'
|
import ThirdPartyAgent from './features/third-party-agent/page'
|
||||||
import SecurityPermissions from './integration/security-permissions/page'
|
|
||||||
import ThirdPartyAgent from './integration/third-party-agent/page'
|
|
||||||
import Limitations from './introduction/limitations/page'
|
import Limitations from './introduction/limitations/page'
|
||||||
// Introduction
|
// Introduction
|
||||||
import Overview from './introduction/overview/page'
|
import Overview from './introduction/overview/page'
|
||||||
@@ -80,6 +80,11 @@ export default function DocsRouter() {
|
|||||||
<ChromeExtension />
|
<ChromeExtension />
|
||||||
</DocsPage>
|
</DocsPage>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/features/third-party-agent">
|
||||||
|
<DocsPage>
|
||||||
|
<ThirdPartyAgent />
|
||||||
|
</DocsPage>
|
||||||
|
</Route>
|
||||||
|
|
||||||
{/* Advanced */}
|
{/* Advanced */}
|
||||||
<Route path="/advanced/page-agent">
|
<Route path="/advanced/page-agent">
|
||||||
@@ -92,26 +97,21 @@ export default function DocsRouter() {
|
|||||||
<PageAgentCoreDocs />
|
<PageAgentCoreDocs />
|
||||||
</DocsPage>
|
</DocsPage>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/advanced/page-controller">
|
||||||
|
<DocsPage>
|
||||||
|
<PageControllerDocs />
|
||||||
|
</DocsPage>
|
||||||
|
</Route>
|
||||||
<Route path="/advanced/custom-ui">
|
<Route path="/advanced/custom-ui">
|
||||||
<DocsPage>
|
<DocsPage>
|
||||||
<CustomUIDocs />
|
<CustomUIDocs />
|
||||||
</DocsPage>
|
</DocsPage>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/integration/security-permissions">
|
<Route path="/advanced/security-permissions">
|
||||||
<DocsPage>
|
<DocsPage>
|
||||||
<SecurityPermissions />
|
<SecurityPermissions />
|
||||||
</DocsPage>
|
</DocsPage>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/integration/best-practices">
|
|
||||||
<DocsPage>
|
|
||||||
<BestPractices />
|
|
||||||
</DocsPage>
|
|
||||||
</Route>
|
|
||||||
<Route path="/integration/third-party-agent">
|
|
||||||
<DocsPage>
|
|
||||||
<ThirdPartyAgent />
|
|
||||||
</DocsPage>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
{/* Default redirect or 404 */}
|
{/* Default redirect or 404 */}
|
||||||
<Route path="/docs">
|
<Route path="/docs">
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import BetaNotice from '@/components/BetaNotice'
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
|
||||||
import { useLanguage } from '@/i18n/context'
|
|
||||||
|
|
||||||
export default function BestPractices() {
|
|
||||||
const { isZh } = useLanguage()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1 className="text-4xl font-bold mb-6">{isZh ? '最佳实践' : 'Best Practices'}</h1>
|
|
||||||
<BetaNotice />
|
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
|
|
||||||
{isZh
|
|
||||||
? '使用 page-agent 的最佳实践和常见问题解决方案。'
|
|
||||||
: 'Best practices and common solutions for using page-agent.'}
|
|
||||||
</p>
|
|
||||||
<CodeEditor code={`// TODO`} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -83,7 +83,7 @@ export default function LimitationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Interaction Limitations */}
|
{/* Interaction Limitations */}
|
||||||
<Heading id="interaction-capabilities" className="text-2xl font-bold mb-3">
|
<Heading id="interaction-capabilities" className="text-2xl font-bold mb-3 mt-6">
|
||||||
{isZh ? '交互能力' : 'Interaction Capabilities'}
|
{isZh ? '交互能力' : 'Interaction Capabilities'}
|
||||||
</Heading>
|
</Heading>
|
||||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
|
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
|
||||||
@@ -128,33 +128,25 @@ export default function LimitationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Understanding Limitations */}
|
{/* Understanding Limitations */}
|
||||||
<Heading id="understanding" className="text-2xl font-bold mb-3">
|
<Heading id="understanding" className="text-2xl font-bold mb-3 mt-6">
|
||||||
{isZh ? '理解能力' : 'Understanding'}
|
{isZh ? '理解能力' : 'Understanding'}
|
||||||
</Heading>
|
</Heading>
|
||||||
<div className="bg-amber-50 dark:bg-amber-900/20 border-l-4 border-amber-400 p-4 mb-6">
|
|
||||||
<p className="text-amber-800 dark:text-amber-200 mb-2 font-medium">
|
|
||||||
{isZh
|
|
||||||
? 'Page Agent 不使用多模态模型,不截图,没有视觉能力。仅通过 DOM 结构理解页面。'
|
|
||||||
: 'Page Agent does not use multimodal models, does not take screenshots, and has no visual capability. It reads pages through DOM structure only.'}
|
|
||||||
</p>
|
|
||||||
<p className="text-amber-700 dark:text-amber-300 text-sm">
|
|
||||||
{isZh
|
|
||||||
? '图片、Canvas、WebGL、SVG 等视觉内容无法被识别。页面的语义化程度和可访问性直接影响 AI 的理解准确性。'
|
|
||||||
: 'Images, Canvas, WebGL, SVG and other visual content cannot be recognized. Page semantic quality and accessibility directly affect AI accuracy.'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Website Quality */}
|
<p className="mb-2 font-medium">
|
||||||
<Heading id="page-quality-matters" className="text-2xl font-bold mb-3">
|
{isZh
|
||||||
{isZh ? '网页质量影响' : 'Page Quality Matters'}
|
? 'Page Agent 不使用多模态模型,不截图,没有视觉能力。仅通过 DOM 结构理解页面。'
|
||||||
</Heading>
|
: 'Page Agent does not use multimodal models, does not take screenshots, and has no visual capability. It reads pages through DOM structure only.'}
|
||||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6">
|
</p>
|
||||||
<p className="text-gray-600 dark:text-gray-300 text-sm">
|
<p className="mb-2 font-medium">
|
||||||
{isZh
|
{isZh
|
||||||
? '反常识的交互逻辑、纯视觉的操作提示、快速出现消失的元素等都会降低自动化成功率。语义化的 HTML 和良好的可访问性会显著提升效果。'
|
? '图片、Canvas、WebGL、SVG 等视觉内容无法被识别。页面的语义化程度和可访问性直接影响 AI 的理解准确性。'
|
||||||
: 'Counter-intuitive interactions, visual-only cues, and rapidly appearing/disappearing elements reduce automation success. Semantic HTML and good accessibility significantly improve results.'}
|
: 'Images, Canvas, WebGL, SVG and other visual content cannot be recognized. Page semantic quality and accessibility directly affect AI accuracy.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p className="mb-2 font-medium">
|
||||||
|
{isZh
|
||||||
|
? '反常识的交互逻辑、纯视觉的操作提示、快速出现消失的元素等都会降低自动化成功率。语义化的 HTML 和良好的可访问性会显著提升效果。'
|
||||||
|
: 'Counter-intuitive interactions, visual-only cues, and rapidly appearing/disappearing elements reduce automation success. Semantic HTML and good accessibility significantly improve results.'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function FeaturesSection() {
|
|||||||
color="#ef4444aa"
|
color="#ef4444aa"
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
// multiline={false}
|
// multiline={false}
|
||||||
isView
|
// isView
|
||||||
>
|
>
|
||||||
{cmd}
|
{cmd}
|
||||||
</Highlighter>
|
</Highlighter>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
||||||
import { PageAgent } from 'page-agent'
|
import type { PageAgent as PageAgentType } from 'page-agent'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Link, useSearchParams } from 'wouter'
|
import { Link, useSearchParams } from 'wouter'
|
||||||
|
|
||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
} from '../../constants'
|
} from '../../constants'
|
||||||
import { useLanguage } from '../../i18n/context'
|
import { useLanguage } from '../../i18n/context'
|
||||||
|
|
||||||
|
let pageAgentModule: Promise<typeof import('page-agent')> | null = null
|
||||||
|
|
||||||
function getInjection(useCN?: boolean) {
|
function getInjection(useCN?: boolean) {
|
||||||
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
|
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
|
||||||
|
|
||||||
@@ -55,13 +57,20 @@ export default function HeroSection() {
|
|||||||
const [activeTab, setActiveTab] = useState<'try' | 'other'>(isOther ? 'other' : 'try')
|
const [activeTab, setActiveTab] = useState<'try' | 'other'>(isOther ? 'other' : 'try')
|
||||||
const [cdnSource, setCdnSource] = useState<'international' | 'china'>('international')
|
const [cdnSource, setCdnSource] = useState<'international' | 'china'>('international')
|
||||||
|
|
||||||
const handleExecute = async () => {
|
const [ready, setReady] = useState(false)
|
||||||
if (!task.trim()) return
|
useEffect(() => {
|
||||||
|
pageAgentModule ??= import('page-agent')
|
||||||
|
pageAgentModule.then(() => setReady(true))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleExecute = async () => {
|
||||||
|
if (!task.trim() || !ready || !pageAgentModule) return
|
||||||
|
|
||||||
|
const { PageAgent } = await pageAgentModule
|
||||||
const win = window as any
|
const win = window as any
|
||||||
|
|
||||||
if (!win.pageAgent || win.pageAgent.disposed) {
|
if (!win.pageAgent || win.pageAgent.disposed) {
|
||||||
win.pageAgent = new PageAgent({
|
win.pageAgent = new (PageAgent as typeof PageAgentType)({
|
||||||
interactiveBlacklist: [document.getElementById('root')!],
|
interactiveBlacklist: [document.getElementById('root')!],
|
||||||
language: language,
|
language: language,
|
||||||
|
|
||||||
@@ -208,10 +217,21 @@ export default function HeroSection() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleExecute}
|
onClick={handleExecute}
|
||||||
|
disabled={!ready}
|
||||||
className="absolute right-2 top-2 px-5 py-1.5 bg-linear-to-r from-blue-600 to-purple-600 text-white font-medium rounded-md hover:shadow-md transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none text-sm"
|
className="absolute right-2 top-2 px-5 py-1.5 bg-linear-to-r from-blue-600 to-purple-600 text-white font-medium rounded-md hover:shadow-md transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none text-sm"
|
||||||
data-page-agent-not-interactive
|
data-page-agent-not-interactive
|
||||||
>
|
>
|
||||||
{isZh ? '执行' : 'Run'}
|
{ready ? (
|
||||||
|
isZh ? (
|
||||||
|
'执行'
|
||||||
|
) : (
|
||||||
|
'Run'
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span className="animate-pulse">
|
||||||
|
{isZh ? '准备中...' : 'Preparing...'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 text-left">
|
<p className="text-xs text-gray-500 dark:text-gray-400 text-left">
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ export default function ScenariosSection() {
|
|||||||
const { isZh } = useLanguage()
|
const { isZh } = useLanguage()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="px-6 py-16" aria-labelledby="scenarios-heading">
|
<section
|
||||||
|
className="px-6 py-16 bg-linear-to-b from-blue-100 to-purple-100 dark:from-blue-950/40 dark:to-gray-800"
|
||||||
|
aria-labelledby="scenarios-heading"
|
||||||
|
>
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<BlurFade inView>
|
<BlurFade inView>
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Suspense, lazy } from 'react'
|
||||||
|
|
||||||
|
import HeroSection from './HeroSection'
|
||||||
|
|
||||||
|
const FeaturesSection = lazy(() => import('./FeaturesSection'))
|
||||||
|
const ScenariosSection = lazy(() => import('./ScenariosSection'))
|
||||||
|
const OneMoreThingSection = lazy(() => import('./OneMoreThingSection'))
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<HeroSection />
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex items-center justify-center gap-3 py-20 text-gray-400">
|
||||||
|
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FeaturesSection />
|
||||||
|
<ScenariosSection />
|
||||||
|
<OneMoreThingSection />
|
||||||
|
</Suspense>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Suspense, useLayoutEffect } from 'react'
|
import { Suspense, lazy, useEffect, useLayoutEffect } from 'react'
|
||||||
import { Route, Switch, useLocation } from 'wouter'
|
import { Route, Switch, useLocation } from 'wouter'
|
||||||
|
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import Header from './components/Header'
|
import Header from './components/Header'
|
||||||
import HomePage from './pages/Home'
|
import HomePage from './pages/home'
|
||||||
import DocsPages from './pages/docs/index'
|
|
||||||
|
const docsImport = () => import('./pages/docs')
|
||||||
|
const DocsPages = lazy(docsImport)
|
||||||
|
|
||||||
function ScrollToTop() {
|
function ScrollToTop() {
|
||||||
const [pathname] = useLocation()
|
const [pathname] = useLocation()
|
||||||
@@ -15,6 +17,13 @@ function ScrollToTop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
|
useEffect(() => {
|
||||||
|
const schedule = globalThis.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
|
||||||
|
const cancel = globalThis.cancelIdleCallback ?? clearTimeout
|
||||||
|
const id = schedule(() => docsImport())
|
||||||
|
return () => cancel(id)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex min-h-screen flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
@@ -32,7 +41,16 @@ export default function Router() {
|
|||||||
|
|
||||||
<Route path="/docs" nest>
|
<Route path="/docs" nest>
|
||||||
<div className="flex-1 bg-white dark:bg-gray-900">
|
<div className="flex-1 bg-white dark:bg-gray-900">
|
||||||
<DocsPages />
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex items-center justify-center gap-3 py-20 text-gray-400">
|
||||||
|
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DocsPages />
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ const SPA_ROUTES = [
|
|||||||
'docs/features/custom-instructions',
|
'docs/features/custom-instructions',
|
||||||
'docs/features/models',
|
'docs/features/models',
|
||||||
'docs/features/chrome-extension',
|
'docs/features/chrome-extension',
|
||||||
|
'docs/features/third-party-agent',
|
||||||
'docs/advanced/page-agent',
|
'docs/advanced/page-agent',
|
||||||
'docs/advanced/page-agent-core',
|
'docs/advanced/page-agent-core',
|
||||||
|
'docs/advanced/page-controller',
|
||||||
'docs/advanced/custom-ui',
|
'docs/advanced/custom-ui',
|
||||||
'docs/integration/security-permissions',
|
'docs/advanced/security-permissions',
|
||||||
'docs/integration/best-practices',
|
|
||||||
'docs/integration/third-party-agent',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
function spaRoutes() {
|
function spaRoutes() {
|
||||||
@@ -64,6 +64,11 @@ export default defineConfig(({ mode }) => ({
|
|||||||
if (message.code === 'EVAL') return
|
if (message.code === 'EVAL') return
|
||||||
handler(message)
|
handler(message)
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ['react', 'react-dom', 'wouter'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user