Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1b485691f | |||
| 694e05b800 | |||
| b761795c1a | |||
| 8cc54983aa | |||
| 59a599285e | |||
| 2f8c2b395c | |||
| 28f836674c | |||
| 6f01632231 | |||
| 41123ac561 | |||
| c8bf0dd20f | |||
| ec2b744bab | |||
| eab9cf64e8 | |||
| 42130d6f1d | |||
| c0ce6e7d14 | |||
| 8f5f98f889 | |||
| ee4719cdcf | |||
| b4c82b7833 | |||
| aefc3cfb89 | |||
| 7f2b8c2766 |
Vendored
+1
@@ -5,6 +5,7 @@
|
|||||||
"HITL",
|
"HITL",
|
||||||
"innerhtml",
|
"innerhtml",
|
||||||
"llms",
|
"llms",
|
||||||
|
"onwarn",
|
||||||
"opensource",
|
"opensource",
|
||||||
"qwen",
|
"qwen",
|
||||||
"retryable",
|
"retryable",
|
||||||
|
|||||||
+37
-27
@@ -14,7 +14,7 @@ Thank you for your interest in contributing to Page-Agent! We welcome contributi
|
|||||||
2. **Setup**
|
2. **Setup**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm ci
|
||||||
npm start # Start demo and documentation site
|
npm start # Start demo and documentation site
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ This is a **monorepo** with npm workspaces containing **two main packages**:
|
|||||||
1. **Core Library** (`packages/page-agent/`) - Pure JavaScript/TypeScript AI agent library for browser DOM automation, published as `page-agent` on npm
|
1. **Core Library** (`packages/page-agent/`) - Pure JavaScript/TypeScript AI agent library for browser DOM automation, published as `page-agent` on npm
|
||||||
2. **Website** (`packages/website/`) - React documentation and landing page. Also as demo and test page for the core lib. private package `@page-agent/website`
|
2. **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 `UMD` and `Website`. Bundle everything including local packages.
|
- When bundling `IIFE` and `Website`. Bundle everything including local packages.
|
||||||
|
|
||||||
## 🤝 How to Contribute
|
## 🤝 How to Contribute
|
||||||
|
|
||||||
@@ -61,10 +61,9 @@ We use a simplified monorepo solution with `native npm-workspace + ts reference
|
|||||||
- Update documentation as needed
|
- Update documentation as needed
|
||||||
|
|
||||||
4. **Test Your Changes**
|
4. **Test Your Changes**
|
||||||
|
- Test in our demo website
|
||||||
```bash
|
- Test it on other websites if applicable
|
||||||
# TODO
|
- `@TODO: test suite`
|
||||||
```
|
|
||||||
|
|
||||||
5. **Commit and Push**
|
5. **Commit and Push**
|
||||||
|
|
||||||
@@ -94,41 +93,52 @@ We use a simplified monorepo solution with `native npm-workspace + ts reference
|
|||||||
- It's **recommended** to heavily rely on AI (aka "vibe coding") when maintaining **demo pages and tests**.
|
- It's **recommended** to heavily rely on AI (aka "vibe coding") when maintaining **demo pages and tests**.
|
||||||
- BUT **NOT the core lib!!!** Be very careful if AI ever touched the core lib!!!
|
- BUT **NOT the core lib!!!** Be very careful if AI ever touched the core lib!!!
|
||||||
- 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 when structure changed.
|
- Update the [AI instructions](AGENTS.md) when structure changed.
|
||||||
- Cursor and Cline: `./.cursor/rules`
|
|
||||||
- Github Copilot: `./.github/copilot-instructions.md`
|
|
||||||
- Claude Code: `./CLAUDE.md`
|
|
||||||
|
|
||||||
## 🔧 Development Workflows
|
## 🔧 Development Workflows
|
||||||
|
|
||||||
|
### Test With Your Own LLM API
|
||||||
|
|
||||||
|
- Create a `.env` file in the repo root with your LLM API config
|
||||||
|
|
||||||
|
```env
|
||||||
|
LLM_MODEL_NAME=gpt-5.2
|
||||||
|
LLM_API_KEY=your-api-key
|
||||||
|
LLM_BASE_URL=https://api.your-llm-provider.com/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
- Restart the dev server to load new env vars
|
||||||
|
- If not provided, the demo will the free testing proxy by default
|
||||||
|
|
||||||
### Website Development
|
### Website Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm start # React development server
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### Core Lib Development and Testing
|
### Testing on Other Websites
|
||||||
|
|
||||||
> @TODO this part is outdated. Update this.
|
- Start and serve a local `iife` script
|
||||||
|
|
||||||
- Config your LLM API
|
```bash
|
||||||
- Start and serve a local umd script
|
npm run dev:iife # Serving IIFE with auto rebuild at http://localhost:5174/page-agent.js
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
- Add a new bookmark
|
||||||
npm run dev:umd # Serving UMD with auto rebuild at http://localhost:5173/page-agent.umd.cjs
|
|
||||||
```
|
|
||||||
|
|
||||||
- Add a new bookmark enable it on other website
|
```javascript
|
||||||
|
javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5174/page-agent.js?t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
|
||||||
|
```
|
||||||
|
|
||||||
```
|
- Click the bookmark on any page to load Page-Agent
|
||||||
javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5173/page-agent.umd.cjs?t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
|
|
||||||
```
|
> Warning: AK in your local `.env` will be inlined in the iife script.
|
||||||
|
|
||||||
### Adding Documentation
|
### Adding Documentation
|
||||||
|
|
||||||
1. Create `website/src/docs/section/page-name/page.tsx`
|
Ask an AI to help you add documentation to the `website/` package. Follow the existing style.
|
||||||
2. Add route to `website/src/router.tsx`
|
|
||||||
3. Add navigation link to `website/src/components/DocsLayout.tsx`
|
> Our AGENTS.md file and guardrails are designed for this purpose. But please be careful and review anything AI generated.
|
||||||
|
|
||||||
## 🎯 Contribution Areas
|
## 🎯 Contribution Areas
|
||||||
|
|
||||||
@@ -143,7 +153,7 @@ We especially welcome contributions in:
|
|||||||
|
|
||||||
## 🚫 What We Don't Accept
|
## 🚫 What We Don't Accept
|
||||||
|
|
||||||
- Changes that break existing API compatibility
|
- Changes that break existing API compatibility (Discuss first)
|
||||||
- Heavy dependencies to core library
|
- Heavy dependencies to core library
|
||||||
- Contributions without proper testing
|
- Contributions without proper testing
|
||||||
- Code that doesn't follow project conventions
|
- Code that doesn't follow project conventions
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2025 Alibaba
|
Copyright (c) 2026 Alibaba
|
||||||
|
Copyright (c) 2026 Simon
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
Generated
+15
-29
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
@@ -1540,20 +1540,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/api-extractor/node_modules/typescript": {
|
|
||||||
"version": "5.8.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
|
||||||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@microsoft/api-extractor/node_modules/yallist": {
|
"node_modules/@microsoft/api-extractor/node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
@@ -4627,9 +4613,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
|
||||||
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
|
"integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -8147,15 +8133,15 @@
|
|||||||
},
|
},
|
||||||
"packages/cdn": {
|
"packages/cdn": {
|
||||||
"name": "@page-agent/cdn",
|
"name": "@page-agent/cdn",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"page-agent": "0.1.0"
|
"page-agent": "0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/llms": {
|
"packages/llms": {
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
@@ -8163,19 +8149,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-agent": {
|
"packages/page-agent": {
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/llms": "0.1.0",
|
"@page-agent/llms": "0.2.2",
|
||||||
"@page-agent/page-controller": "0.1.0",
|
"@page-agent/page-controller": "0.2.2",
|
||||||
"@page-agent/ui": "0.1.0",
|
"@page-agent/ui": "0.2.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/page-controller": {
|
"packages/page-controller": {
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ai-motion": "^0.4.8"
|
"ai-motion": "^0.4.8"
|
||||||
@@ -8183,12 +8169,12 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"packages/website": {
|
"packages/website": {
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@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",
|
||||||
|
|||||||
+5
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/page-controller",
|
"packages/page-controller",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"build:website": "npm run build:website --workspace=@page-agent/website",
|
"build:website": "npm run build:website --workspace=@page-agent/website",
|
||||||
"build:libs": "npm run build --workspaces --if-present",
|
"build:libs": "npm run build --workspaces --if-present",
|
||||||
"build": "npm run build:libs && npm run build:website",
|
"build": "npm run build:libs && npm run build:website",
|
||||||
"dev:umd": "npm run dev:umd --workspace=page-agent",
|
"dev:iife": "npm run dev:iife --workspace=page-agent",
|
||||||
"version": "node scripts/sync-version.js",
|
"version": "node scripts/sync-version.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
@@ -59,6 +59,9 @@
|
|||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.2"
|
"vite-plugin-css-injected-by-js": "^3.5.2"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,cjs,cts,mjs,mts}": [
|
"*.{js,ts,cjs,cts,mjs,mts}": [
|
||||||
"npx prettier --write --ignore-unknown",
|
"npx prettier --write --ignore-unknown",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/cdn",
|
"name": "@page-agent/cdn",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/"
|
"dist/"
|
||||||
@@ -18,6 +18,6 @@
|
|||||||
"build": "vite build && vite build --mode demo"
|
"build": "vite build && vite build --mode demo"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"page-agent": "0.1.0"
|
"page-agent": "0.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ export default defineConfig(({ mode }) => {
|
|||||||
outDir: resolve(__dirname, 'dist'),
|
outDir: resolve(__dirname, 'dist'),
|
||||||
emptyOutDir: !isDemo, // only empty on first build (full)
|
emptyOutDir: !isDemo, // only empty on first build (full)
|
||||||
minify: false,
|
minify: false,
|
||||||
|
cssCodeSplit: true,
|
||||||
|
rollupOptions: {
|
||||||
|
onwarn: function (message, handler) {
|
||||||
|
if (message.code === 'EVAL') return
|
||||||
|
handler(message)
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': '"production"',
|
'process.env.NODE_ENV': '"production"',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/llms",
|
"name": "@page-agent/llms",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"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",
|
||||||
|
|||||||
@@ -34,4 +34,3 @@ export default defineConfig({
|
|||||||
'process.env.NODE_ENV': '"production"',
|
'process.env.NODE_ENV': '"production"',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "page-agent",
|
"name": "page-agent",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"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",
|
||||||
@@ -37,17 +37,16 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://alibaba.github.io/page-agent/",
|
"homepage": "https://alibaba.github.io/page-agent/",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build && vite build --config vite.umd.config.js",
|
"build": "vite build",
|
||||||
"serve": "npx serve dist/umd -p 5173",
|
"dev:iife": "concurrently \"vite build --config vite.iife.config.js --watch\" \"npx serve dist/iife -p 5174\"",
|
||||||
"dev:umd": "concurrently \"vite build --config vite.umd.config.js --watch\" \"npm run serve\"",
|
|
||||||
"prepublishOnly": "node -e \"const fs=require('fs');['README.md','LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
"prepublishOnly": "node -e \"const fs=require('fs');['README.md','LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
||||||
"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": {
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"@page-agent/llms": "0.1.0",
|
"@page-agent/llms": "0.2.2",
|
||||||
"@page-agent/page-controller": "0.1.0",
|
"@page-agent/page-controller": "0.2.2",
|
||||||
"@page-agent/ui": "0.1.0"
|
"@page-agent/ui": "0.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import type { PageAgentConfig } from './config'
|
|||||||
import { MAX_STEPS } from './config/constants'
|
import { 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 { normalizeResponse, trimLines, uid, waitUntil } from './utils'
|
import { normalizeResponse, trimLines, uid } from './utils'
|
||||||
import { assert } from './utils/assert'
|
import { assert } from './utils/assert'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent brain state - the reflection-before-action model
|
* Agent reflection state - the reflection-before-action model
|
||||||
*
|
*
|
||||||
* Every tool call must first reflect on:
|
* Every tool call must first reflect on:
|
||||||
* - evaluation_previous_goal: How well did the previous action achieve its goal?
|
* - evaluation_previous_goal: How well did the previous action achieve its goal?
|
||||||
@@ -50,8 +50,12 @@ export interface MacroToolResult {
|
|||||||
export type { PageAgentConfig }
|
export type { PageAgentConfig }
|
||||||
export { tool, type PageAgentTool } from './tools'
|
export { tool, type PageAgentTool } from './tools'
|
||||||
|
|
||||||
export interface AgentHistory {
|
/**
|
||||||
brain: Partial<AgentReflection>
|
* A single agent step with reflection and action
|
||||||
|
*/
|
||||||
|
export interface AgentStep {
|
||||||
|
type: 'step'
|
||||||
|
reflection: Partial<AgentReflection>
|
||||||
action: {
|
action: {
|
||||||
name: string
|
name: string
|
||||||
input: any
|
input: any
|
||||||
@@ -66,10 +70,33 @@ export interface AgentHistory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistent observation event (stays in memory)
|
||||||
|
*/
|
||||||
|
export interface ObservationEvent {
|
||||||
|
type: 'observation'
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User takeover event
|
||||||
|
*/
|
||||||
|
export interface UserTakeoverEvent {
|
||||||
|
type: 'user_takeover'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type for all history events
|
||||||
|
*/
|
||||||
|
export type HistoryEvent = AgentStep | ObservationEvent | UserTakeoverEvent
|
||||||
|
|
||||||
|
/** @deprecated Use AgentStep instead */
|
||||||
|
export type AgentHistory = AgentStep
|
||||||
|
|
||||||
export interface ExecutionResult {
|
export interface ExecutionResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
data: string
|
data: string
|
||||||
history: AgentHistory[]
|
history: HistoryEvent[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageAgent extends EventTarget {
|
export class PageAgent extends EventTarget {
|
||||||
@@ -77,13 +104,11 @@ export class PageAgent extends EventTarget {
|
|||||||
id = uid()
|
id = uid()
|
||||||
panel: Panel
|
panel: Panel
|
||||||
tools: typeof tools
|
tools: typeof tools
|
||||||
paused = false
|
|
||||||
disposed = false
|
disposed = false
|
||||||
task = ''
|
task = ''
|
||||||
taskId = ''
|
taskId = ''
|
||||||
|
|
||||||
#llm: LLM
|
#llm: LLM
|
||||||
#totalWaitTime = 0
|
|
||||||
#abortController = new AbortController()
|
#abortController = new AbortController()
|
||||||
#llmRetryListener: ((e: Event) => void) | null = null
|
#llmRetryListener: ((e: Event) => void) | null = null
|
||||||
#llmErrorListener: ((e: Event) => void) | null = null
|
#llmErrorListener: ((e: Event) => void) | null = null
|
||||||
@@ -92,8 +117,16 @@ export class PageAgent extends EventTarget {
|
|||||||
/** PageController for DOM operations */
|
/** PageController for DOM operations */
|
||||||
pageController: PageController
|
pageController: PageController
|
||||||
|
|
||||||
/** History records */
|
/** Runtime states for tracking across steps */
|
||||||
history: AgentHistory[] = []
|
states = {
|
||||||
|
/** Accumulated wait time in seconds, used by wait tool */
|
||||||
|
totalWaitTime: 0,
|
||||||
|
/** Last known URL for detecting navigation */
|
||||||
|
lastURL: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
/** History event stream */
|
||||||
|
history: HistoryEvent[] = []
|
||||||
|
|
||||||
constructor(config: PageAgentConfig) {
|
constructor(config: PageAgentConfig) {
|
||||||
super()
|
super()
|
||||||
@@ -104,11 +137,6 @@ export class PageAgent extends EventTarget {
|
|||||||
language: this.config.language,
|
language: this.config.language,
|
||||||
onExecuteTask: (task) => this.execute(task),
|
onExecuteTask: (task) => this.execute(task),
|
||||||
onStop: () => this.dispose(),
|
onStop: () => this.dispose(),
|
||||||
onPauseToggle: () => {
|
|
||||||
this.paused = !this.paused
|
|
||||||
return this.paused
|
|
||||||
},
|
|
||||||
getPaused: () => this.paused,
|
|
||||||
})
|
})
|
||||||
this.tools = new Map(tools)
|
this.tools = new Map(tools)
|
||||||
|
|
||||||
@@ -150,6 +178,15 @@ export class PageAgent extends EventTarget {
|
|||||||
window.addEventListener('beforeunload', this.#beforeUnloadListener)
|
window.addEventListener('beforeunload', this.#beforeUnloadListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a persistent observation to the history event stream.
|
||||||
|
* This will be visible in <agent_history> and remain in memory across steps.
|
||||||
|
*/
|
||||||
|
pushObservation(content: string): void {
|
||||||
|
this.history.push({ type: 'observation', content })
|
||||||
|
this.panel.update({ type: 'observation', content })
|
||||||
|
}
|
||||||
|
|
||||||
async execute(task: string): Promise<ExecutionResult> {
|
async execute(task: string): Promise<ExecutionResult> {
|
||||||
if (!task) throw new Error('Task is required')
|
if (!task) throw new Error('Task is required')
|
||||||
this.task = task
|
this.task = task
|
||||||
@@ -177,18 +214,24 @@ export class PageAgent extends EventTarget {
|
|||||||
|
|
||||||
this.history = []
|
this.history = []
|
||||||
|
|
||||||
|
// Reset states
|
||||||
|
this.states = {
|
||||||
|
totalWaitTime: 0,
|
||||||
|
lastURL: '',
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let step = 0
|
let step = 0
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
await this.#generateObservations(step)
|
||||||
|
|
||||||
await onBeforeStep.call(this, step)
|
await onBeforeStep.call(this, step)
|
||||||
|
|
||||||
console.group(`step: ${step}`)
|
console.group(`step: ${step}`)
|
||||||
|
|
||||||
// abort
|
// abort
|
||||||
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
||||||
// pause
|
|
||||||
await waitUntil(() => !this.paused)
|
|
||||||
|
|
||||||
// Update status to thinking
|
// Update status to thinking
|
||||||
console.log(chalk.blue('Thinking...'))
|
console.log(chalk.blue('Thinking...'))
|
||||||
@@ -216,23 +259,24 @@ export class PageAgent extends EventTarget {
|
|||||||
const macroResult = result.toolResult as MacroToolResult
|
const macroResult = result.toolResult as MacroToolResult
|
||||||
const input = macroResult.input
|
const input = macroResult.input
|
||||||
const output = macroResult.output
|
const output = macroResult.output
|
||||||
const brain = {
|
const reflection: Partial<AgentReflection> = {
|
||||||
evaluation_previous_goal: input.evaluation_previous_goal || '',
|
evaluation_previous_goal: input.evaluation_previous_goal,
|
||||||
memory: input.memory || '',
|
memory: input.memory,
|
||||||
next_goal: input.next_goal || '',
|
next_goal: input.next_goal,
|
||||||
}
|
}
|
||||||
const actionName = Object.keys(input.action)[0]
|
const actionName = Object.keys(input.action)[0]
|
||||||
const action = {
|
const action: AgentStep['action'] = {
|
||||||
name: actionName,
|
name: actionName,
|
||||||
input: input.action[actionName],
|
input: input.action[actionName],
|
||||||
output: output,
|
output: output,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.history.push({
|
this.history.push({
|
||||||
brain,
|
type: 'step',
|
||||||
|
reflection,
|
||||||
action,
|
action,
|
||||||
usage: result.usage,
|
usage: result.usage,
|
||||||
})
|
} as AgentStep)
|
||||||
|
|
||||||
console.log(chalk.green('Step finished:'), actionName)
|
console.log(chalk.green('Step finished:'), actionName)
|
||||||
console.groupEnd()
|
console.groupEnd()
|
||||||
@@ -311,21 +355,26 @@ export class PageAgent extends EventTarget {
|
|||||||
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
||||||
// abort
|
// abort
|
||||||
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
||||||
// pause
|
|
||||||
await waitUntil(() => !this.paused)
|
|
||||||
|
|
||||||
console.log(chalk.blue.bold('MacroTool execute'), input)
|
console.log(chalk.blue.bold('MacroTool execute'), input)
|
||||||
const action = input.action
|
const action = input.action
|
||||||
|
|
||||||
const toolName = Object.keys(action)[0]
|
const toolName = Object.keys(action)[0]
|
||||||
const toolInput = action[toolName]
|
const toolInput = action[toolName]
|
||||||
const brain = trimLines(`✅: ${input.evaluation_previous_goal}
|
|
||||||
💾: ${input.memory}
|
|
||||||
🎯: ${input.next_goal}
|
|
||||||
`)
|
|
||||||
|
|
||||||
console.log(brain)
|
// Build reflection text, only include non-empty fields
|
||||||
this.panel.update({ type: 'thinking', text: brain })
|
const reflectionLines: string[] = []
|
||||||
|
if (input.evaluation_previous_goal)
|
||||||
|
reflectionLines.push(`✅: ${input.evaluation_previous_goal}`)
|
||||||
|
if (input.memory) reflectionLines.push(`💾: ${input.memory}`)
|
||||||
|
if (input.next_goal) reflectionLines.push(`🎯: ${input.next_goal}`)
|
||||||
|
|
||||||
|
const reflectionText = reflectionLines.length > 0 ? reflectionLines.join('\n') : ''
|
||||||
|
|
||||||
|
if (reflectionText) {
|
||||||
|
console.log(reflectionText)
|
||||||
|
this.panel.update({ type: 'thinking', text: reflectionText })
|
||||||
|
}
|
||||||
|
|
||||||
// Find the corresponding tool
|
// Find the corresponding tool
|
||||||
const tool = tools.get(toolName)
|
const tool = tools.get(toolName)
|
||||||
@@ -337,20 +386,14 @@ export class PageAgent extends EventTarget {
|
|||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
// Execute tool, bind `this` to PageAgent
|
// Execute tool, bind `this` to PageAgent
|
||||||
let result = await tool.execute.bind(this)(toolInput)
|
const result = await tool.execute.bind(this)(toolInput)
|
||||||
|
|
||||||
const duration = Date.now() - startTime
|
const duration = Date.now() - startTime
|
||||||
console.log(chalk.green.bold(`Tool (${toolName}) executed for ${duration}ms`), result)
|
console.log(chalk.green.bold(`Tool (${toolName}) executed for ${duration}ms`), result)
|
||||||
|
|
||||||
if (toolName === 'wait') {
|
// Reset wait time for non-wait tools
|
||||||
this.#totalWaitTime += Math.round(toolInput.seconds + duration / 1000)
|
if (toolName !== 'wait') {
|
||||||
result += `\n<sys> You have waited ${this.#totalWaitTime} seconds accumulatively.`
|
this.states.totalWaitTime = 0
|
||||||
if (this.#totalWaitTime >= 3)
|
|
||||||
result += '\nDo NOT wait any longer unless you have a good reason.\n'
|
|
||||||
result += '</sys>'
|
|
||||||
} else {
|
|
||||||
// For other tools, reset wait time
|
|
||||||
this.#totalWaitTime = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Briefly display execution result
|
// Briefly display execution result
|
||||||
@@ -427,6 +470,34 @@ export class PageAgent extends EventTarget {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate observations before each step
|
||||||
|
* - URL change detection
|
||||||
|
* - Too many steps warning
|
||||||
|
* @todo loop detection
|
||||||
|
* @todo console error
|
||||||
|
*/
|
||||||
|
async #generateObservations(stepCount: number): Promise<void> {
|
||||||
|
// Detect URL change
|
||||||
|
const currentURL = await this.pageController.getCurrentUrl()
|
||||||
|
if (currentURL !== this.states.lastURL) {
|
||||||
|
this.pushObservation(`Page navigated to → ${currentURL}`)
|
||||||
|
this.states.lastURL = currentURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about remaining steps
|
||||||
|
const remaining = MAX_STEPS - stepCount
|
||||||
|
if (remaining === 5) {
|
||||||
|
this.pushObservation(
|
||||||
|
`⚠️ Only ${remaining} steps remaining. Consider wrapping up or calling done with partial results.`
|
||||||
|
)
|
||||||
|
} else if (remaining === 2) {
|
||||||
|
this.pushObservation(
|
||||||
|
`⚠️ Critical: Only ${remaining} steps left! You must finish the task or call done immediately.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async #assembleUserPrompt(): Promise<string> {
|
async #assembleUserPrompt(): Promise<string> {
|
||||||
let prompt = ''
|
let prompt = ''
|
||||||
|
|
||||||
@@ -438,31 +509,42 @@ export class PageAgent extends EventTarget {
|
|||||||
// - <step_info>
|
// - <step_info>
|
||||||
// <agent_state>
|
// <agent_state>
|
||||||
|
|
||||||
|
const stepCount = this.history.filter((e) => e.type === 'step').length
|
||||||
|
|
||||||
prompt += `<agent_state>
|
prompt += `<agent_state>
|
||||||
<user_request>
|
<user_request>
|
||||||
${this.task}
|
${this.task}
|
||||||
</user_request>
|
</user_request>
|
||||||
<step_info>
|
<step_info>
|
||||||
Step ${this.history.length + 1} of ${MAX_STEPS} max possible steps
|
Step ${stepCount + 1} of ${MAX_STEPS} max possible steps
|
||||||
Current date and time: ${new Date().toISOString()}
|
Current date and time: ${new Date().toISOString()}
|
||||||
</step_info>
|
</step_info>
|
||||||
</agent_state>
|
</agent_state>
|
||||||
`
|
`
|
||||||
|
|
||||||
// <agent_history>
|
// <agent_history>
|
||||||
// - <step_>
|
// - <step_N> for steps
|
||||||
|
// - <sys> for observations and system messages
|
||||||
|
|
||||||
prompt += '\n<agent_history>\n'
|
prompt += '\n<agent_history>\n'
|
||||||
|
|
||||||
this.history.forEach((history, index) => {
|
let stepIndex = 0
|
||||||
prompt += `<step_${index + 1}>
|
for (const event of this.history) {
|
||||||
Evaluation of Previous Step: ${history.brain.evaluation_previous_goal}
|
if (event.type === 'step') {
|
||||||
Memory: ${history.brain.memory}
|
stepIndex++
|
||||||
Next Goal: ${history.brain.next_goal}
|
prompt += `<step_${stepIndex}>
|
||||||
Action Results: ${history.action.output}
|
Evaluation of Previous Step: ${event.reflection.evaluation_previous_goal}
|
||||||
</step_${index + 1}>
|
Memory: ${event.reflection.memory}
|
||||||
|
Next Goal: ${event.reflection.next_goal}
|
||||||
|
Action Results: ${event.action.output}
|
||||||
|
</step_${stepIndex}>
|
||||||
`
|
`
|
||||||
})
|
} else if (event.type === 'observation') {
|
||||||
|
prompt += `<sys>${event.content}</sys>\n`
|
||||||
|
} else if (event.type === 'user_takeover') {
|
||||||
|
prompt += `<sys>User took over control and made changes to the page.</sys>\n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prompt += '</agent_history>\n\n'
|
prompt += '</agent_history>\n\n'
|
||||||
|
|
||||||
@@ -492,51 +574,22 @@ export class PageAgent extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #getBrowserState(): Promise<string> {
|
async #getBrowserState(): Promise<string> {
|
||||||
const pageUrl = await this.pageController.getCurrentUrl()
|
const state = await this.pageController.getBrowserState()
|
||||||
const pageTitle = await this.pageController.getPageTitle()
|
|
||||||
const pi = await this.pageController.getPageInfo()
|
|
||||||
const viewportExpansion = await this.pageController.getViewportExpansion()
|
|
||||||
|
|
||||||
await this.pageController.updateTree()
|
|
||||||
|
|
||||||
let simplifiedHTML = await this.pageController.getSimplifiedHTML()
|
|
||||||
|
|
||||||
|
let content = state.content
|
||||||
if (this.config.transformPageContent) {
|
if (this.config.transformPageContent) {
|
||||||
simplifiedHTML = await this.config.transformPageContent(simplifiedHTML)
|
content = await this.config.transformPageContent(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
let prompt = trimLines(`<browser_state>
|
return trimLines(`<browser_state>
|
||||||
Current Page: [${pageTitle}](${pageUrl})
|
Current Page: [${state.title}](${state.url})
|
||||||
|
|
||||||
Page info: ${pi.viewport_width}x${pi.viewport_height}px viewport, ${pi.page_width}x${pi.page_height}px total page size, ${pi.pages_above.toFixed(1)} pages above, ${pi.pages_below.toFixed(1)} pages below, ${pi.total_pages.toFixed(1)} total pages, at ${(pi.current_page_position * 100).toFixed(0)}% of page
|
${state.header}
|
||||||
|
${content}
|
||||||
${viewportExpansion === -1 ? 'Interactive elements from top layer of the current page (full page):' : 'Interactive elements from top layer of the current page inside the viewport:'}
|
${state.footer}
|
||||||
|
|
||||||
|
</browser_state>
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// Page header info
|
|
||||||
const has_content_above = pi.pixels_above > 4
|
|
||||||
if (has_content_above && viewportExpansion !== -1) {
|
|
||||||
prompt += `... ${pi.pixels_above} pixels above (${pi.pages_above.toFixed(1)} pages) - scroll to see more ...\n`
|
|
||||||
} else {
|
|
||||||
prompt += `[Start of page]\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current viewport info
|
|
||||||
prompt += simplifiedHTML
|
|
||||||
prompt += `\n`
|
|
||||||
|
|
||||||
// Page footer info
|
|
||||||
const has_content_below = pi.pixels_below > 4
|
|
||||||
if (has_content_below && viewportExpansion !== -1) {
|
|
||||||
prompt += `... ${pi.pixels_below} pixels below (${pi.pages_below.toFixed(1)} pages) - scroll to see more ...\n`
|
|
||||||
} else {
|
|
||||||
prompt += `[End of page]\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt += `</browser_state>\n`
|
|
||||||
|
|
||||||
return prompt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(reason?: string) {
|
dispose(reason?: string) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { LLMConfig } from '@page-agent/llms'
|
|||||||
import type { PageControllerConfig } from '@page-agent/page-controller'
|
import type { PageControllerConfig } from '@page-agent/page-controller'
|
||||||
import type { SupportedLanguage } from '@page-agent/ui'
|
import type { SupportedLanguage } from '@page-agent/ui'
|
||||||
|
|
||||||
import type { AgentHistory, ExecutionResult, PageAgent } from '../PageAgent'
|
import type { ExecutionResult, HistoryEvent, PageAgent } from '../PageAgent'
|
||||||
import type { PageAgentTool } from '../tools'
|
import type { PageAgentTool } from '../tools'
|
||||||
|
|
||||||
export type { LLMConfig }
|
export type { LLMConfig }
|
||||||
@@ -65,7 +65,7 @@ export interface AgentConfig {
|
|||||||
// @todo: remove `this` binding, pass agent as explicit parameter instead
|
// @todo: remove `this` binding, pass agent as explicit parameter instead
|
||||||
|
|
||||||
onBeforeStep?: (this: PageAgent, stepCnt: number) => Promise<void> | void
|
onBeforeStep?: (this: PageAgent, stepCnt: number) => Promise<void> | void
|
||||||
onAfterStep?: (this: PageAgent, stepCnt: number, history: AgentHistory[]) => Promise<void> | void
|
onAfterStep?: (this: PageAgent, stepCnt: number, history: HistoryEvent[]) => Promise<void> | void
|
||||||
onBeforeTask?: (this: PageAgent) => Promise<void> | void
|
onBeforeTask?: (this: PageAgent) => Promise<void> | void
|
||||||
onAfterTask?: (this: PageAgent, result: ExecutionResult) => Promise<void> | void
|
onAfterTask?: (this: PageAgent, result: ExecutionResult) => Promise<void> | void
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,13 @@ setTimeout(() => {
|
|||||||
} else {
|
} else {
|
||||||
console.log('🚀 page-agent.js no current script detected, using default demo config')
|
console.log('🚀 page-agent.js no current script detected, using default demo config')
|
||||||
const config: PageAgentConfig = {
|
const config: PageAgentConfig = {
|
||||||
model: DEMO_MODEL,
|
// model: DEMO_MODEL,
|
||||||
baseURL: DEMO_BASE_URL,
|
// baseURL: DEMO_BASE_URL,
|
||||||
apiKey: DEMO_API_KEY,
|
// apiKey: DEMO_API_KEY,
|
||||||
|
|
||||||
|
model: import.meta.env.LLM_MODEL_NAME ? import.meta.env.LLM_MODEL_NAME : DEMO_MODEL,
|
||||||
|
baseURL: import.meta.env.LLM_BASE_URL ? import.meta.env.LLM_BASE_URL : DEMO_BASE_URL,
|
||||||
|
apiKey: import.meta.env.LLM_API_KEY ? import.meta.env.LLM_API_KEY : DEMO_API_KEY,
|
||||||
}
|
}
|
||||||
window.pageAgent = new PageAgent(config)
|
window.pageAgent = new PageAgent(config)
|
||||||
}
|
}
|
||||||
@@ -57,6 +57,15 @@ tools.set(
|
|||||||
const actualWaitTime = Math.max(0, input.seconds - (Date.now() - lastTimeUpdate) / 1000)
|
const actualWaitTime = Math.max(0, input.seconds - (Date.now() - lastTimeUpdate) / 1000)
|
||||||
console.log(`actualWaitTime: ${actualWaitTime} seconds`)
|
console.log(`actualWaitTime: ${actualWaitTime} seconds`)
|
||||||
await waitFor(actualWaitTime)
|
await waitFor(actualWaitTime)
|
||||||
|
|
||||||
|
this.states.totalWaitTime += input.seconds
|
||||||
|
|
||||||
|
if (this.states.totalWaitTime >= 3) {
|
||||||
|
this.pushObservation(
|
||||||
|
`You have waited ${this.states.totalWaitTime} seconds accumulatively. Do NOT wait any longer unless you have a good reason.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return `✅ Waited for ${input.seconds} seconds.`
|
return `✅ Waited for ${input.seconds} seconds.`
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ export interface PageAgentEventMap {
|
|||||||
// PageAgent status events
|
// PageAgent status events
|
||||||
// 'agent:execute': { params: { task: string } }
|
// 'agent:execute': { params: { task: string } }
|
||||||
// 'agent:done': { params: { text: string; success: boolean } }
|
// 'agent:done': { params: { text: string; success: boolean } }
|
||||||
// 'agent:paused': { params: undefined }
|
|
||||||
// 'agent:resumed': { params: undefined }
|
|
||||||
// 'agent:disposed': { params: undefined }
|
// 'agent:disposed': { params: undefined }
|
||||||
// 'agent:error': { params: { error: string | Error } }
|
// 'agent:error': { params: { error: string | Error } }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
import { config as dotenvConfig } from 'dotenv'
|
||||||
import { dirname, resolve } from 'path'
|
import { dirname, resolve } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
@@ -6,11 +7,14 @@ import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// Load .env from repo root
|
||||||
|
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
||||||
|
|
||||||
// UMD Bundle for CDN
|
// UMD Bundle for CDN
|
||||||
// - alias all local packages so that they can be build in
|
// - alias all local packages so that they can be build in
|
||||||
// - no external
|
// - no external
|
||||||
// - no d.ts. dts does not work with monorepo aliasing
|
// - no d.ts. dts does not work with monorepo aliasing
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => ({
|
||||||
plugins: [cssInjectedByJsPlugin({ relativeCSSInjection: true })],
|
plugins: [cssInjectedByJsPlugin({ relativeCSSInjection: true })],
|
||||||
publicDir: false,
|
publicDir: false,
|
||||||
esbuild: {
|
esbuild: {
|
||||||
@@ -25,21 +29,28 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'src/umd.ts'),
|
entry: resolve(__dirname, 'src/iife.ts'),
|
||||||
name: 'PageAgent',
|
name: 'PageAgent',
|
||||||
fileName: 'page-agent',
|
fileName: 'page-agent',
|
||||||
formats: ['umd'],
|
formats: ['iife'],
|
||||||
},
|
},
|
||||||
outDir: resolve(__dirname, 'dist', 'umd'),
|
outDir: resolve(__dirname, 'dist', 'iife'),
|
||||||
|
cssCodeSplit: true,
|
||||||
|
minify: false,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// force use .js as extension
|
// force use .js as extension
|
||||||
entryFileNames: 'page-agent.js',
|
entryFileNames: 'page-agent.js',
|
||||||
},
|
},
|
||||||
|
onwarn: function (message, handler) {
|
||||||
|
if (message.code === 'EVAL') return
|
||||||
|
handler(message)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cssCodeSplit: true,
|
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': '"production"',
|
'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),
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/page-controller",
|
"name": "@page-agent/page-controller",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"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",
|
||||||
|
|||||||
@@ -32,6 +32,20 @@ export interface PageControllerConfig extends dom.DomConfig {
|
|||||||
enableMask?: boolean
|
enableMask?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structured browser state for LLM consumption
|
||||||
|
*/
|
||||||
|
export interface BrowserState {
|
||||||
|
url: string
|
||||||
|
title: string
|
||||||
|
/** Page info + scroll position hint (e.g. "Page info: 1920x1080px...\n[Start of page]") */
|
||||||
|
header: string
|
||||||
|
/** Simplified HTML of interactive elements */
|
||||||
|
content: string
|
||||||
|
/** Page footer hint (e.g. "... 300 pixels below ..." or "[End of page]") */
|
||||||
|
footer: string
|
||||||
|
}
|
||||||
|
|
||||||
interface ActionResult {
|
interface ActionResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
message: string
|
message: string
|
||||||
@@ -93,42 +107,6 @@ export class PageController extends EventTarget {
|
|||||||
return window.location.href
|
return window.location.href
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current page title
|
|
||||||
*/
|
|
||||||
async getPageTitle(): Promise<string> {
|
|
||||||
return document.title
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get page scroll and size info
|
|
||||||
*/
|
|
||||||
async getPageInfo() {
|
|
||||||
return getPageInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the simplified HTML representation of the page.
|
|
||||||
* This is used by LLM to understand the page structure.
|
|
||||||
*/
|
|
||||||
async getSimplifiedHTML(): Promise<string> {
|
|
||||||
return this.simplifiedHTML
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get text description for an element by index
|
|
||||||
*/
|
|
||||||
async getElementText(index: number): Promise<string | undefined> {
|
|
||||||
return this.elementTextMap.get(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get total number of indexed interactive elements
|
|
||||||
*/
|
|
||||||
async getElementCount(): Promise<number> {
|
|
||||||
return this.selectorMap.size
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get last tree update timestamp
|
* Get last tree update timestamp
|
||||||
*/
|
*/
|
||||||
@@ -137,10 +115,43 @@ export class PageController extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the viewport expansion setting
|
* Get structured browser state for LLM consumption.
|
||||||
|
* Automatically calls updateTree() to refresh the DOM state.
|
||||||
*/
|
*/
|
||||||
async getViewportExpansion(): Promise<number> {
|
async getBrowserState(): Promise<BrowserState> {
|
||||||
return this.config.viewportExpansion ?? VIEWPORT_EXPANSION
|
const url = window.location.href
|
||||||
|
const title = document.title
|
||||||
|
const pi = getPageInfo()
|
||||||
|
const viewportExpansion = this.config.viewportExpansion ?? VIEWPORT_EXPANSION
|
||||||
|
|
||||||
|
await this.updateTree()
|
||||||
|
|
||||||
|
const content = this.simplifiedHTML
|
||||||
|
|
||||||
|
// Build header: page info + scroll position hint
|
||||||
|
const pageInfoLine = `Page info: ${pi.viewport_width}x${pi.viewport_height}px viewport, ${pi.page_width}x${pi.page_height}px total page size, ${pi.pages_above.toFixed(1)} pages above, ${pi.pages_below.toFixed(1)} pages below, ${pi.total_pages.toFixed(1)} total pages, at ${(pi.current_page_position * 100).toFixed(0)}% of page`
|
||||||
|
|
||||||
|
const elementsLabel =
|
||||||
|
viewportExpansion === -1
|
||||||
|
? 'Interactive elements from top layer of the current page (full page):'
|
||||||
|
: 'Interactive elements from top layer of the current page inside the viewport:'
|
||||||
|
|
||||||
|
const hasContentAbove = pi.pixels_above > 4
|
||||||
|
const scrollHintAbove =
|
||||||
|
hasContentAbove && viewportExpansion !== -1
|
||||||
|
? `... ${pi.pixels_above} pixels above (${pi.pages_above.toFixed(1)} pages) - scroll to see more ...`
|
||||||
|
: '[Start of page]'
|
||||||
|
|
||||||
|
const header = `${pageInfoLine}\n\n${elementsLabel}\n\n${scrollHintAbove}`
|
||||||
|
|
||||||
|
// Build footer: scroll position hint
|
||||||
|
const hasContentBelow = pi.pixels_below > 4
|
||||||
|
const footer =
|
||||||
|
hasContentBelow && viewportExpansion !== -1
|
||||||
|
? `... ${pi.pixels_below} pixels below (${pi.pages_below.toFixed(1)} pages) - scroll to see more ...`
|
||||||
|
: '[End of page]'
|
||||||
|
|
||||||
|
return { url, title, header, content, footer }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======= DOM Tree Operations =======
|
// ======= DOM Tree Operations =======
|
||||||
|
|||||||
@@ -287,12 +287,52 @@ export async function scrollVertically(
|
|||||||
(document.documentElement as HTMLElement)
|
(document.documentElement as HTMLElement)
|
||||||
|
|
||||||
if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
|
if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
|
||||||
|
// Page-level scroll
|
||||||
|
const scrollBefore = window.scrollY
|
||||||
|
const scrollMax = document.documentElement.scrollHeight - window.innerHeight
|
||||||
|
|
||||||
window.scrollBy(0, dy)
|
window.scrollBy(0, dy)
|
||||||
return `✅ Scrolled page by ${dy}px.`
|
|
||||||
|
const scrollAfter = window.scrollY
|
||||||
|
const scrolled = scrollAfter - scrollBefore
|
||||||
|
|
||||||
|
if (Math.abs(scrolled) < 1) {
|
||||||
|
return dy > 0
|
||||||
|
? `⚠️ Already at the bottom of the page, cannot scroll down further.`
|
||||||
|
: `⚠️ Already at the top of the page, cannot scroll up further.`
|
||||||
|
}
|
||||||
|
|
||||||
|
const reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1
|
||||||
|
const reachedTop = dy < 0 && scrollAfter <= 1
|
||||||
|
|
||||||
|
if (reachedBottom) return `✅ Scrolled page by ${scrolled}px. Reached the bottom of the page.`
|
||||||
|
if (reachedTop) return `✅ Scrolled page by ${scrolled}px. Reached the top of the page.`
|
||||||
|
return `✅ Scrolled page by ${scrolled}px.`
|
||||||
} else {
|
} else {
|
||||||
|
// Container scroll
|
||||||
|
const scrollBefore = el!.scrollTop
|
||||||
|
const scrollMax = el!.scrollHeight - el!.clientHeight
|
||||||
|
|
||||||
el!.scrollBy({ top: dy, behavior: 'smooth' })
|
el!.scrollBy({ top: dy, behavior: 'smooth' })
|
||||||
await waitFor(0.1) // Animation playback
|
await waitFor(0.1)
|
||||||
return `✅ Scrolled container (${el!.tagName}) by ${dy}px.`
|
|
||||||
|
const scrollAfter = el!.scrollTop
|
||||||
|
const scrolled = scrollAfter - scrollBefore
|
||||||
|
|
||||||
|
if (Math.abs(scrolled) < 1) {
|
||||||
|
return dy > 0
|
||||||
|
? `⚠️ Already at the bottom of container (${el!.tagName}), cannot scroll down further.`
|
||||||
|
: `⚠️ Already at the top of container (${el!.tagName}), cannot scroll up further.`
|
||||||
|
}
|
||||||
|
|
||||||
|
const reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1
|
||||||
|
const reachedTop = dy < 0 && scrollAfter <= 1
|
||||||
|
|
||||||
|
if (reachedBottom)
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the bottom.`
|
||||||
|
if (reachedTop)
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the top.`
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) by ${scrolled}px.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,11 +450,52 @@ export async function scrollHorizontally(
|
|||||||
(document.documentElement as HTMLElement)
|
(document.documentElement as HTMLElement)
|
||||||
|
|
||||||
if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
|
if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
|
||||||
|
// Page-level scroll
|
||||||
|
const scrollBefore = window.scrollX
|
||||||
|
const scrollMax = document.documentElement.scrollWidth - window.innerWidth
|
||||||
|
|
||||||
window.scrollBy(dx, 0)
|
window.scrollBy(dx, 0)
|
||||||
return `✅ Scrolled page horizontally by ${dx}px`
|
|
||||||
|
const scrollAfter = window.scrollX
|
||||||
|
const scrolled = scrollAfter - scrollBefore
|
||||||
|
|
||||||
|
if (Math.abs(scrolled) < 1) {
|
||||||
|
return dx > 0
|
||||||
|
? `⚠️ Already at the right edge of the page, cannot scroll right further.`
|
||||||
|
: `⚠️ Already at the left edge of the page, cannot scroll left further.`
|
||||||
|
}
|
||||||
|
|
||||||
|
const reachedRight = dx > 0 && scrollAfter >= scrollMax - 1
|
||||||
|
const reachedLeft = dx < 0 && scrollAfter <= 1
|
||||||
|
|
||||||
|
if (reachedRight)
|
||||||
|
return `✅ Scrolled page by ${scrolled}px. Reached the right edge of the page.`
|
||||||
|
if (reachedLeft) return `✅ Scrolled page by ${scrolled}px. Reached the left edge of the page.`
|
||||||
|
return `✅ Scrolled page horizontally by ${scrolled}px.`
|
||||||
} else {
|
} else {
|
||||||
|
// Container scroll
|
||||||
|
const scrollBefore = el!.scrollLeft
|
||||||
|
const scrollMax = el!.scrollWidth - el!.clientWidth
|
||||||
|
|
||||||
el!.scrollBy({ left: dx, behavior: 'smooth' })
|
el!.scrollBy({ left: dx, behavior: 'smooth' })
|
||||||
await waitFor(0.1) // Animation playback
|
await waitFor(0.1)
|
||||||
return `✅ Scrolled container (${el!.tagName}) horizontally by ${dx}px`
|
|
||||||
|
const scrollAfter = el!.scrollLeft
|
||||||
|
const scrolled = scrollAfter - scrollBefore
|
||||||
|
|
||||||
|
if (Math.abs(scrolled) < 1) {
|
||||||
|
return dx > 0
|
||||||
|
? `⚠️ Already at the right edge of container (${el!.tagName}), cannot scroll right further.`
|
||||||
|
: `⚠️ Already at the left edge of container (${el!.tagName}), cannot scroll left further.`
|
||||||
|
}
|
||||||
|
|
||||||
|
const reachedRight = dx > 0 && scrollAfter >= scrollMax - 1
|
||||||
|
const reachedLeft = dx < 0 && scrollAfter <= 1
|
||||||
|
|
||||||
|
if (reachedRight)
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the right edge.`
|
||||||
|
if (reachedLeft)
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the left edge.`
|
||||||
|
return `✅ Scrolled container (${el!.tagName}) horizontally by ${scrolled}px.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export default defineConfig({
|
|||||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: ['@page-agent/*', 'ai-motion'],
|
external: ['@page-agent/*', 'ai-motion'],
|
||||||
|
onwarn: function (message, handler) {
|
||||||
|
if (message.code === 'EVAL') return
|
||||||
|
handler(message)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
minify: false,
|
minify: false,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ui",
|
"name": "@page-agent/ui",
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"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",
|
||||||
|
|||||||
@@ -209,18 +209,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pauseButton {
|
|
||||||
font-weight: 600;
|
|
||||||
&.paused {
|
|
||||||
background: rgba(34, 197, 94, 0.2); /* 绿色背景表示可以继续 */
|
|
||||||
color: rgb(34, 197, 94);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(34, 197, 94, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stopButton {
|
.stopButton {
|
||||||
background: rgba(239, 68, 68, 0.2);
|
background: rgba(239, 68, 68, 0.2);
|
||||||
color: rgb(255, 41, 41);
|
color: rgb(255, 41, 41);
|
||||||
@@ -364,6 +352,11 @@
|
|||||||
background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));
|
background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.observation {
|
||||||
|
border-left-color: rgb(147, 51, 234);
|
||||||
|
background: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));
|
||||||
|
}
|
||||||
|
|
||||||
/* 突出显示 done 成功结果 */
|
/* 突出显示 done 成功结果 */
|
||||||
&.doneSuccess {
|
&.doneSuccess {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
|
|||||||
+10
-55
@@ -11,8 +11,6 @@ export interface PanelConfig {
|
|||||||
language?: SupportedLanguage
|
language?: SupportedLanguage
|
||||||
onExecuteTask: (task: string) => void
|
onExecuteTask: (task: string) => void
|
||||||
onStop: () => void
|
onStop: () => void
|
||||||
onPauseToggle: () => boolean // returns new paused state
|
|
||||||
getPaused: () => boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +27,7 @@ export type PanelUpdate =
|
|||||||
| { type: 'completed' }
|
| { type: 'completed' }
|
||||||
| { type: 'toolExecuting'; toolName: string; args: any }
|
| { type: 'toolExecuting'; toolName: string; args: any }
|
||||||
| { type: 'toolCompleted'; toolName: string; args: any; result?: string; duration?: number }
|
| { type: 'toolCompleted'; toolName: string; args: any; result?: string; duration?: number }
|
||||||
|
| { type: 'observation'; content: string }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent control panel
|
* Agent control panel
|
||||||
@@ -39,7 +38,6 @@ export class Panel {
|
|||||||
#statusText: HTMLElement
|
#statusText: HTMLElement
|
||||||
#historySection: HTMLElement
|
#historySection: HTMLElement
|
||||||
#expandButton: HTMLElement
|
#expandButton: HTMLElement
|
||||||
#pauseButton: HTMLElement
|
|
||||||
#stopButton: HTMLElement
|
#stopButton: HTMLElement
|
||||||
#inputSection: HTMLElement
|
#inputSection: HTMLElement
|
||||||
#taskInput: HTMLInputElement
|
#taskInput: HTMLInputElement
|
||||||
@@ -66,7 +64,6 @@ export class Panel {
|
|||||||
this.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!
|
this.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!
|
||||||
this.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!
|
this.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!
|
||||||
this.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!
|
this.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!
|
||||||
this.#pauseButton = this.#wrapper.querySelector(`.${styles.pauseButton}`)!
|
|
||||||
this.#stopButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!
|
this.#stopButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!
|
||||||
this.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!
|
this.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!
|
||||||
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
||||||
@@ -75,6 +72,8 @@ export class Panel {
|
|||||||
this.#startHeaderUpdateLoop()
|
this.#startHeaderUpdateLoop()
|
||||||
|
|
||||||
this.#showInputArea()
|
this.#showInputArea()
|
||||||
|
|
||||||
|
this.hide() // Start hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,11 +119,6 @@ export class Panel {
|
|||||||
this.#updateStatusIndicator('thinking')
|
this.#updateStatusIndicator('thinking')
|
||||||
this.#updateHistory()
|
this.#updateHistory()
|
||||||
this.#collapse()
|
this.#collapse()
|
||||||
// Reset pause state via callback
|
|
||||||
if (this.#config.getPaused()) {
|
|
||||||
this.#config.onPauseToggle()
|
|
||||||
}
|
|
||||||
this.#updatePauseButton()
|
|
||||||
// Reset user input state
|
// Reset user input state
|
||||||
this.#isWaitingForUserAnswer = false
|
this.#isWaitingForUserAnswer = false
|
||||||
this.#userAnswerResolver = null
|
this.#userAnswerResolver = null
|
||||||
@@ -205,6 +199,8 @@ export class Panel {
|
|||||||
duration: data.duration,
|
duration: data.duration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'observation':
|
||||||
|
return { type: 'observation', displayText: data.content }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,39 +273,6 @@ export class Panel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle pause state
|
|
||||||
*/
|
|
||||||
#togglePause(): void {
|
|
||||||
const paused = this.#config.onPauseToggle()
|
|
||||||
this.#updatePauseButton()
|
|
||||||
|
|
||||||
// Update status display
|
|
||||||
if (paused) {
|
|
||||||
this.#statusText.textContent = this.#i18n.t('ui.panel.paused')
|
|
||||||
this.#updateStatusIndicator('thinking')
|
|
||||||
} else {
|
|
||||||
this.#statusText.textContent = this.#i18n.t('ui.panel.continueExecution')
|
|
||||||
this.#updateStatusIndicator('tool_executing')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update pause button state
|
|
||||||
*/
|
|
||||||
#updatePauseButton(): void {
|
|
||||||
const paused = this.#config.getPaused()
|
|
||||||
if (paused) {
|
|
||||||
this.#pauseButton.textContent = '▶'
|
|
||||||
this.#pauseButton.title = this.#i18n.t('ui.panel.continue')
|
|
||||||
this.#pauseButton.classList.add(styles.paused)
|
|
||||||
} else {
|
|
||||||
this.#pauseButton.textContent = '⏸︎'
|
|
||||||
this.#pauseButton.title = this.#i18n.t('ui.panel.pause')
|
|
||||||
this.#pauseButton.classList.remove(styles.paused)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop Agent
|
* Stop Agent
|
||||||
*/
|
*/
|
||||||
@@ -401,7 +364,7 @@ export class Panel {
|
|||||||
#createWrapper(): HTMLElement {
|
#createWrapper(): HTMLElement {
|
||||||
const wrapper = document.createElement('div')
|
const wrapper = document.createElement('div')
|
||||||
wrapper.id = 'page-agent-runtime_agent-panel'
|
wrapper.id = 'page-agent-runtime_agent-panel'
|
||||||
wrapper.className = `${styles.wrapper} ${styles.collapsed}`
|
wrapper.className = styles.wrapper
|
||||||
wrapper.setAttribute('data-browser-use-ignore', 'true')
|
wrapper.setAttribute('data-browser-use-ignore', 'true')
|
||||||
|
|
||||||
wrapper.innerHTML = `
|
wrapper.innerHTML = `
|
||||||
@@ -426,9 +389,6 @@ export class Panel {
|
|||||||
<button class="${styles.controlButton} ${styles.expandButton}" title="${this.#i18n.t('ui.panel.expand')}">
|
<button class="${styles.controlButton} ${styles.expandButton}" title="${this.#i18n.t('ui.panel.expand')}">
|
||||||
▼
|
▼
|
||||||
</button>
|
</button>
|
||||||
<button class="${styles.controlButton} ${styles.pauseButton}" title="${this.#i18n.t('ui.panel.pause')}">
|
|
||||||
⏸︎
|
|
||||||
</button>
|
|
||||||
<button class="${styles.controlButton} ${styles.stopButton}" title="${this.#i18n.t('ui.panel.stop')}">
|
<button class="${styles.controlButton} ${styles.stopButton}" title="${this.#i18n.t('ui.panel.stop')}">
|
||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
@@ -466,12 +426,6 @@ export class Panel {
|
|||||||
this.#toggle()
|
this.#toggle()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Pause/continue button
|
|
||||||
this.#pauseButton.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.#togglePause()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Stop button
|
// Stop button
|
||||||
this.#stopButton.addEventListener('click', (e) => {
|
this.#stopButton.addEventListener('click', (e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -503,7 +457,6 @@ export class Panel {
|
|||||||
|
|
||||||
#expand(): void {
|
#expand(): void {
|
||||||
this.#isExpanded = true
|
this.#isExpanded = true
|
||||||
this.wrapper.classList.remove(styles.collapsed)
|
|
||||||
this.wrapper.classList.add(styles.expanded)
|
this.wrapper.classList.add(styles.expanded)
|
||||||
this.#expandButton.textContent = '▲'
|
this.#expandButton.textContent = '▲'
|
||||||
}
|
}
|
||||||
@@ -511,7 +464,6 @@ export class Panel {
|
|||||||
#collapse(): void {
|
#collapse(): void {
|
||||||
this.#isExpanded = false
|
this.#isExpanded = false
|
||||||
this.wrapper.classList.remove(styles.expanded)
|
this.wrapper.classList.remove(styles.expanded)
|
||||||
this.wrapper.classList.add(styles.collapsed)
|
|
||||||
this.#expandButton.textContent = '▼'
|
this.#expandButton.textContent = '▼'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +584,7 @@ export class Panel {
|
|||||||
typeClass = styles.error
|
typeClass = styles.error
|
||||||
statusIcon = '❌'
|
statusIcon = '❌'
|
||||||
} else if (step.type === 'tool_executing') {
|
} else if (step.type === 'tool_executing') {
|
||||||
statusIcon = '⚙️'
|
statusIcon = '🔨'
|
||||||
} else if (step.type === 'output') {
|
} else if (step.type === 'output') {
|
||||||
typeClass = styles.output
|
typeClass = styles.output
|
||||||
statusIcon = '🤖'
|
statusIcon = '🤖'
|
||||||
@@ -642,6 +594,9 @@ export class Panel {
|
|||||||
} else if (step.type === 'retry') {
|
} else if (step.type === 'retry') {
|
||||||
typeClass = styles.retry
|
typeClass = styles.retry
|
||||||
statusIcon = '🔄'
|
statusIcon = '🔄'
|
||||||
|
} else if (step.type === 'observation') {
|
||||||
|
typeClass = styles.observation
|
||||||
|
statusIcon = '👁️'
|
||||||
} else {
|
} else {
|
||||||
statusIcon = '🧠'
|
statusIcon = '🧠'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,15 @@ export interface Step {
|
|||||||
id: string
|
id: string
|
||||||
stepNumber: number
|
stepNumber: number
|
||||||
timestamp: Date
|
timestamp: Date
|
||||||
type: 'thinking' | 'tool_executing' | 'completed' | 'error' | 'output' | 'input' | 'retry'
|
type:
|
||||||
|
| 'thinking'
|
||||||
|
| 'tool_executing'
|
||||||
|
| 'completed'
|
||||||
|
| 'error'
|
||||||
|
| 'output'
|
||||||
|
| 'input'
|
||||||
|
| 'retry'
|
||||||
|
| 'observation'
|
||||||
|
|
||||||
// Tool execution related
|
// Tool execution related
|
||||||
toolName?: string
|
toolName?: string
|
||||||
@@ -18,7 +26,7 @@ export interface Step {
|
|||||||
duration?: number
|
duration?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AgentStatus = 'idle' | 'running' | 'paused' | 'completed' | 'error'
|
export type AgentStatus = 'idle' | 'running' | 'completed' | 'error'
|
||||||
|
|
||||||
export class UIState {
|
export class UIState {
|
||||||
private steps: Step[] = []
|
private steps: Step[] = []
|
||||||
|
|||||||
@@ -4,17 +4,13 @@ const enUS = {
|
|||||||
panel: {
|
panel: {
|
||||||
ready: 'Ready',
|
ready: 'Ready',
|
||||||
thinking: 'Thinking...',
|
thinking: 'Thinking...',
|
||||||
paused: 'Paused',
|
|
||||||
taskInput: 'Enter new task, describe steps in detail, press Enter to submit',
|
taskInput: 'Enter new task, describe steps in detail, press Enter to submit',
|
||||||
userAnswerPrompt: 'Please answer the question above, press Enter to submit',
|
userAnswerPrompt: 'Please answer the question above, press Enter to submit',
|
||||||
taskTerminated: 'Task terminated',
|
taskTerminated: 'Task terminated',
|
||||||
taskCompleted: 'Task completed',
|
taskCompleted: 'Task completed',
|
||||||
continueExecution: 'Continue execution',
|
|
||||||
userAnswer: 'User answer: {{input}}',
|
userAnswer: 'User answer: {{input}}',
|
||||||
question: 'Question: {{question}}',
|
question: 'Question: {{question}}',
|
||||||
waitingPlaceholder: 'Waiting for task to start...',
|
waitingPlaceholder: 'Waiting for task to start...',
|
||||||
pause: 'Pause',
|
|
||||||
continue: 'Continue',
|
|
||||||
stop: 'Stop',
|
stop: 'Stop',
|
||||||
expand: 'Expand history',
|
expand: 'Expand history',
|
||||||
collapse: 'Collapse history',
|
collapse: 'Collapse history',
|
||||||
@@ -54,17 +50,13 @@ const zhCN = {
|
|||||||
panel: {
|
panel: {
|
||||||
ready: '准备就绪',
|
ready: '准备就绪',
|
||||||
thinking: '正在思考...',
|
thinking: '正在思考...',
|
||||||
paused: '暂停中,稍后',
|
|
||||||
taskInput: '输入新任务,详细描述步骤,回车提交',
|
taskInput: '输入新任务,详细描述步骤,回车提交',
|
||||||
userAnswerPrompt: '请回答上面问题,回车提交',
|
userAnswerPrompt: '请回答上面问题,回车提交',
|
||||||
taskTerminated: '任务已终止',
|
taskTerminated: '任务已终止',
|
||||||
taskCompleted: '任务结束',
|
taskCompleted: '任务结束',
|
||||||
continueExecution: '继续执行',
|
|
||||||
userAnswer: '用户回答: {{input}}',
|
userAnswer: '用户回答: {{input}}',
|
||||||
question: '询问: {{question}}',
|
question: '询问: {{question}}',
|
||||||
waitingPlaceholder: '等待任务开始...',
|
waitingPlaceholder: '等待任务开始...',
|
||||||
pause: '暂停',
|
|
||||||
continue: '继续',
|
|
||||||
stop: '终止',
|
stop: '终止',
|
||||||
expand: '展开历史',
|
expand: '展开历史',
|
||||||
collapse: '收起历史',
|
collapse: '收起历史',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/website",
|
"name": "@page-agent/website",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 0.0.0.0",
|
"dev": "vite --host 0.0.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
||||||
import { Bot, Box, MessageSquare, PlayCircle, Shield, Sparkles, Users, Zap } from 'lucide-react'
|
import { Bot, Box, MessageSquare, PlayCircle, Shield, Sparkles, Users, Zap } from 'lucide-react'
|
||||||
import { PageAgent } from 'page-agent'
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Link, useSearchParams } from 'wouter'
|
import { Link, useSearchParams } from 'wouter'
|
||||||
@@ -59,13 +58,12 @@ export default function HomePage() {
|
|||||||
const handleExecute = async () => {
|
const handleExecute = async () => {
|
||||||
if (!task.trim()) return
|
if (!task.trim()) return
|
||||||
|
|
||||||
let pageAgent: PageAgent
|
|
||||||
const win = window as any
|
const win = window as any
|
||||||
|
|
||||||
if (win.pageAgent && !win.pageAgent.disposed) {
|
// Lazy load PageAgent only when user clicks execute
|
||||||
pageAgent = win.pageAgent
|
if (!win.pageAgent || win.pageAgent.disposed) {
|
||||||
} else {
|
const { PageAgent } = await import('page-agent')
|
||||||
pageAgent = new PageAgent({
|
win.pageAgent = new PageAgent({
|
||||||
// 把 react 根元素排除掉,挂了很多冒泡时间导致假阳
|
// 把 react 根元素排除掉,挂了很多冒泡时间导致假阳
|
||||||
interactiveBlacklist: [document.getElementById('root')!],
|
interactiveBlacklist: [document.getElementById('root')!],
|
||||||
language: i18n.language as any,
|
language: i18n.language as any,
|
||||||
@@ -92,11 +90,9 @@ export default function HomePage() {
|
|||||||
? import.meta.env.LLM_API_KEY
|
? import.meta.env.LLM_API_KEY
|
||||||
: DEMO_API_KEY,
|
: DEMO_API_KEY,
|
||||||
})
|
})
|
||||||
win.pageAgent = pageAgent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await pageAgent.execute(task)
|
const result = await win.pageAgent.execute(task)
|
||||||
|
|
||||||
console.log(result)
|
console.log(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
-122
@@ -1,148 +1,123 @@
|
|||||||
|
import { Suspense, lazy } from 'react'
|
||||||
import { Route, Switch } from 'wouter'
|
import { Route, Switch } from 'wouter'
|
||||||
|
|
||||||
import Header from './components/Header'
|
import Header from './components/Header'
|
||||||
import HomePage from './pages/Home'
|
|
||||||
import DocsLayout from './pages/docs/Layout'
|
import DocsLayout from './pages/docs/Layout'
|
||||||
// Features pages
|
|
||||||
import Instructions from './pages/docs/features/custom-instructions/page'
|
// Lazy load pages
|
||||||
import CustomTools from './pages/docs/features/custom-tools/page'
|
const HomePage = lazy(() => import('./pages/Home'))
|
||||||
import DataMasking from './pages/docs/features/data-masking/page'
|
// Introduction
|
||||||
import Models from './pages/docs/features/models/page'
|
const Overview = lazy(() => import('./pages/docs/introduction/overview/page'))
|
||||||
// Integration pages
|
const QuickStart = lazy(() => import('./pages/docs/introduction/quick-start/page'))
|
||||||
import BestPractices from './pages/docs/integration/best-practices/page'
|
const Limitations = lazy(() => import('./pages/docs/introduction/limitations/page'))
|
||||||
import CdnSetup from './pages/docs/integration/cdn-setup/page'
|
// Features
|
||||||
import Configuration from './pages/docs/integration/configuration/page'
|
const CustomTools = lazy(() => import('./pages/docs/features/custom-tools/page'))
|
||||||
import SecurityPermissions from './pages/docs/integration/security-permissions/page'
|
const DataMasking = lazy(() => import('./pages/docs/features/data-masking/page'))
|
||||||
import ThirdPartyAgent from './pages/docs/integration/third-party-agent/page'
|
const Instructions = lazy(() => import('./pages/docs/features/custom-instructions/page'))
|
||||||
// Introduction pages
|
const Models = lazy(() => import('./pages/docs/features/models/page'))
|
||||||
import Limitations from './pages/docs/introduction/limitations/page'
|
// Integration
|
||||||
import Overview from './pages/docs/introduction/overview/page'
|
const CdnSetup = lazy(() => import('./pages/docs/integration/cdn-setup/page'))
|
||||||
import QuickStart from './pages/docs/introduction/quick-start/page'
|
const SecurityPermissions = lazy(() => import('./pages/docs/integration/security-permissions/page'))
|
||||||
|
const Configuration = lazy(() => import('./pages/docs/integration/configuration/page'))
|
||||||
|
const BestPractices = lazy(() => import('./pages/docs/integration/best-practices/page'))
|
||||||
|
const ThirdPartyAgent = lazy(() => import('./pages/docs/integration/third-party-agent/page'))
|
||||||
|
|
||||||
|
function DocsPage({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white dark:bg-gray-900">
|
||||||
|
<Header />
|
||||||
|
<DocsLayout>
|
||||||
|
<Suspense>{children}</Suspense>
|
||||||
|
</DocsLayout>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Suspense>
|
||||||
{/* Home page */}
|
<Switch>
|
||||||
<Route path="/" component={HomePage} />
|
{/* Home page */}
|
||||||
|
<Route path="/">
|
||||||
|
<HomePage />
|
||||||
|
</Route>
|
||||||
|
|
||||||
{/* Documentation pages with layout */}
|
{/* Introduction */}
|
||||||
<Route path="/docs/introduction/overview">
|
<Route path="/docs/introduction/overview">
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
<DocsPage>
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<Overview />
|
<Overview />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/introduction/quick-start">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/introduction/quick-start">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<QuickStart />
|
<QuickStart />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/introduction/limitations">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/introduction/limitations">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<Limitations />
|
<Limitations />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path="/docs/features/custom-tools">
|
{/* Features */}
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
<Route path="/docs/features/custom-tools">
|
||||||
<Header />
|
<DocsPage>
|
||||||
<DocsLayout>
|
|
||||||
<CustomTools />
|
<CustomTools />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/features/data-masking">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/features/data-masking">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<DataMasking />
|
<DataMasking />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/features/custom-instructions">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/features/custom-instructions">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<Instructions />
|
<Instructions />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/features/models">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/features/models">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<Models />
|
<Models />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path="/docs/integration/cdn-setup">
|
{/* Integration */}
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
<Route path="/docs/integration/cdn-setup">
|
||||||
<Header />
|
<DocsPage>
|
||||||
<DocsLayout>
|
|
||||||
<CdnSetup />
|
<CdnSetup />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/integration/security-permissions">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/integration/security-permissions">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<SecurityPermissions />
|
<SecurityPermissions />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/integration/configuration">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/integration/configuration">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<Configuration />
|
<Configuration />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/integration/best-practices">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/integration/best-practices">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<BestPractices />
|
<BestPractices />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
<Route path="/docs/integration/third-party-agent">
|
||||||
|
<DocsPage>
|
||||||
<Route path="/docs/integration/third-party-agent">
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
|
||||||
<Header />
|
|
||||||
<DocsLayout>
|
|
||||||
<ThirdPartyAgent />
|
<ThirdPartyAgent />
|
||||||
</DocsLayout>
|
</DocsPage>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
|
||||||
|
|
||||||
{/* 404 page */}
|
{/* 404 */}
|
||||||
<Route>
|
<Route>
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900 flex items-center justify-center">
|
<div className="min-h-screen bg-white dark:bg-gray-900 flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">404</h1>
|
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">404</h1>
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300">页面未找到</p>
|
<p className="text-xl text-gray-600 dark:text-gray-300">页面未找到</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
</Switch>
|
||||||
</Switch>
|
</Suspense>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,20 @@ const pageAgentPkg = JSON.parse(
|
|||||||
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
||||||
|
|
||||||
// Website Config (React Documentation Site)
|
// Website Config (React Documentation Site)
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => ({
|
||||||
base: './',
|
base: './',
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss()],
|
||||||
|
build: {
|
||||||
|
chunkSizeWarningLimit: 2000,
|
||||||
|
cssCodeSplit: true,
|
||||||
|
rollupOptions: {
|
||||||
|
onwarn: function (message, handler) {
|
||||||
|
if (message.code === 'EVAL') return
|
||||||
|
handler(message)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
// Self root
|
// Self root
|
||||||
@@ -33,9 +43,11 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'import.meta.env.LLM_MODEL_NAME': JSON.stringify(process.env.LLM_MODEL_NAME),
|
...(mode === 'development' && {
|
||||||
'import.meta.env.LLM_API_KEY': JSON.stringify(process.env.LLM_API_KEY),
|
'import.meta.env.LLM_MODEL_NAME': JSON.stringify(process.env.LLM_MODEL_NAME),
|
||||||
'import.meta.env.LLM_BASE_URL': JSON.stringify(process.env.LLM_BASE_URL),
|
'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),
|
'import.meta.env.VERSION': JSON.stringify(pageAgentPkg.version),
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user