Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62e24a3050 | |||
| 4f6249a252 | |||
| 62519416bb | |||
| a1e70e663d | |||
| 338e317be1 | |||
| 9a66d5205f | |||
| 7d45bf6598 | |||
| 78ee72eea4 | |||
| 3b71c535b1 | |||
| 6615f7b300 | |||
| 38caae5e23 | |||
| 1751ba325d | |||
| 066b045822 | |||
| 3b21c42cdb | |||
| 8b1e91d665 | |||
| aa5909264a | |||
| b6232d4e21 |
@@ -9,9 +9,10 @@ This is a **monorepo** with npm workspaces:
|
||||
|
||||
Internal packages:
|
||||
|
||||
- **CDN** (`packages/cdn/`) - IIFE builds for script tag usage (npm: `@page-agent/cdn`)
|
||||
- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model
|
||||
- **Page Controller** (`packages/page-controller/`) - DOM operations, independent of LLM
|
||||
- **UI** (`packages/ui/`) - Panel, SimulatorMask, i18n. Decoupled from PageAgent
|
||||
- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM
|
||||
- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent
|
||||
|
||||
## Development Commands
|
||||
|
||||
@@ -31,6 +32,7 @@ Simple monorepo solution: TypeScript references + Vite aliases. Update tsconfig
|
||||
```
|
||||
packages/
|
||||
├── page-agent/ # npm: "page-agent" ⭐ MAIN
|
||||
├── cdn/ # npm: "@page-agent/cdn" (IIFE builds)
|
||||
├── website/ # @page-agent/website (private)
|
||||
├── llms/ # @page-agent/llms
|
||||
├── page-controller/ # @page-agent/page-controller
|
||||
@@ -43,8 +45,8 @@ packages/
|
||||
|
||||
- **Page Agent**: Core lib. Imports from `@page-agent/llms`, `@page-agent/page-controller`, `@page-agent/ui`
|
||||
- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent
|
||||
- **UI**: Panel, Mask, i18n. No dependency on page-agent
|
||||
- **Page Controller**: Pure DOM operations. No LLM or UI dependency
|
||||
- **UI**: Panel and i18n. No dependency on page-agent
|
||||
- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config
|
||||
|
||||
### PageController ↔ PageAgent Communication
|
||||
|
||||
@@ -69,41 +71,43 @@ const pageInfo = await this.pageController.getPageInfo()
|
||||
3. **LLM Processing**: AI returns action plans (page-agent)
|
||||
4. **Indexed Operations**: PageAgent calls PageController by element index
|
||||
|
||||
### CDN Auto-Injection
|
||||
### CDN Builds (`packages/cdn/`)
|
||||
|
||||
Library auto-initializes via script tag:
|
||||
Two IIFE builds for script tag usage:
|
||||
|
||||
```html
|
||||
<script src="page-agent.js?model=gpt-4"></script>
|
||||
```
|
||||
| Build | File | Description |
|
||||
| ----- | -------------------- | -------------------------------- |
|
||||
| Demo | `page-agent.demo.js` | Auto-init with built-in test API |
|
||||
| Full | `page-agent.js` | Exposes `PageAgent` class only |
|
||||
|
||||
Query params configure `PageAgentConfig` in `src/umd.ts`.
|
||||
Demo build supports query params (e.g., `?model=gpt-4&lang=en-US`).
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
### Page Agent (`packages/page-agent/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/PageAgent.ts` | ⭐ Main AI agent class |
|
||||
| `src/umd.ts` | CDN/UMD entry with auto-init |
|
||||
| `src/tools/` | Tool definitions calling PageController |
|
||||
| File | Description |
|
||||
| ------------------ | --------------------------------------- |
|
||||
| `src/PageAgent.ts` | ⭐ Main AI agent class |
|
||||
| `src/umd.ts` | CDN/UMD entry with auto-init |
|
||||
| `src/tools/` | Tool definitions calling PageController |
|
||||
|
||||
### LLMs (`packages/llms/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/index.ts` | ⭐ LLM class with retry logic |
|
||||
| `src/types.ts` | MacroToolInput, AgentBrain, LLMConfig |
|
||||
| `src/OpenAILenientClient.ts` | OpenAI-compatible client |
|
||||
| File | Description |
|
||||
| ---------------------------- | ------------------------------------- |
|
||||
| `src/index.ts` | ⭐ LLM class with retry logic |
|
||||
| `src/types.ts` | MacroToolInput, AgentBrain, LLMConfig |
|
||||
| `src/OpenAILenientClient.ts` | OpenAI-compatible client |
|
||||
|
||||
### Page Controller (`packages/page-controller/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/PageController.ts` | ⭐ Main controller class |
|
||||
| `src/actions.ts` | Element interactions (click, input, scroll) |
|
||||
| `src/dom/dom_tree/index.js` | Core DOM extraction engine |
|
||||
| File | Description |
|
||||
| --------------------------- | ---------------------------------------------------------- |
|
||||
| `src/PageController.ts` | ⭐ Main controller class with optional mask support |
|
||||
| `src/SimulatorMask.ts` | Visual overlay blocking user interaction during automation |
|
||||
| `src/actions.ts` | Element interactions (click, input, scroll) |
|
||||
| `src/dom/dom_tree/index.js` | Core DOM extraction engine |
|
||||
|
||||
## Adding New Features
|
||||
|
||||
|
||||
+38
-25
@@ -28,7 +28,22 @@
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### NPM 安装
|
||||
### 快速体验 (Demo CDN)
|
||||
|
||||
最快的体验方式:
|
||||
|
||||
```html
|
||||
<script src="https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.demo.js" crossorigin="true"></script>
|
||||
```
|
||||
|
||||
> ⚠️ **仅用于技术评估。** Demo 模型有速率限制和使用限制,生产环境请使用 NPM 方式。
|
||||
|
||||
| 位置 | URL |
|
||||
| ------ | --------------------------------------------------------------------------------------- |
|
||||
| 全球 | https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.demo.js |
|
||||
| 中国 | https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.demo.js |
|
||||
|
||||
### NPM 安装(推荐)
|
||||
|
||||
```bash
|
||||
npm install page-agent
|
||||
@@ -37,40 +52,37 @@ npm install page-agent
|
||||
```javascript
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
// 测试接口
|
||||
// @note: 限流,限制 prompt 内容,限制来源,随时变更,请替换成你自己的
|
||||
// @note: 使用 DeepSeek-chat(3.2) 官方版本,使用协议和隐私策略见 DeepSeek 网站
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
const agent = new PageAgent({
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
language: 'zh-CN',
|
||||
})
|
||||
|
||||
await agent.execute('点击登录按钮')
|
||||
```
|
||||
|
||||
### CDN 集成
|
||||
### CDN 引入
|
||||
|
||||
Fastest way to try PageAgent is to include it via CDN. Demo model will be used by default.
|
||||
|
||||
| Location | URL |
|
||||
| -------- | ----------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@latest/dist/umd/page-agent.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/latest/files/dist/umd/page-agent.js |
|
||||
适用于无法使用 NPM 的环境,用法与 NPM 一致:
|
||||
|
||||
```html
|
||||
<script
|
||||
src="https://registry.npmmirror.com/page-agent/latest/files/dist/umd/page-agent.js"
|
||||
crossorigin="true"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
<script src="https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.js" crossorigin="true"></script>
|
||||
<script>
|
||||
const agent = new PageAgent({
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
})
|
||||
await agent.execute('点击登录按钮')
|
||||
</script>
|
||||
```
|
||||
|
||||
| 位置 | URL |
|
||||
| ------ | ----------------------------------------------------------------------------------- |
|
||||
| 全球 | https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.js |
|
||||
| 中国 | https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.js |
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
PageAgent adopts a simplified monorepo structure:
|
||||
@@ -79,8 +91,9 @@ PageAgent adopts a simplified monorepo structure:
|
||||
packages/
|
||||
├── page-agent/ # AI agent (npm: page-agent)
|
||||
├── llms/ # LLM 客户端 (npm: @page-agent/llms)
|
||||
├── page-controller/ # DOM 操作 (npm: @page-agent/page-controller)
|
||||
├── ui/ # 面板 & 蒙层 & 模拟鼠标 (npm: @page-agent/ui)
|
||||
├── page-controller/ # DOM 操作 & 蒙层 & 模拟鼠标 (npm: @page-agent/page-controller)
|
||||
├── ui/ # 面板 & i18n (npm: @page-agent/ui)
|
||||
├── cdn/ # CDN IIFE builds (npm: @page-agent/cdn)
|
||||
└── website/ # 文档站点
|
||||
```
|
||||
|
||||
|
||||
@@ -28,7 +28,22 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### NPM Installation
|
||||
### Quick Try (Demo CDN)
|
||||
|
||||
Fastest way to try PageAgent:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.demo.js" crossorigin="true"></script>
|
||||
```
|
||||
|
||||
> ⚠️ **For technical evaluation only.** Demo model has rate limits and usage restrictions. Use NPM for production.
|
||||
|
||||
| Location | URL |
|
||||
| -------- | --------------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.demo.js |
|
||||
|
||||
### NPM Installation (Recommended)
|
||||
|
||||
```bash
|
||||
npm install page-agent
|
||||
@@ -37,40 +52,37 @@ npm install page-agent
|
||||
```javascript
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
// test server
|
||||
// @note: rate limit. prompt limit. Origin limit. May change anytime. Use your own llm!
|
||||
// @note Using official DeepSeek-chat(3.2). Go to DeepSeek website for privacy policy.
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
const agent = new PageAgent({
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
language: 'en-US',
|
||||
})
|
||||
|
||||
await agent.execute('Click the login button')
|
||||
```
|
||||
|
||||
### CDN Integration
|
||||
### CDN Build
|
||||
|
||||
Fastest way to try PageAgent is to include it via CDN. Demo model will be used by default.
|
||||
|
||||
| Location | URL |
|
||||
| -------- | ----------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@latest/dist/umd/page-agent.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/latest/files/dist/umd/page-agent.js |
|
||||
For environments where NPM is not available. Usage is identical to NPM:
|
||||
|
||||
```html
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/page-agent@latest/dist/umd/page-agent.js"
|
||||
crossorigin="true"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.js" crossorigin="true"></script>
|
||||
<script>
|
||||
const agent = new PageAgent({
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
})
|
||||
await agent.execute('Click the login button')
|
||||
</script>
|
||||
```
|
||||
|
||||
| Location | URL |
|
||||
| -------- | ----------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.js |
|
||||
| China | https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.js |
|
||||
|
||||
## 🏗️ Structure
|
||||
|
||||
PageAgent adopts a simplified monorepo structure:
|
||||
@@ -79,8 +91,9 @@ PageAgent adopts a simplified monorepo structure:
|
||||
packages/
|
||||
├── page-agent/ # AI agent (npm: page-agent)
|
||||
├── llms/ # LLM client (npm: @page-agent/llms)
|
||||
├── page-controller/ # DOM operations (npm: @page-agent/page-controller)
|
||||
├── ui/ # Panel & Mask & Mouse Animation (npm: @page-agent/ui)
|
||||
├── page-controller/ # DOM operations & Visual Mask (npm: @page-agent/page-controller)
|
||||
├── ui/ # Panel & i18n (npm: @page-agent/ui)
|
||||
├── cdn/ # CDN IIFE builds (npm: @page-agent/cdn)
|
||||
└── website/ # Demo & Documentation site
|
||||
```
|
||||
|
||||
|
||||
Generated
+27
-14
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "root",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
"packages/ui",
|
||||
"packages/llms",
|
||||
"packages/page-agent",
|
||||
"packages/cdn",
|
||||
"packages/website"
|
||||
],
|
||||
"devDependencies": {
|
||||
@@ -1597,6 +1598,10 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@page-agent/cdn": {
|
||||
"resolved": "packages/cdn",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@page-agent/llms": {
|
||||
"resolved": "packages/llms",
|
||||
"link": true
|
||||
@@ -8140,9 +8145,17 @@
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/cdn": {
|
||||
"name": "@page-agent/cdn",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"page-agent": "0.1.0"
|
||||
}
|
||||
},
|
||||
"packages/llms": {
|
||||
"name": "@page-agent/llms",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
@@ -8150,32 +8163,32 @@
|
||||
}
|
||||
},
|
||||
"packages/page-agent": {
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/llms": "0.0.21",
|
||||
"@page-agent/page-controller": "0.0.21",
|
||||
"@page-agent/ui": "0.0.21",
|
||||
"@page-agent/llms": "0.1.0",
|
||||
"@page-agent/page-controller": "0.1.0",
|
||||
"@page-agent/ui": "0.1.0",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
},
|
||||
"packages/page-controller": {
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "0.0.21",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@page-agent/ui",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
}
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@page-agent/ui",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/website": {
|
||||
"name": "@page-agent/website",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
|
||||
+2
-1
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
"packages/ui",
|
||||
"packages/llms",
|
||||
"packages/page-agent",
|
||||
"packages/cdn",
|
||||
"packages/website"
|
||||
],
|
||||
"description": "AI-powered UI agent for web applications",
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@page-agent/cdn",
|
||||
"private": false,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"description": "CDN builds for page-agent - IIFE bundles for script tag usage",
|
||||
"author": "Simon<gaomeng1900>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alibaba/page-agent.git"
|
||||
},
|
||||
"homepage": "https://alibaba.github.io/page-agent/",
|
||||
"scripts": {
|
||||
"build": "vite build && vite build --mode demo"
|
||||
},
|
||||
"dependencies": {
|
||||
"page-agent": "0.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Demo CDN build for page-agent
|
||||
* Auto-initializes with built-in demo API for testing
|
||||
*
|
||||
* Usage:
|
||||
* <script src="https://unpkg.com/@page-agent/cdn/dist/page-agent.demo.js"></script>
|
||||
*/
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
// Clean up existing instances to prevent multiple injections from bookmarklet
|
||||
if (window.pageAgent) {
|
||||
window.pageAgent.dispose()
|
||||
}
|
||||
|
||||
// Mount to global window object
|
||||
window.PageAgent = PageAgent
|
||||
|
||||
// Export for IIFE
|
||||
export { PageAgent }
|
||||
|
||||
console.log('🚀 page-agent.js loaded!')
|
||||
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
// Demo warning
|
||||
console.log(
|
||||
'%c⚠️ DEMO MODE %c\n' +
|
||||
'This build uses a shared testing LLM with rate limits.\n' +
|
||||
'For production, use page-agent.js with your own API key:\n' +
|
||||
'https://alibaba.github.io/page-agent/#/docs/features/models',
|
||||
'background: #f59e0b; color: #000; font-size: 14px; font-weight: bold; padding: 4px 8px; border-radius: 4px;',
|
||||
'color: #f59e0b; font-size: 12px;'
|
||||
)
|
||||
|
||||
// in case document.x is not ready yet
|
||||
setTimeout(() => {
|
||||
const currentScript = document.currentScript
|
||||
|
||||
if (currentScript) {
|
||||
console.log('🚀 page-agent.js detected current script:', currentScript.src)
|
||||
const url = new URL(currentScript.src)
|
||||
const model = url.searchParams.get('model') || DEMO_MODEL
|
||||
const baseURL = url.searchParams.get('baseURL') || DEMO_BASE_URL
|
||||
const apiKey = url.searchParams.get('apiKey') || DEMO_API_KEY
|
||||
const language = url.searchParams.get('lang') || 'zh-CN'
|
||||
const config = { model, baseURL, apiKey, language }
|
||||
window.pageAgent = new PageAgent(config)
|
||||
} else {
|
||||
console.log('🚀 page-agent.js no current script detected, using default demo config')
|
||||
const config = {
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
}
|
||||
window.pageAgent = new PageAgent(config)
|
||||
}
|
||||
|
||||
console.log('🚀 page-agent.js initialized with config:', window.pageAgent.config)
|
||||
|
||||
window.pageAgent.panel.show()
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Full CDN build for page-agent
|
||||
* Exposes PageAgent class for manual instantiation and configuration
|
||||
*
|
||||
* Usage:
|
||||
* <script src="https://unpkg.com/@page-agent/cdn/dist/page-agent.js"></script>
|
||||
* <script>
|
||||
* const agent = new PageAgent({ model: 'gpt-4o', apiKey: 'your-key' })
|
||||
* agent.panel.show()
|
||||
* </script>
|
||||
*/
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
export { PageAgent }
|
||||
@@ -0,0 +1,42 @@
|
||||
// @ts-nocheck
|
||||
import { dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { defineConfig } from 'vite'
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
/**
|
||||
* CDN IIFE builds for page-agent
|
||||
*
|
||||
* Build targets (via --mode):
|
||||
* - demo: page-agent.demo.js with auto-init and built-in API
|
||||
* - full (default): page-agent.js exposes PageAgent class only
|
||||
*/
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isDemo = mode === 'demo'
|
||||
const entry = isDemo ? resolve(__dirname, 'src/demo.js') : resolve(__dirname, 'src/full.js')
|
||||
const fileName = isDemo ? 'page-agent.demo' : 'page-agent'
|
||||
|
||||
return {
|
||||
plugins: [cssInjectedByJsPlugin({ relativeCSSInjection: true })],
|
||||
publicDir: false,
|
||||
esbuild: {
|
||||
keepNames: true,
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry,
|
||||
name: 'PageAgent',
|
||||
fileName: () => `${fileName}.js`,
|
||||
formats: ['iife'],
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist'),
|
||||
emptyOutDir: !isDemo, // only empty on first build (full)
|
||||
minify: false,
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/llms",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-llms.js",
|
||||
"module": "./dist/lib/page-agent-llms.js",
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
// Dev environment: use .env config if available, otherwise fallback to testing api
|
||||
export const DEFAULT_MODEL_NAME: string =
|
||||
import.meta.env.DEV && import.meta.env.LLM_MODEL_NAME
|
||||
? import.meta.env.LLM_MODEL_NAME
|
||||
: 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
export const DEFAULT_API_KEY: string =
|
||||
import.meta.env.DEV && import.meta.env.LLM_API_KEY
|
||||
? import.meta.env.LLM_API_KEY
|
||||
: 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
export const DEFAULT_BASE_URL: string =
|
||||
import.meta.env.DEV && import.meta.env.LLM_BASE_URL
|
||||
? import.meta.env.LLM_BASE_URL
|
||||
: 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
|
||||
// internal
|
||||
|
||||
// Internal constants
|
||||
export const LLM_MAX_RETRIES = 2
|
||||
export const DEFAULT_TEMPERATURE = 0.7 // higher randomness helps auto-recovery
|
||||
|
||||
+12
-10
@@ -1,21 +1,23 @@
|
||||
import { OpenAIClient } from './OpenAIClient'
|
||||
import {
|
||||
DEFAULT_API_KEY,
|
||||
DEFAULT_BASE_URL,
|
||||
DEFAULT_MODEL_NAME,
|
||||
DEFAULT_TEMPERATURE,
|
||||
LLM_MAX_RETRIES,
|
||||
} from './constants'
|
||||
import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants'
|
||||
import { InvokeError } from './errors'
|
||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||
|
||||
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
|
||||
|
||||
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
|
||||
// Runtime validation as defensive programming (types already guarantee these)
|
||||
if (!config.baseURL || !config.apiKey || !config.model) {
|
||||
throw new Error(
|
||||
'[PageAgent] LLM configuration required. Please provide: baseURL, apiKey, model. ' +
|
||||
'See: https://alibaba.github.io/page-agent/#/docs/features/models'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
baseURL: config.baseURL ?? DEFAULT_BASE_URL,
|
||||
apiKey: config.apiKey ?? DEFAULT_API_KEY,
|
||||
model: config.model ?? DEFAULT_MODEL_NAME,
|
||||
baseURL: config.baseURL,
|
||||
apiKey: config.apiKey,
|
||||
model: config.model,
|
||||
temperature: config.temperature ?? DEFAULT_TEMPERATURE,
|
||||
maxRetries: config.maxRetries ?? LLM_MAX_RETRIES,
|
||||
customFetch: (config.customFetch ?? fetch).bind(globalThis), // fetch will be illegal unless bound
|
||||
|
||||
@@ -87,9 +87,9 @@ export interface InvokeResult<TResult = unknown> {
|
||||
* LLM configuration
|
||||
*/
|
||||
export interface LLMConfig {
|
||||
baseURL?: string
|
||||
apiKey?: string
|
||||
model?: string
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
|
||||
temperature?: number
|
||||
maxRetries?: number
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "page-agent",
|
||||
"private": false,
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent.js",
|
||||
"module": "./dist/esm/page-agent.js",
|
||||
@@ -46,8 +46,8 @@
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5",
|
||||
"@page-agent/llms": "0.0.21",
|
||||
"@page-agent/page-controller": "0.0.21",
|
||||
"@page-agent/ui": "0.0.21"
|
||||
"@page-agent/llms": "0.1.0",
|
||||
"@page-agent/page-controller": "0.1.0",
|
||||
"@page-agent/ui": "0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { LLM, type Tool } from '@page-agent/llms'
|
||||
import { PageController } from '@page-agent/page-controller'
|
||||
import { Panel, SimulatorMask } from '@page-agent/ui'
|
||||
import { Panel } from '@page-agent/ui'
|
||||
import chalk from 'chalk'
|
||||
import zod from 'zod'
|
||||
|
||||
@@ -92,12 +92,10 @@ export class PageAgent extends EventTarget {
|
||||
/** PageController for DOM operations */
|
||||
pageController: PageController
|
||||
|
||||
/** Fullscreen mask */
|
||||
mask = new SimulatorMask()
|
||||
/** History records */
|
||||
history: AgentHistory[] = []
|
||||
|
||||
constructor(config: PageAgentConfig = {}) {
|
||||
constructor(config: PageAgentConfig) {
|
||||
super()
|
||||
|
||||
this.config = config
|
||||
@@ -114,8 +112,11 @@ export class PageAgent extends EventTarget {
|
||||
})
|
||||
this.tools = new Map(tools)
|
||||
|
||||
// Initialize PageController with config
|
||||
this.pageController = new PageController(this.config)
|
||||
// Initialize PageController with config (mask enabled by default)
|
||||
this.pageController = new PageController({
|
||||
...this.config,
|
||||
enableMask: this.config.enableMask ?? true,
|
||||
})
|
||||
|
||||
// Listen to LLM events
|
||||
this.#llmRetryListener = (e) => {
|
||||
@@ -162,7 +163,7 @@ export class PageAgent extends EventTarget {
|
||||
await onBeforeTask.call(this)
|
||||
|
||||
// Show mask and panel
|
||||
this.mask.show()
|
||||
this.pageController.showMask()
|
||||
|
||||
this.panel.show()
|
||||
this.panel.reset()
|
||||
@@ -485,7 +486,7 @@ export class PageAgent extends EventTarget {
|
||||
// Task completed
|
||||
this.panel.update({ type: 'completed' })
|
||||
|
||||
this.mask.hide()
|
||||
this.pageController.hideMask()
|
||||
|
||||
this.#abortController.abort()
|
||||
}
|
||||
@@ -496,9 +497,7 @@ export class PageAgent extends EventTarget {
|
||||
const pi = await this.pageController.getPageInfo()
|
||||
const viewportExpansion = await this.pageController.getViewportExpansion()
|
||||
|
||||
this.mask.wrapper.style.pointerEvents = 'none'
|
||||
await this.pageController.updateTree()
|
||||
this.mask.wrapper.style.pointerEvents = 'auto'
|
||||
|
||||
let simplifiedHTML = await this.pageController.getSimplifiedHTML()
|
||||
|
||||
@@ -545,7 +544,6 @@ export class PageAgent extends EventTarget {
|
||||
this.disposed = true
|
||||
this.pageController.dispose()
|
||||
this.panel.dispose()
|
||||
this.mask.dispose()
|
||||
this.history = []
|
||||
this.#abortController.abort(reason ?? 'PageAgent disposed')
|
||||
|
||||
|
||||
@@ -35,7 +35,12 @@ setTimeout(() => {
|
||||
window.pageAgent = new PageAgent(config)
|
||||
} else {
|
||||
console.log('🚀 page-agent.js no current script detected, using default demo config')
|
||||
window.pageAgent = new PageAgent()
|
||||
const config: PageAgentConfig = {
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
}
|
||||
window.pageAgent = new PageAgent(config)
|
||||
}
|
||||
|
||||
console.log('🚀 page-agent.js initialized with config:', window.pageAgent.config)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-controller.js",
|
||||
"module": "./dist/lib/page-controller.js",
|
||||
@@ -34,5 +34,8 @@
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "node -e \"const fs=require('fs');['LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,18 @@ import { VIEWPORT_EXPANSION } from './constants'
|
||||
import * as dom from './dom'
|
||||
import type { FlatDomTree, InteractiveElementDomNode } from './dom/dom_tree/type'
|
||||
import { getPageInfo } from './dom/getPageInfo'
|
||||
import { SimulatorMask } from './mask/SimulatorMask'
|
||||
import { patchReact } from './patches/react'
|
||||
|
||||
export { SimulatorMask }
|
||||
|
||||
/**
|
||||
* Configuration for PageController
|
||||
*/
|
||||
export interface PageControllerConfig extends dom.DomConfig {
|
||||
viewportExpansion?: number
|
||||
/** Enable visual mask overlay during operations (default: false) */
|
||||
enableMask?: boolean
|
||||
}
|
||||
|
||||
interface ActionResult {
|
||||
@@ -64,12 +69,19 @@ export class PageController extends EventTarget {
|
||||
/** last time the tree was updated */
|
||||
private lastTimeUpdate = 0
|
||||
|
||||
/** Visual mask overlay for blocking user interaction during automation */
|
||||
private mask: SimulatorMask | null = null
|
||||
|
||||
constructor(config: PageControllerConfig = {}) {
|
||||
super()
|
||||
|
||||
this.config = config
|
||||
|
||||
patchReact(this)
|
||||
|
||||
if (config.enableMask) {
|
||||
this.mask = new SimulatorMask()
|
||||
}
|
||||
}
|
||||
|
||||
// ======= State Queries =======
|
||||
@@ -136,12 +148,18 @@ export class PageController extends EventTarget {
|
||||
/**
|
||||
* Update DOM tree, returns simplified HTML for LLM.
|
||||
* This is the main method to refresh the page state.
|
||||
* Automatically bypasses mask during DOM extraction if enabled.
|
||||
*/
|
||||
async updateTree(): Promise<string> {
|
||||
this.dispatchEvent(new Event('beforeUpdate'))
|
||||
|
||||
this.lastTimeUpdate = Date.now()
|
||||
|
||||
// Temporarily bypass mask to allow DOM extraction
|
||||
if (this.mask) {
|
||||
this.mask.wrapper.style.pointerEvents = 'none'
|
||||
}
|
||||
|
||||
dom.cleanUpHighlights()
|
||||
|
||||
const blacklist = [
|
||||
@@ -162,6 +180,11 @@ export class PageController extends EventTarget {
|
||||
this.elementTextMap.clear()
|
||||
this.elementTextMap = dom.getElementTextMap(this.simplifiedHTML)
|
||||
|
||||
// Restore mask blocking
|
||||
if (this.mask) {
|
||||
this.mask.wrapper.style.pointerEvents = 'auto'
|
||||
}
|
||||
|
||||
this.dispatchEvent(new Event('afterUpdate'))
|
||||
|
||||
return this.simplifiedHTML
|
||||
@@ -326,6 +349,24 @@ export class PageController extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
// ======= Mask Operations =======
|
||||
|
||||
/**
|
||||
* Show the visual mask overlay.
|
||||
* Only works if enableMask was set to true in config.
|
||||
*/
|
||||
showMask(): void {
|
||||
this.mask?.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the visual mask overlay.
|
||||
* Only works if enableMask was set to true in config.
|
||||
*/
|
||||
hideMask(): void {
|
||||
this.mask?.hide()
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose and clean up resources
|
||||
*/
|
||||
@@ -335,5 +376,7 @@ export class PageController extends EventTarget {
|
||||
this.selectorMap.clear()
|
||||
this.elementTextMap.clear()
|
||||
this.simplifiedHTML = '<EMPTY>'
|
||||
this.mask?.dispose()
|
||||
this.mask = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* @edit scrollable element detection
|
||||
* @edit add `data-browser-use-ignore` attribute
|
||||
* @edit improve `sampleRect`, filter out rects with 0 area
|
||||
* @edit exclude aria-hidden elements
|
||||
*/
|
||||
|
||||
export default (
|
||||
@@ -1446,7 +1447,14 @@ export default (
|
||||
* @edit add `data-browser-use-ignore` attribute
|
||||
*/
|
||||
if (node.dataset?.browserUseIgnore === 'true') {
|
||||
return true // Skip this node and its children
|
||||
return null // Skip this node and its children
|
||||
}
|
||||
|
||||
/**
|
||||
* @edit exclude aria-hidden elements
|
||||
*/
|
||||
if (node.getAttribute && node.getAttribute('aria-hidden') === 'true') {
|
||||
return null // Skip this node and its children
|
||||
}
|
||||
|
||||
// Special handling for root node (body)
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export default defineConfig({
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||
rollupOptions: {
|
||||
external: ['@page-agent/*'],
|
||||
external: ['@page-agent/*', 'ai-motion'],
|
||||
},
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/ui",
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-ui.js",
|
||||
"module": "./dist/lib/page-agent-ui.js",
|
||||
@@ -15,7 +15,7 @@
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"description": "UI components for page-agent - Panel, SimulatorMask, and i18n",
|
||||
"description": "UI components for page-agent - Panel and i18n",
|
||||
"keywords": [
|
||||
"page-agent",
|
||||
"ui",
|
||||
@@ -34,8 +34,5 @@
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "node -e \"const fs=require('fs');['LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type Step, UIState } from './UIState'
|
||||
import { I18n, type SupportedLanguage } from './i18n'
|
||||
import { truncate } from './utils'
|
||||
import { escapeHtml, truncate } from './utils'
|
||||
|
||||
import styles from './Panel.module.css'
|
||||
|
||||
@@ -590,10 +590,7 @@ export class Panel {
|
||||
|
||||
#updateHistory(): void {
|
||||
const steps = this.#state.getAllSteps()
|
||||
|
||||
this.#historySection.innerHTML = steps.map((step) => this.#createHistoryItem(step)).join('')
|
||||
|
||||
// Scroll to bottom to show latest records
|
||||
this.#scrollToBottom()
|
||||
}
|
||||
|
||||
@@ -660,7 +657,7 @@ export class Panel {
|
||||
<div class="${styles.historyItem} ${typeClass}">
|
||||
<div class="${styles.historyContent}">
|
||||
<span class="${styles.statusIcon}">${statusIcon}</span>
|
||||
<span>${step.displayText}</span>
|
||||
<span>${escapeHtml(step.displayText)}</span>
|
||||
</div>
|
||||
<div class="${styles.historyMeta}">
|
||||
${stepLabel}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { Panel, type PanelConfig, type PanelUpdate } from './Panel'
|
||||
export { SimulatorMask } from './SimulatorMask'
|
||||
export { UIState, type Step, type AgentStatus } from './UIState'
|
||||
export { I18n, type SupportedLanguage, type TranslationKey } from './i18n'
|
||||
|
||||
@@ -4,3 +4,15 @@ export function truncate(text: string, maxLength: number): string {
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML special characters to prevent XSS and rendering issues
|
||||
*/
|
||||
export function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default defineConfig({
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||
rollupOptions: {
|
||||
external: ['ai-motion'],
|
||||
external: [],
|
||||
},
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@page-agent/website",
|
||||
"private": true,
|
||||
"version": "0.0.21",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build:website": "vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -41,6 +41,7 @@ export default function Header() {
|
||||
className="hidden sm:block text-xs text-gray-600 dark:text-gray-300 py-0 font-normal overflow-visible"
|
||||
duration={600}
|
||||
animateOnHover={true}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{t('header.slogan')}
|
||||
</HyperText>
|
||||
@@ -84,6 +85,9 @@ export default function Header() {
|
||||
role="navigation"
|
||||
aria-label={t('header.nav_docs')}
|
||||
>
|
||||
<span className="text-xs font-mono text-gray-400 dark:text-gray-500 tabular-nums before:content-['v']">
|
||||
{import.meta.env.VERSION}
|
||||
</span>
|
||||
<Link
|
||||
href="/docs/introduction/overview"
|
||||
className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
export const CDN_URL = 'https://cdn.jsdelivr.net/npm/page-agent@latest/dist/umd/page-agent.js'
|
||||
export const CDN_CN_URL =
|
||||
'https://registry.npmmirror.com/page-agent/latest/files/dist/umd/page-agent.js'
|
||||
// Demo build (auto-init with demo LLM, for quick testing)
|
||||
export const CDN_DEMO_URL = 'https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.demo.js'
|
||||
export const CDN_DEMO_CN_URL =
|
||||
'https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.demo.js'
|
||||
|
||||
// Full build (exports PageAgent class, usage identical to npm)
|
||||
export const CDN_FULL_URL = 'https://cdn.jsdelivr.net/npm/@page-agent/cdn/dist/page-agent.js'
|
||||
export const CDN_FULL_CN_URL =
|
||||
'https://registry.npmmirror.com/@page-agent/cdn/latest/files/dist/page-agent.js'
|
||||
|
||||
// Demo LLM for website testing
|
||||
export const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
export const DEMO_BASE_URL =
|
||||
'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
export const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
Vendored
+4
@@ -1,5 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VERSION: string
|
||||
}
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
|
||||
@@ -12,10 +12,16 @@ import { Highlighter } from '../components/ui/highlighter'
|
||||
import { NeonGradientCard } from '../components/ui/neon-gradient-card'
|
||||
import { Particles } from '../components/ui/particles'
|
||||
import { SparklesText } from '../components/ui/sparkles-text'
|
||||
import { CDN_CN_URL, CDN_URL } from '../constants'
|
||||
import {
|
||||
CDN_DEMO_CN_URL,
|
||||
CDN_DEMO_URL,
|
||||
DEMO_API_KEY,
|
||||
DEMO_BASE_URL,
|
||||
DEMO_MODEL,
|
||||
} from '../constants'
|
||||
|
||||
function getInjection(useCN?: boolean) {
|
||||
const cdn = useCN ? CDN_CN_URL : CDN_URL
|
||||
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
|
||||
|
||||
const injection = encodeURI(
|
||||
`javascript:(function(){var s=document.createElement('script');s.src=\`${cdn}?t=\${Math.random()}\`;s.setAttribute('crossorigin', true);s.type="text/javascript";s.onload=()=>console.log('PageAgent script loaded!');document.body.appendChild(s);})();`
|
||||
@@ -73,13 +79,18 @@ export default function HomePage() {
|
||||
},
|
||||
},
|
||||
|
||||
// experimentalScriptExecutionTool: true,
|
||||
|
||||
// testing server
|
||||
// @note: rate limit. prompt limit.
|
||||
// model: DEMO_MODEL,
|
||||
// baseURL: DEMO_BASE_URL,
|
||||
// apiKey: DEMO_API_KEY,
|
||||
model:
|
||||
import.meta.env.DEV && import.meta.env.LLM_MODEL_NAME
|
||||
? import.meta.env.LLM_MODEL_NAME
|
||||
: DEMO_MODEL,
|
||||
baseURL:
|
||||
import.meta.env.DEV && import.meta.env.LLM_BASE_URL
|
||||
? import.meta.env.LLM_BASE_URL
|
||||
: DEMO_BASE_URL,
|
||||
apiKey:
|
||||
import.meta.env.DEV && import.meta.env.LLM_API_KEY
|
||||
? import.meta.env.LLM_API_KEY
|
||||
: DEMO_API_KEY,
|
||||
})
|
||||
win.pageAgent = pageAgent
|
||||
}
|
||||
@@ -95,7 +106,10 @@ export default function HomePage() {
|
||||
|
||||
{/* Hero Section */}
|
||||
<main id="main-content">
|
||||
<section className="relative px-6 py-22 lg:py-28" aria-labelledby="hero-heading">
|
||||
<section
|
||||
className="relative px-6 py-22 lg:py-28 overflow-hidden"
|
||||
aria-labelledby="hero-heading"
|
||||
>
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
{/* Background Pattern + Particles */}
|
||||
<div className="absolute inset-0 opacity-30" aria-hidden="true">
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
||||
items: [
|
||||
{ title: t('nav.configuration'), path: '/docs/integration/configuration' },
|
||||
{ title: t('nav.third_party_agent'), path: '/docs/integration/third-party-agent' },
|
||||
{ title: '🚧 ' + t('nav.cdn_setup'), path: '/docs/integration/cdn-setup' },
|
||||
{ title: t('nav.cdn_setup'), path: '/docs/integration/cdn-setup' },
|
||||
{
|
||||
title: '🚧 ' + t('nav.security_permissions'),
|
||||
path: '/docs/integration/security-permissions',
|
||||
|
||||
@@ -1,81 +1,124 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
import { CDN_CN_URL, CDN_URL } from '@/constants'
|
||||
import { CDN_DEMO_CN_URL, CDN_DEMO_URL, CDN_FULL_CN_URL, CDN_FULL_URL } from '@/constants'
|
||||
|
||||
export default function CdnSetup() {
|
||||
const { i18n } = useTranslation()
|
||||
const isZh = i18n.language === 'zh-CN'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BetaNotice />
|
||||
<div className="space-y-10">
|
||||
<header>
|
||||
<h1 className="text-4xl font-bold mb-4">{isZh ? 'CDN 引入' : 'CDN Setup'}</h1>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{isZh
|
||||
? 'CDN 提供两种构建版本:Demo 版用于快速体验,标准版用法与 NPM 一致。'
|
||||
: 'CDN provides two builds: Demo for quick testing, standard build with usage identical to NPM.'}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<h1 className="text-4xl font-bold mb-6">{isZh ? 'CDN 引入' : 'CDN Setup'}</h1>
|
||||
{/* Demo Build Section */}
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-3">{isZh ? '🚀 快速体验' : '🚀 Quick Try'}</h2>
|
||||
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
|
||||
{isZh
|
||||
? '通过 CDN 快速集成 page-agent,无需复杂的构建配置。默认使用演示模型。'
|
||||
: 'Fastest way to integrate page-agent via CDN. No complex build setup needed. Demo model will be used by default.'}
|
||||
</p>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'CDN 地址' : 'CDN URLs'}</h2>
|
||||
|
||||
<div className="overflow-x-auto mb-6">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-gray-200 dark:border-gray-700">
|
||||
<th className="text-left py-3 px-4 font-semibold">{isZh ? '位置' : 'Location'}</th>
|
||||
<th className="text-left py-3 px-4 font-semibold">URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-100 dark:border-gray-800">
|
||||
<td className="py-3 px-4">{isZh ? '全球' : 'Global'}</td>
|
||||
<td className="py-3 px-4 font-mono text-xs break-all">{CDN_URL}</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100 dark:border-gray-800">
|
||||
<td className="py-3 px-4">{isZh ? '中国镜像' : 'China Mirror'}</td>
|
||||
<td className="py-3 px-4 font-mono text-xs break-all">{CDN_CN_URL}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '快速开始' : 'Quick Start'}</h2>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-3">
|
||||
{isZh
|
||||
? '自动初始化并使用内置 Demo LLM,无需任何配置:'
|
||||
: 'Auto-initializes with built-in demo LLM, no configuration needed:'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-6"
|
||||
code={`<script
|
||||
src="${CDN_URL}"
|
||||
crossorigin="true"
|
||||
type="text/javascript"
|
||||
></script>`}
|
||||
className="mb-3"
|
||||
code={`<script src="${CDN_DEMO_URL}" crossorigin="true"></script>`}
|
||||
/>
|
||||
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 px-4 py-3 rounded-lg mb-4 text-sm">
|
||||
<span className="text-yellow-800 dark:text-yellow-200">
|
||||
⚠️{' '}
|
||||
{isZh
|
||||
? '仅用于技术评估,Demo 模型有速率限制。'
|
||||
: 'For evaluation only. Demo model has rate limits.'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-gray-200 dark:border-gray-700">
|
||||
<th className="text-left py-2 px-3 font-semibold w-28">
|
||||
{isZh ? '位置' : 'Location'}
|
||||
</th>
|
||||
<th className="text-left py-2 px-3 font-semibold">URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-100 dark:border-gray-800">
|
||||
<td className="py-2 px-3">{isZh ? '全球' : 'Global'}</td>
|
||||
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_DEMO_URL}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3">{isZh ? '中国' : 'China'}</td>
|
||||
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_DEMO_CN_URL}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-4 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
|
||||
⚠️ {isZh ? '注意事项' : 'Notes'}
|
||||
</h3>
|
||||
<ul className="text-gray-600 dark:text-gray-300 space-y-1">
|
||||
<li>
|
||||
• {isZh ? '生产环境建议使用固定版本号' : 'Use fixed version number in production'}
|
||||
</li>
|
||||
<li>• {isZh ? '确保 HTTPS 环境下使用' : 'Ensure HTTPS environment'}</li>
|
||||
<li>
|
||||
•{' '}
|
||||
{isZh
|
||||
? '配置 CSP 策略允许脚本执行'
|
||||
: 'Configure CSP policy to allow script execution'}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* CDN Build Section */}
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-3">{isZh ? '📦 标准版' : '📦 Standard Build'}</h2>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-3">
|
||||
{isZh
|
||||
? '将 PageAgent 类暴露到 globalThis,用法与 NPM 一致:'
|
||||
: 'Exposes PageAgent class to globalThis, usage identical to NPM:'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
className="mb-4"
|
||||
code={`<script src="${CDN_FULL_URL}" crossorigin="true"></script>
|
||||
<script>
|
||||
const agent = new PageAgent({
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
})
|
||||
await agent.execute('Click the submit button')
|
||||
</script>`}
|
||||
/>
|
||||
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-gray-200 dark:border-gray-700">
|
||||
<th className="text-left py-2 px-3 font-semibold w-28">
|
||||
{isZh ? '位置' : 'Location'}
|
||||
</th>
|
||||
<th className="text-left py-2 px-3 font-semibold">URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-100 dark:border-gray-800">
|
||||
<td className="py-2 px-3">{isZh ? '全球' : 'Global'}</td>
|
||||
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_FULL_URL}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3">{isZh ? '中国' : 'China'}</td>
|
||||
<td className="py-2 px-3 font-mono text-xs break-all">{CDN_FULL_CN_URL}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
{/* Tips */}
|
||||
<section className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
|
||||
<h3 className="text-base font-semibold mb-2 text-blue-900 dark:text-blue-300">
|
||||
💡 {isZh ? '提示' : 'Tips'}
|
||||
</h3>
|
||||
<ul className="text-gray-600 dark:text-gray-300 text-sm space-y-1">
|
||||
<li>• {isZh ? '生产环境推荐使用 NPM' : 'NPM is recommended for production'}</li>
|
||||
<li>• {isZh ? '生产环境建议锁定版本号' : 'Lock version number in production'}</li>
|
||||
<li>• {isZh ? '确保 HTTPS 环境' : 'Ensure HTTPS environment'}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -121,6 +121,13 @@ export default function Configuration() {
|
||||
|
||||
/** Viewport expansion in pixels (-1 for full page) */
|
||||
viewportExpansion?: number
|
||||
|
||||
/**
|
||||
* Enable visual mask overlay during automation.
|
||||
* Blocks user interaction while agent is running.
|
||||
* Default: false for PageController, true for PageAgent.
|
||||
*/
|
||||
enableMask?: boolean
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
@@ -2,10 +2,11 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import BetaNotice from '@/components/BetaNotice'
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
import { CDN_CN_URL, CDN_URL } from '@/constants'
|
||||
import { CDN_DEMO_CN_URL, CDN_DEMO_URL } from '@/constants'
|
||||
|
||||
export default function QuickStart() {
|
||||
const { t } = useTranslation('docs')
|
||||
const { t, i18n } = useTranslation('docs')
|
||||
const isZh = i18n.language === 'zh-CN'
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -16,64 +17,62 @@ export default function QuickStart() {
|
||||
<h2 className="text-2xl font-bold mb-3">{t('quick_start.installation')}</h2>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
{/* Demo CDN - One Line */}
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-blue-900 dark:text-blue-300">
|
||||
{t('quick_start.step1_title')}
|
||||
{isZh ? '🚀 快速体验(Demo CDN)' : '🚀 Quick Try (Demo CDN)'}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-2">{t('quick_start.step1_cdn')}</p>
|
||||
<CodeEditor
|
||||
code={`// CDN: \t${CDN_URL}
|
||||
// Mirror: \t${CDN_CN_URL}
|
||||
<script src="${CDN_URL}" crossorigin="true" type="text/javascript"></script>`}
|
||||
language="html"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-2">{t('quick_start.step1_npm')}</p>
|
||||
<CodeEditor
|
||||
code={`// npm install page-agent
|
||||
import PageAgent from 'page-agent'`}
|
||||
language="bash"
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-2 rounded mb-3 text-sm">
|
||||
<span className="text-yellow-800 dark:text-yellow-200">
|
||||
⚠️ {isZh ? '仅用于技术评估' : 'For evaluation only'}
|
||||
</span>
|
||||
</div>
|
||||
<CodeEditor
|
||||
code={`// Global: ${CDN_DEMO_URL}
|
||||
// China: ${CDN_DEMO_CN_URL}
|
||||
<script src="${isZh ? CDN_DEMO_CN_URL : CDN_DEMO_URL}" crossorigin="true"></script>`}
|
||||
language="html"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* NPM - Recommended */}
|
||||
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-green-900 dark:text-green-300">
|
||||
{t('quick_start.step2_title')}
|
||||
{isZh ? '📦 NPM 安装(推荐)' : '📦 NPM Install (Recommended)'}
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`// 仅供测试使用,生产环境需要配置 LLM 接入点,本工具不提供 LLM 服务
|
||||
// 测试接口
|
||||
// @note: 限流,限制 prompt 内容,限制来源,随时变更,请替换成你自己的
|
||||
// @note: 使用 DeepSeek-chat(3.2) 官方版本,使用协议和隐私策略见 DeepSeek 网站
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
code={`npm install page-agent
|
||||
|
||||
const agent = new PageAgent({
|
||||
model: DEMO_MODEL,
|
||||
baseURL: DEMO_BASE_URL,
|
||||
apiKey: DEMO_API_KEY,
|
||||
language: 'zh-CN'
|
||||
})`}
|
||||
language="javascript"
|
||||
import { PageAgent } from 'page-agent'`}
|
||||
language="bash"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-purple-900 dark:text-purple-300">
|
||||
{t('quick_start.step2_title')}
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`const agent = new PageAgent({
|
||||
model: 'deepseek-chat',
|
||||
baseURL: 'https://api.deepseek.com',
|
||||
apiKey: 'YOUR_API_KEY',
|
||||
language: '${isZh ? 'zh-CN' : 'en-US'}'
|
||||
})`}
|
||||
language="javascript"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2 text-orange-900 dark:text-orange-300">
|
||||
{t('quick_start.step3_title')}
|
||||
</h3>
|
||||
<CodeEditor
|
||||
code={`// 程序化执行自然语言指令
|
||||
await pageAgent.execute('点击提交按钮,然后填写用户名为张三');
|
||||
code={`// ${isZh ? '程序化执行自然语言指令' : 'Execute natural language instructions programmatically'}
|
||||
await agent.execute('${isZh ? '点击提交按钮,然后填写用户名为张三' : 'Click submit button, then fill username as John'}');
|
||||
|
||||
// 或者显示对话框让用户输入指令
|
||||
pageAgent.panel.show()
|
||||
// ${isZh ? '或者显示对话框让用户输入指令' : 'Or show panel for user to input instructions'}
|
||||
agent.panel.show()
|
||||
`}
|
||||
language="javascript"
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import { config as dotenvConfig } from 'dotenv'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const pageAgentPkg = JSON.parse(
|
||||
readFileSync(resolve(__dirname, '../page-agent/package.json'), 'utf-8')
|
||||
)
|
||||
|
||||
// Load .env from repo root
|
||||
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
||||
@@ -32,5 +36,6 @@ export default defineConfig({
|
||||
'import.meta.env.LLM_MODEL_NAME': JSON.stringify(process.env.LLM_MODEL_NAME),
|
||||
'import.meta.env.LLM_API_KEY': JSON.stringify(process.env.LLM_API_KEY),
|
||||
'import.meta.env.LLM_BASE_URL': JSON.stringify(process.env.LLM_BASE_URL),
|
||||
'import.meta.env.VERSION': JSON.stringify(pageAgentPkg.version),
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user