Compare commits
16 Commits
v1.1.0
...
EXT_v0.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bd4a47d35 | |||
| 71ca554108 | |||
| 648a0c1bda | |||
| 1a0422f99d | |||
| 2f72c677a0 | |||
| 18019d390a | |||
| bb3605b3df | |||
| 2b3bb8ab9e | |||
| 7a09bc559d | |||
| a834d37f28 | |||
| 0c3be817c9 | |||
| b8999fdb7c | |||
| 9ba95e96b6 | |||
| 4459026297 | |||
| 98d3016395 | |||
| 15ea488719 |
Generated
+494
-771
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -42,13 +42,14 @@
|
|||||||
"@microsoft/api-extractor": "^7.55.2",
|
"@microsoft/api-extractor": "^7.55.2",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.0.9",
|
"@types/node": "^25.0.9",
|
||||||
|
"chalk": "^5.6.2",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-react-dom": "^2.7.2",
|
"eslint-plugin-react-dom": "^2.7.2",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.26",
|
"eslint-plugin-react-refresh": "^0.5.0",
|
||||||
"eslint-plugin-react-x": "^2.7.2",
|
"eslint-plugin-react-x": "^2.7.2",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.0.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
|
|||||||
@@ -417,10 +417,8 @@ export class PageAgentCore extends EventTarget {
|
|||||||
return this.config.customSystemPrompt
|
return this.config.customSystemPrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
let systemPrompt = SYSTEM_PROMPT
|
|
||||||
|
|
||||||
const targetLanguage = this.config.language === 'zh-CN' ? '中文' : 'English'
|
const targetLanguage = this.config.language === 'zh-CN' ? '中文' : 'English'
|
||||||
systemPrompt = systemPrompt.replace(
|
const systemPrompt = SYSTEM_PROMPT.replace(
|
||||||
/Default working language: \*\*.*?\*\*/,
|
/Default working language: \*\*.*?\*\*/,
|
||||||
`Default working language: **${targetLanguage}**`
|
`Default working language: **${targetLanguage}**`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ You excel at following tasks:
|
|||||||
</intro>
|
</intro>
|
||||||
|
|
||||||
<language_settings>
|
<language_settings>
|
||||||
- Default working language: **中文**
|
- Default working language: **English**
|
||||||
- Use the language that user is using. Return in user's language.
|
- Use the language that user is using. Return in user's language.
|
||||||
</language_settings>
|
</language_settings>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,291 @@
|
|||||||
|
# Page Agent Extension API
|
||||||
|
|
||||||
|
This document describes how to integrate the Page Agent browser extension into your web application.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Install the browser extension
|
||||||
|
|
||||||
|
Install the Page Agent extension from the Chrome Web Store.
|
||||||
|
|
||||||
|
### 2. Install type definitions (recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @page-agent/core --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Set up authentication
|
||||||
|
|
||||||
|
The extension only injects APIs when it detects a valid token in `localStorage`.
|
||||||
|
|
||||||
|
1. Open the extension's side panel to get your authorization token
|
||||||
|
2. Set the token in your page:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
localStorage.setItem('PageAgentExtUserAuthToken', 'your-token')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
// Wait for extension injection (up to 1 second)
|
||||||
|
async function waitForExtension(timeout = 1000): Promise<boolean> {
|
||||||
|
const start = Date.now()
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
if (window.PAGE_AGENT_EXT) return true
|
||||||
|
await new Promise((r) => setTimeout(r, 100))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
if (await waitForExtension()) {
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
onStatusChange: (status) => console.log('Status:', status),
|
||||||
|
onActivity: (activity) => console.log('Activity:', activity),
|
||||||
|
})
|
||||||
|
console.log('Result:', result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global API
|
||||||
|
|
||||||
|
The extension injects the following APIs into the `window` object:
|
||||||
|
|
||||||
|
### `window.PAGE_AGENT_EXT_VERSION`
|
||||||
|
|
||||||
|
Extension version string (e.g., `"1.0.0"`). This is exposed separately to allow version checking before accessing the main API object.
|
||||||
|
|
||||||
|
### `window.PAGE_AGENT_EXT`
|
||||||
|
|
||||||
|
Main API namespace object containing:
|
||||||
|
|
||||||
|
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||||
|
|
||||||
|
Execute an agent task.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
| Name | Type | Required | Description |
|
||||||
|
|------|------|----------|-------------|
|
||||||
|
| `task` | `string` | Yes | Task description |
|
||||||
|
| `config` | `ExecuteConfig` | Yes | Execution configuration (LLM settings, options, and event callbacks) |
|
||||||
|
|
||||||
|
**Returns:** `Promise<ExecutionResult>`
|
||||||
|
|
||||||
|
#### `PAGE_AGENT_EXT.dispose()`
|
||||||
|
|
||||||
|
Stop and destroy the current running agent.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
Install `@page-agent/core` for full type definitions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
export interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to include the initial tab (that holds this main world script) in the task.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
|
||||||
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
onActivity?: (activity: AgentActivity) => void
|
||||||
|
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||||
|
onDispose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
|
```
|
||||||
|
|
||||||
|
### AgentStatus
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type AgentStatus = 'idle' | 'running' | 'completed' | 'error'
|
||||||
|
```
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `idle` | Agent is idle, ready to execute |
|
||||||
|
| `running` | Agent is executing a task |
|
||||||
|
| `completed` | Task completed successfully |
|
||||||
|
| `error` | Task failed with an error |
|
||||||
|
|
||||||
|
### AgentActivity
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type AgentActivity =
|
||||||
|
| { type: 'thinking' }
|
||||||
|
| { type: 'executing'; tool: string; input: unknown }
|
||||||
|
| { type: 'executed'; tool: string; input: unknown; output: string; duration: number }
|
||||||
|
| { type: 'retrying'; attempt: number; maxAttempts: number }
|
||||||
|
| { type: 'error'; message: string }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `thinking` | Agent is analyzing the page and planning |
|
||||||
|
| `executing` | Agent is executing a tool action |
|
||||||
|
| `executed` | Tool execution completed |
|
||||||
|
| `retrying` | Retrying after a failure |
|
||||||
|
| `error` | An error occurred |
|
||||||
|
|
||||||
|
### HistoricalEvent
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type HistoricalEvent =
|
||||||
|
| { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }
|
||||||
|
| { type: 'observation'; content: string }
|
||||||
|
| { type: 'user_takeover' }
|
||||||
|
| { type: 'retry'; message: string; attempt: number; maxAttempts: number }
|
||||||
|
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExecutionResult
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ExecutionResult {
|
||||||
|
success: boolean
|
||||||
|
data: string
|
||||||
|
history: HistoricalEvent[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Execution
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||||
|
'Fill in the email field with test@example.com and click Submit',
|
||||||
|
{
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('Task completed:', result.data)
|
||||||
|
} else {
|
||||||
|
console.error('Task failed')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exclude Initial Tab
|
||||||
|
|
||||||
|
By default, the agent includes the initial tab (where the script runs) in the task. Set `includeInitialTab: false` to exclude it:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||||
|
'Open a new tab and search for page-agent on GitHub',
|
||||||
|
{
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
includeInitialTab: false, // Agent will open new tabs only
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Event Callbacks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await window.PAGE_AGENT_EXT!.execute('Navigate to the settings page', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
onStatusChange: (status) => {
|
||||||
|
updateUI({ agentStatus: status })
|
||||||
|
},
|
||||||
|
onActivity: (activity) => {
|
||||||
|
switch (activity.type) {
|
||||||
|
case 'thinking':
|
||||||
|
showSpinner('Agent is thinking...')
|
||||||
|
break
|
||||||
|
case 'executing':
|
||||||
|
showSpinner(`Executing: ${activity.tool}`)
|
||||||
|
break
|
||||||
|
case 'executed':
|
||||||
|
log(`${activity.tool} completed in ${activity.duration}ms`)
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
showError(activity.message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHistoryUpdate: (history) => {
|
||||||
|
renderHistory(history)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop Execution
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Start a task
|
||||||
|
window.PAGE_AGENT_EXT!.execute('Scroll through all pages', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Later, stop it
|
||||||
|
window.PAGE_AGENT_EXT!.dispose()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Window Type Declaration
|
||||||
|
|
||||||
|
If not using `@page-agent/core`, add this to your project:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
onActivity?: (activity: AgentActivity) => void
|
||||||
|
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||||
|
onDispose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
PAGE_AGENT_EXT_VERSION?: string
|
||||||
|
PAGE_AGENT_EXT?: {
|
||||||
|
version: string
|
||||||
|
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
|
dispose: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
# Page Agent 浏览器插件 API
|
||||||
|
|
||||||
|
本文档介绍如何在网页应用中接入 Page Agent 浏览器插件。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
### 1. 安装浏览器插件
|
||||||
|
|
||||||
|
从 Chrome 应用商店安装 Page Agent 插件。
|
||||||
|
|
||||||
|
### 2. 安装类型定义(推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @page-agent/core --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置认证
|
||||||
|
|
||||||
|
插件在页面加载后检测 `localStorage` 中的 token,匹配时才会注入 API。
|
||||||
|
|
||||||
|
1. 打开插件的侧边栏面板,获取授权 token
|
||||||
|
2. 在页面中设置 token:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
localStorage.setItem('PageAgentExtUserAuthToken', 'your-token')
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
// 等待插件注入(最多 1 秒)
|
||||||
|
async function waitForExtension(timeout = 1000): Promise<boolean> {
|
||||||
|
const start = Date.now()
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
if (window.PAGE_AGENT_EXT) return true
|
||||||
|
await new Promise((r) => setTimeout(r, 100))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
if (await waitForExtension()) {
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute('点击登录按钮', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
onStatusChange: (status) => console.log('状态:', status),
|
||||||
|
onActivity: (activity) => console.log('活动:', activity),
|
||||||
|
})
|
||||||
|
console.log('结果:', result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 全局 API
|
||||||
|
|
||||||
|
插件在 `window` 对象上注入以下 API:
|
||||||
|
|
||||||
|
### `window.PAGE_AGENT_EXT_VERSION`
|
||||||
|
|
||||||
|
插件版本号字符串(例如 `"1.0.0"`)。单独暴露版本号,方便在访问主 API 对象前进行版本检查。
|
||||||
|
|
||||||
|
### `window.PAGE_AGENT_EXT`
|
||||||
|
|
||||||
|
主 API 命名空间对象,包含:
|
||||||
|
|
||||||
|
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||||
|
|
||||||
|
执行 Agent 任务。
|
||||||
|
|
||||||
|
**参数:**
|
||||||
|
|
||||||
|
| 名称 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| `task` | `string` | 是 | 任务描述 |
|
||||||
|
| `config` | `ExecuteConfig` | 是 | 执行配置(LLM 设置、选项和事件回调) |
|
||||||
|
|
||||||
|
**返回:** `Promise<ExecutionResult>`
|
||||||
|
|
||||||
|
#### `PAGE_AGENT_EXT.dispose()`
|
||||||
|
|
||||||
|
停止并销毁当前运行的 Agent。
|
||||||
|
|
||||||
|
## 类型定义
|
||||||
|
|
||||||
|
安装 `@page-agent/core` 获取完整类型:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
export interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否将初始标签页(运行此脚本的页面)包含在任务中。
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
|
||||||
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
onActivity?: (activity: AgentActivity) => void
|
||||||
|
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||||
|
onDispose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
|
```
|
||||||
|
|
||||||
|
### AgentStatus
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type AgentStatus = 'idle' | 'running' | 'completed' | 'error'
|
||||||
|
```
|
||||||
|
|
||||||
|
| 状态 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `idle` | 空闲,准备执行 |
|
||||||
|
| `running` | 正在执行任务 |
|
||||||
|
| `completed` | 任务成功完成 |
|
||||||
|
| `error` | 任务执行失败 |
|
||||||
|
|
||||||
|
### AgentActivity
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type AgentActivity =
|
||||||
|
| { type: 'thinking' }
|
||||||
|
| { type: 'executing'; tool: string; input: unknown }
|
||||||
|
| { type: 'executed'; tool: string; input: unknown; output: string; duration: number }
|
||||||
|
| { type: 'retrying'; attempt: number; maxAttempts: number }
|
||||||
|
| { type: 'error'; message: string }
|
||||||
|
```
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `thinking` | Agent 正在分析页面并规划 |
|
||||||
|
| `executing` | 正在执行工具操作 |
|
||||||
|
| `executed` | 工具执行完成 |
|
||||||
|
| `retrying` | 失败后重试 |
|
||||||
|
| `error` | 发生错误 |
|
||||||
|
|
||||||
|
### HistoricalEvent
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type HistoricalEvent =
|
||||||
|
| { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }
|
||||||
|
| { type: 'observation'; content: string }
|
||||||
|
| { type: 'user_takeover' }
|
||||||
|
| { type: 'retry'; message: string; attempt: number; maxAttempts: number }
|
||||||
|
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExecutionResult
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ExecutionResult {
|
||||||
|
success: boolean
|
||||||
|
data: string
|
||||||
|
history: HistoricalEvent[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 基础执行
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||||
|
'在邮箱输入框填入 test@example.com 然后点击提交',
|
||||||
|
{
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('任务完成:', result.data)
|
||||||
|
} else {
|
||||||
|
console.error('任务失败')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 排除初始标签页
|
||||||
|
|
||||||
|
默认情况下,Agent 会将初始标签页(运行脚本的页面)包含在任务中。设置 `includeInitialTab: false` 可以排除它:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||||
|
'打开新标签页并在 GitHub 上搜索 page-agent',
|
||||||
|
{
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
includeInitialTab: false, // Agent 只会打开新标签页
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用事件回调
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await window.PAGE_AGENT_EXT!.execute('导航到设置页面', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
onStatusChange: (status) => {
|
||||||
|
updateUI({ agentStatus: status })
|
||||||
|
},
|
||||||
|
onActivity: (activity) => {
|
||||||
|
switch (activity.type) {
|
||||||
|
case 'thinking':
|
||||||
|
showSpinner('Agent 正在思考...')
|
||||||
|
break
|
||||||
|
case 'executing':
|
||||||
|
showSpinner(`正在执行: ${activity.tool}`)
|
||||||
|
break
|
||||||
|
case 'executed':
|
||||||
|
log(`${activity.tool} 完成,耗时 ${activity.duration}ms`)
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
showError(activity.message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHistoryUpdate: (history) => {
|
||||||
|
renderHistory(history)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 停止执行
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 启动任务
|
||||||
|
window.PAGE_AGENT_EXT!.execute('滚动浏览所有页面', {
|
||||||
|
baseURL: 'https://api.openai.com/v1',
|
||||||
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
|
model: 'gpt-5.2',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 稍后停止
|
||||||
|
window.PAGE_AGENT_EXT!.dispose()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Window 类型声明
|
||||||
|
|
||||||
|
如果不使用 `@page-agent/core`,可以添加以下声明:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
AgentActivity,
|
||||||
|
AgentStatus,
|
||||||
|
ExecutionResult,
|
||||||
|
HistoricalEvent,
|
||||||
|
} from '@page-agent/core'
|
||||||
|
|
||||||
|
interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
onActivity?: (activity: AgentActivity) => void
|
||||||
|
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||||
|
onDispose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
PAGE_AGENT_EXT_VERSION?: string
|
||||||
|
PAGE_AGENT_EXT?: {
|
||||||
|
version: string
|
||||||
|
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
|
dispose: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ext",
|
"name": "@page-agent/ext",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
@@ -16,14 +16,10 @@
|
|||||||
"@types/react-dom": "^19.2.1",
|
"@types/react-dom": "^19.2.1",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"@wxt-dev/module-react": "^1.1.5",
|
"@wxt-dev/module-react": "^1.1.5",
|
||||||
"i18next": "^25.7.4",
|
|
||||||
"i18next-browser-languagedetector": "^8.2.0",
|
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-i18next": "^16.5.2",
|
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"wouter": "^3.9.0",
|
|
||||||
"wxt": "^0.20.13"
|
"wxt": "^0.20.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -41,10 +37,10 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.563.0",
|
"lucide-react": "^0.563.0",
|
||||||
"motion": "^12.29.2",
|
"motion": "^12.30.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.5.0",
|
"simple-icons": "^16.7.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0"
|
"tailwind-merge": "^3.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,35 @@ import { TabsController } from './TabsController'
|
|||||||
import SYSTEM_PROMPT from './system_prompt.md?raw'
|
import SYSTEM_PROMPT from './system_prompt.md?raw'
|
||||||
import { createTabTools } from './tabTools'
|
import { createTabTools } from './tabTools'
|
||||||
|
|
||||||
|
/** Detect user language from browser settings */
|
||||||
|
function detectLanguage(): 'en-US' | 'zh-CN' {
|
||||||
|
const lang = navigator.language || navigator.languages?.[0] || 'en-US'
|
||||||
|
return lang.startsWith('zh') ? 'zh-CN' : 'en-US'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MultiPageAgent
|
* MultiPageAgent
|
||||||
* - use with extension
|
* - use with extension
|
||||||
* - can be used from a side panel or a content script
|
* - can be used from a side panel or a content script
|
||||||
*/
|
*/
|
||||||
export class MultiPageAgent extends PageAgentCore {
|
export class MultiPageAgent extends PageAgentCore {
|
||||||
constructor(config: Omit<PageAgentConfig, 'pageController'>) {
|
constructor(config: Omit<PageAgentConfig, 'pageController'> & { includeInitialTab?: boolean }) {
|
||||||
|
// multi page controller
|
||||||
const tabsController = new TabsController()
|
const tabsController = new TabsController()
|
||||||
const pageController = new RemotePageController(tabsController)
|
const pageController = new RemotePageController(tabsController)
|
||||||
const customTools = createTabTools(tabsController)
|
const customTools = createTabTools(tabsController)
|
||||||
|
|
||||||
|
// system prompt - auto-detect language if not specified
|
||||||
|
const language = config.language ?? detectLanguage()
|
||||||
|
const targetLanguage = language === 'zh-CN' ? '中文' : 'English'
|
||||||
|
const systemPrompt = SYSTEM_PROMPT.replace(
|
||||||
|
/Default working language: \*\*.*?\*\*/,
|
||||||
|
`Default working language: **${targetLanguage}**`
|
||||||
|
)
|
||||||
|
|
||||||
|
// include initial tab for controlling
|
||||||
|
const includeInitialTab = config.includeInitialTab ?? true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the agent is in side-panel and user closed the side-panel.
|
* When the agent is in side-panel and user closed the side-panel.
|
||||||
* There is no chance for isAgentRunning to be set false.
|
* There is no chance for isAgentRunning to be set false.
|
||||||
@@ -29,10 +47,10 @@ export class MultiPageAgent extends PageAgentCore {
|
|||||||
...config,
|
...config,
|
||||||
pageController: pageController as any,
|
pageController: pageController as any,
|
||||||
customTools: customTools,
|
customTools: customTools,
|
||||||
customSystemPrompt: SYSTEM_PROMPT,
|
customSystemPrompt: systemPrompt,
|
||||||
|
|
||||||
onBeforeTask: async (agent) => {
|
onBeforeTask: async (agent) => {
|
||||||
await tabsController.init(agent.task)
|
await tabsController.init(agent.task, includeInitialTab)
|
||||||
|
|
||||||
heartBeatInterval = window.setInterval(() => {
|
heartBeatInterval = window.setInterval(() => {
|
||||||
chrome.storage.local.set({
|
chrome.storage.local.set({
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class TabsController extends EventTarget {
|
|||||||
private task: string = ''
|
private task: string = ''
|
||||||
private windowId: number | null = null
|
private windowId: number | null = null
|
||||||
|
|
||||||
async init(task: string) {
|
async init(task: string, includeInitialTab: boolean = true) {
|
||||||
this.task = task
|
this.task = task
|
||||||
this.tabs = []
|
this.tabs = []
|
||||||
this.currentTabId = null
|
this.currentTabId = null
|
||||||
@@ -26,17 +26,19 @@ export class TabsController extends EventTarget {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.initialTabId = result.tabId
|
this.initialTabId = result.tabId
|
||||||
this.currentTabId = result.tabId
|
|
||||||
|
|
||||||
this.tabs.push({
|
|
||||||
id: result.tabId,
|
|
||||||
isInitial: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!this.initialTabId) {
|
if (!this.initialTabId) {
|
||||||
throw new Error('Failed to get initial tab ID')
|
throw new Error('Failed to get initial tab ID')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includeInitialTab) {
|
||||||
|
this.currentTabId = this.initialTabId
|
||||||
|
this.tabs.push({
|
||||||
|
id: result.tabId,
|
||||||
|
isInitial: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
await this.updateCurrentTabId(this.currentTabId)
|
await this.updateCurrentTabId(this.currentTabId)
|
||||||
|
|
||||||
const tabChangeHandler = (message: any): void => {
|
const tabChangeHandler = (message: any): void => {
|
||||||
@@ -230,6 +232,10 @@ export class TabsController extends EventTarget {
|
|||||||
`| ${tab.id} | ${url} | ${title} | ${this.currentTabId === tab.id ? '✅' : ''} |`
|
`| ${tab.id} | ${url} | ${title} | ${this.currentTabId === tab.id ? '✅' : ''} |`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (!this.tabs.length) {
|
||||||
|
summaries.push('\nNo tabs available. Open a tab if needed.')
|
||||||
|
}
|
||||||
|
|
||||||
return summaries.join('\n')
|
return summaries.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ You excel at following tasks:
|
|||||||
</intro>
|
</intro>
|
||||||
|
|
||||||
<language_settings>
|
<language_settings>
|
||||||
- Default working language: **中文**
|
- Default working language: **English**
|
||||||
- Use the language that user is using. Return in user's language.
|
- Use the language that user is using. Return in user's language.
|
||||||
</language_settings>
|
</language_settings>
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export function useAgent(): UseAgentResult {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const configure = useCallback(async (newConfig: LLMConfig) => {
|
const configure = useCallback(async (newConfig: LLMConfig) => {
|
||||||
|
await chrome.storage.local.set({ llmConfig: newConfig })
|
||||||
setConfig(newConfig)
|
setConfig(newConfig)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: oklch(0.145 0 0);
|
--background: oklch(0.19 0 0);
|
||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.145 0 0);
|
--card: oklch(0.145 0 0);
|
||||||
--card-foreground: oklch(0.985 0 0);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ async function exposeAgentToPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { task, llmConfig } = payload
|
const { task, config } = payload
|
||||||
|
|
||||||
// create when used
|
// create when used
|
||||||
|
|
||||||
multiPageAgent = new MultiPageAgent(llmConfig)
|
multiPageAgent = new MultiPageAgent(config)
|
||||||
|
|
||||||
// events
|
// events
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,38 @@
|
|||||||
import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'
|
import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'
|
||||||
import type { LLMConfig } from '@page-agent/llms'
|
|
||||||
|
|
||||||
export interface ExecuteHooks {
|
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
|
|
||||||
|
export interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to include the initial tab (that holds this main world script) in the task.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
|
||||||
onStatusChange?: (status: AgentStatus) => void
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
onActivity?: (activity: AgentActivity) => void
|
onActivity?: (activity: AgentActivity) => void
|
||||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||||
onDispose?: () => void
|
onDispose?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Execute = (
|
|
||||||
task: string,
|
|
||||||
llmConfig: LLMConfig,
|
|
||||||
hooks?: ExecuteHooks
|
|
||||||
) => Promise<ExecutionResult>
|
|
||||||
|
|
||||||
export default defineUnlistedScript(() => {
|
export default defineUnlistedScript(() => {
|
||||||
const w = window as any
|
|
||||||
|
|
||||||
let _lastId = 0
|
let _lastId = 0
|
||||||
function getId() {
|
function getId() {
|
||||||
_lastId += 1
|
_lastId += 1
|
||||||
return _lastId
|
return _lastId
|
||||||
}
|
}
|
||||||
|
|
||||||
w.execute = async (task: string, llmConfig: LLMConfig, hooks?: ExecuteHooks) => {
|
const execute: Execute = async (task, config) => {
|
||||||
if (typeof task !== 'string') throw new Error('Task must be a string')
|
if (typeof task !== 'string') throw new Error('Task must be a string')
|
||||||
if (task.trim().length === 0) throw new Error('Task cannot be empty')
|
if (task.trim().length === 0) throw new Error('Task cannot be empty')
|
||||||
if (!llmConfig) throw new Error('LLM config is required')
|
if (!config) throw new Error('Config is required')
|
||||||
if (!llmConfig.baseURL) throw new Error('LLM config must have a baseURL')
|
if (!config.baseURL) throw new Error('Config must have a baseURL')
|
||||||
if (!llmConfig.apiKey) throw new Error('LLM config must have an apiKey')
|
if (!config.apiKey) throw new Error('Config must have an apiKey')
|
||||||
if (!llmConfig.model) throw new Error('LLM config must have a model')
|
if (!config.model) throw new Error('Config must have a model')
|
||||||
|
|
||||||
const id = getId()
|
const id = getId()
|
||||||
|
|
||||||
@@ -42,30 +45,31 @@ export default defineUnlistedScript(() => {
|
|||||||
|
|
||||||
// events
|
// events
|
||||||
|
|
||||||
if (data.action === 'status_change_event' && hooks?.onStatusChange) {
|
if (data.action === 'status_change_event' && config.onStatusChange) {
|
||||||
hooks.onStatusChange(data.payload)
|
config.onStatusChange(data.payload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.action === 'activity_event' && hooks?.onActivity) {
|
if (data.action === 'activity_event' && config.onActivity) {
|
||||||
hooks.onActivity(data.payload)
|
config.onActivity(data.payload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.action === 'history_change_event' && hooks?.onHistoryUpdate) {
|
if (data.action === 'history_change_event' && config.onHistoryUpdate) {
|
||||||
hooks.onHistoryUpdate(data.payload)
|
config.onHistoryUpdate(data.payload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.action === 'dispose_event' && hooks?.onDispose) {
|
if (data.action === 'dispose_event' && config.onDispose) {
|
||||||
hooks.onDispose()
|
config.onDispose()
|
||||||
|
window.removeEventListener('message', handleMessage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// result
|
|
||||||
|
|
||||||
if (data.action !== 'execute_result') return
|
if (data.action !== 'execute_result') return
|
||||||
|
|
||||||
|
// execute_result
|
||||||
|
|
||||||
window.removeEventListener('message', handleMessage)
|
window.removeEventListener('message', handleMessage)
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
@@ -75,6 +79,7 @@ export default defineUnlistedScript(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @note will be removed on dispose or result
|
||||||
window.addEventListener('message', handleMessage)
|
window.addEventListener('message', handleMessage)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,7 +88,15 @@ export default defineUnlistedScript(() => {
|
|||||||
channel: 'PAGE_AGENT_EXT_REQUEST',
|
channel: 'PAGE_AGENT_EXT_REQUEST',
|
||||||
id,
|
id,
|
||||||
action: 'execute',
|
action: 'execute',
|
||||||
payload: { task, llmConfig },
|
payload: {
|
||||||
|
task,
|
||||||
|
config: {
|
||||||
|
baseURL: config.baseURL,
|
||||||
|
apiKey: config.apiKey,
|
||||||
|
model: config.model,
|
||||||
|
includeInitialTab: config.includeInitialTab,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'*'
|
'*'
|
||||||
)
|
)
|
||||||
@@ -91,7 +104,7 @@ export default defineUnlistedScript(() => {
|
|||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
w.dispose = () => {
|
const dispose = () => {
|
||||||
const id = getId()
|
const id = getId()
|
||||||
|
|
||||||
window.postMessage(
|
window.postMessage(
|
||||||
@@ -103,4 +116,11 @@ export default defineUnlistedScript(() => {
|
|||||||
'*'
|
'*'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
;(window as any).PAGE_AGENT_EXT_VERSION = __EXT_VERSION__
|
||||||
|
;(window as any).PAGE_AGENT_EXT = {
|
||||||
|
version: __EXT_VERSION__,
|
||||||
|
execute,
|
||||||
|
dispose,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function App() {
|
|||||||
}, [history, activity])
|
}, [history, activity])
|
||||||
|
|
||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
(e?: React.FormEvent) => {
|
(e?: React.SyntheticEvent) => {
|
||||||
e?.preventDefault()
|
e?.preventDefault()
|
||||||
if (!inputValue.trim() || status === 'running') return
|
if (!inputValue.trim() || status === 'running') return
|
||||||
|
|
||||||
@@ -82,7 +82,12 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<StatusDot status={status} />
|
<StatusDot status={status} />
|
||||||
<Button variant="ghost" size="icon-sm" onClick={() => setShowConfig(true)}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
onClick={() => setShowConfig(true)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
<Settings className="size-3.5" />
|
<Settings className="size-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,7 +147,7 @@ export default function App() {
|
|||||||
variant="default"
|
variant="default"
|
||||||
onClick={() => handleSubmit()}
|
onClick={() => handleSubmit()}
|
||||||
disabled={!inputValue.trim()}
|
disabled={!inputValue.trim()}
|
||||||
className="size-7"
|
className="size-7 cursor-pointer"
|
||||||
>
|
>
|
||||||
<Send className="size-3" />
|
<Send className="size-3" />
|
||||||
</InputGroupButton>
|
</InputGroupButton>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { LLMConfig } from '@page-agent/llms'
|
import type { LLMConfig } from '@page-agent/llms'
|
||||||
import { Copy, Loader2 } from 'lucide-react'
|
import { Copy, CornerUpLeft, Eye, EyeOff, HatGlasses, Home, Loader2, Scale } from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { siGithub } from 'simple-icons'
|
||||||
|
|
||||||
import { DEMO_API_KEY, DEMO_BASE_URL, DEMO_MODEL } from '@/agent/constants'
|
import { DEMO_API_KEY, DEMO_BASE_URL, DEMO_MODEL } from '@/agent/constants'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -19,6 +20,8 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [userAuthToken, setUserAuthToken] = useState<string>('')
|
const [userAuthToken, setUserAuthToken] = useState<string>('')
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
const [showToken, setShowToken] = useState(false)
|
||||||
|
const [showApiKey, setShowApiKey] = useState(false)
|
||||||
|
|
||||||
// Update local state when config prop changes
|
// Update local state when config prop changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -69,8 +72,18 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 p-4">
|
<div className="flex flex-col gap-4 p-4 relative">
|
||||||
<h2 className="text-base font-semibold">Settings</h2>
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-base font-semibold">Settings</h2>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-2 right-3 cursor-pointer"
|
||||||
|
>
|
||||||
|
<CornerUpLeft className="size-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* User Auth Token Section */}
|
{/* User Auth Token Section */}
|
||||||
<div className="flex flex-col gap-1.5 p-3 bg-muted/50 rounded-md border">
|
<div className="flex flex-col gap-1.5 p-3 bg-muted/50 rounded-md border">
|
||||||
@@ -81,9 +94,24 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Input
|
<Input
|
||||||
readOnly
|
readOnly
|
||||||
value={userAuthToken || 'Loading...'}
|
value={
|
||||||
|
userAuthToken
|
||||||
|
? showToken
|
||||||
|
? userAuthToken
|
||||||
|
: `${userAuthToken.slice(0, 4)}${'•'.repeat(userAuthToken.length - 8)}${userAuthToken.slice(-4)}`
|
||||||
|
: 'Loading...'
|
||||||
|
}
|
||||||
className="text-xs h-8 font-mono bg-background"
|
className="text-xs h-8 font-mono bg-background"
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 cursor-pointer"
|
||||||
|
onClick={() => setShowToken(!showToken)}
|
||||||
|
disabled={!userAuthToken}
|
||||||
|
>
|
||||||
|
{showToken ? <EyeOff className="size-3" /> : <Eye className="size-3" />}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -118,23 +146,98 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
|
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="text-xs text-muted-foreground">API Key</label>
|
<label className="text-xs text-muted-foreground">API Key</label>
|
||||||
<Input
|
<div className="flex gap-2 items-center">
|
||||||
type="password"
|
<Input
|
||||||
placeholder="sk-..."
|
type={showApiKey ? 'text' : 'password'}
|
||||||
value={apiKey}
|
placeholder="sk-..."
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
value={apiKey}
|
||||||
className="text-xs h-8"
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
/>
|
className="text-xs h-8"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 cursor-pointer"
|
||||||
|
onClick={() => setShowApiKey(!showApiKey)}
|
||||||
|
>
|
||||||
|
{showApiKey ? <EyeOff className="size-3" /> : <Eye className="size-3" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 mt-2">
|
<div className="flex gap-2 mt-2">
|
||||||
<Button variant="outline" onClick={onClose} className="flex-1 h-8 text-xs">
|
<Button variant="outline" onClick={onClose} className="flex-1 h-8 text-xs cursor-pointer">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSave} disabled={saving} className="flex-1 h-8 text-xs">
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={saving}
|
||||||
|
className="flex-1 h-8 text-xs cursor-pointer"
|
||||||
|
>
|
||||||
{saving ? <Loader2 className="size-3 animate-spin" /> : 'Save'}
|
{saving ? <Loader2 className="size-3 animate-spin" /> : 'Save'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="mt-4 mb-4 pt-4 border-t border-border/50 flex gap-2 justify-between text-[10px] text-muted-foreground">
|
||||||
|
<div className="flex flex-col justify-between">
|
||||||
|
<a
|
||||||
|
href="https://github.com/alibaba/page-agent"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-1 hover:text-foreground"
|
||||||
|
>
|
||||||
|
<svg role="img" viewBox="0 0 24 24" className="size-3 fill-current">
|
||||||
|
<path d={siGithub.path} />
|
||||||
|
</svg>
|
||||||
|
<span>Source Code</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://alibaba.github.io/page-agent/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-1 hover:text-foreground"
|
||||||
|
>
|
||||||
|
<Home className="size-3" />
|
||||||
|
<span>Home Page</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://github.com/alibaba/page-agent/blob/main/packages/extension/PRIVACY.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-1 hover:text-foreground"
|
||||||
|
>
|
||||||
|
<HatGlasses className="size-3" />
|
||||||
|
<span>Privacy Policy</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
<span>
|
||||||
|
Extension <span className="font-mono">v{__EXT_VERSION__}</span>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
PageAgent <span className="font-mono">v{__CORE_VERSION__}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* attribute */}
|
||||||
|
<div className="text-[10px] text-muted-foreground bg-background fixed bottom-0 w-full flex justify-around">
|
||||||
|
<span className="leading-loose">
|
||||||
|
Built with ♥️ by{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/gaomeng1900"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline hover:text-foreground"
|
||||||
|
>
|
||||||
|
@Simon
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,9 +315,7 @@ export function EventCard({ event }: { event: HistoricalEvent }) {
|
|||||||
<ResultCard
|
<ResultCard
|
||||||
success={input?.success ?? true}
|
success={input?.success ?? true}
|
||||||
text={input?.text || event.action.output || ''}
|
text={input?.text || event.action.output || ''}
|
||||||
>
|
/>
|
||||||
<RawSection rawRequest={event.rawRequest} rawResponse={event.rawResponse} />
|
|
||||||
</ResultCard>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ import { ErrorBoundary } from './components/ErrorBoundary'
|
|||||||
|
|
||||||
import '@/assets/index.css'
|
import '@/assets/index.css'
|
||||||
|
|
||||||
|
// Sync dark mode with system preference
|
||||||
|
const syncDarkMode = () => {
|
||||||
|
document.documentElement.classList.toggle(
|
||||||
|
'dark',
|
||||||
|
matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
)
|
||||||
|
}
|
||||||
|
syncDarkMode()
|
||||||
|
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', syncDarkMode)
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
declare const __EXT_VERSION__: string
|
||||||
|
declare const __CORE_VERSION__: string
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import { mkdirSync } from 'node:fs'
|
import { mkdirSync, readFileSync } from 'node:fs'
|
||||||
import { defineConfig } from 'wxt'
|
import { defineConfig } from 'wxt'
|
||||||
|
|
||||||
const chromeProfile = '.wxt/chrome-data'
|
const chromeProfile = '.wxt/chrome-data'
|
||||||
mkdirSync(chromeProfile, { recursive: true })
|
mkdirSync(chromeProfile, { recursive: true })
|
||||||
|
|
||||||
|
const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'))
|
||||||
|
|
||||||
// See https://wxt.dev/api/config.html
|
// See https://wxt.dev/api/config.html
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
srcDir: 'src',
|
srcDir: 'src',
|
||||||
@@ -16,6 +18,10 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
vite: () => ({
|
vite: () => ({
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
|
define: {
|
||||||
|
__EXT_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
__CORE_VERSION__: JSON.stringify(pkg.dependencies['@page-agent/core']),
|
||||||
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
force: true,
|
force: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,10 +29,10 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.563.0",
|
"lucide-react": "^0.563.0",
|
||||||
"motion": "^12.29.2",
|
"motion": "^12.30.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"rough-notation": "^0.5.1",
|
"rough-notation": "^0.5.1",
|
||||||
"simple-icons": "^16.6.0",
|
"simple-icons": "^16.7.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0"
|
"tailwind-merge": "^3.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,13 +103,13 @@ export default function ChromeExtension() {
|
|||||||
isZh
|
isZh
|
||||||
? `// 1. 用户安装扩展并在扩展设置中配置 auth token
|
? `// 1. 用户安装扩展并在扩展设置中配置 auth token
|
||||||
// 2. 你的页面读取相同的 token 并存入 localStorage
|
// 2. 你的页面读取相同的 token 并存入 localStorage
|
||||||
// 3. Token 匹配后,扩展会暴露 window.execute() 和 window.dispose()
|
// 3. Token 匹配后,扩展会暴露 window.PAGE_AGENT_EXT 对象
|
||||||
|
|
||||||
// ⚠️ 请在扩展弹窗中查看你的 auth token,然后填入下方
|
// ⚠️ 请在扩展弹窗中查看你的 auth token,然后填入下方
|
||||||
localStorage.setItem('PageAgentExtUserAuthToken', '<从扩展中获取的-token>')`
|
localStorage.setItem('PageAgentExtUserAuthToken', '<从扩展中获取的-token>')`
|
||||||
: `// 1. User installs extension and sets an auth token in extension settings
|
: `// 1. User installs extension and sets an auth token in extension settings
|
||||||
// 2. Your page reads the same token and stores it in localStorage
|
// 2. Your page reads the same token and stores it in localStorage
|
||||||
// 3. After token match, extension exposes window.execute() and window.dispose()
|
// 3. After token match, extension exposes window.PAGE_AGENT_EXT object
|
||||||
|
|
||||||
// ⚠️ Check your extension popup for the auth token
|
// ⚠️ Check your extension popup for the auth token
|
||||||
localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')`
|
localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')`
|
||||||
@@ -122,25 +122,54 @@ localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')
|
|||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'API 参考' : 'API Reference'}</h2>
|
<h2 className="text-2xl font-bold mb-4">{isZh ? 'API 参考' : 'API Reference'}</h2>
|
||||||
|
|
||||||
<h3 className="text-xl font-semibold mb-3">window.execute(task, llmConfig, hooks?)</h3>
|
{/* AI Assistant Instructions */}
|
||||||
|
<section className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
||||||
|
<h3 className="text-lg font-semibold text-purple-900 dark:text-purple-300 mb-2">
|
||||||
|
🤖 {isZh ? '给 AI 编程助手的文档' : 'Instructions for Your AI Assistant'}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 dark:text-gray-300 mb-3 text-sm">
|
||||||
|
{isZh
|
||||||
|
? '如果你在使用 AI 编程助手(如 Cursor、GitHub Copilot),可以将以下文档链接提供给它,让它更好地理解和使用 Page Agent 扩展 API:'
|
||||||
|
: 'If you are using an AI coding assistant (like Cursor, GitHub Copilot), share these documentation links with it for better understanding of Page Agent Extension API:'}
|
||||||
|
</p>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<a
|
||||||
|
href="https://github.com/alibaba/page-agent/blob/main/packages/extension/docs/extension_api.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 hover:underline"
|
||||||
|
>
|
||||||
|
📄 {isZh ? '英文版 API 文档' : 'API Documentation (English)'}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/alibaba/page-agent/blob/main/packages/extension/docs/extension_api_zh.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 hover:underline"
|
||||||
|
>
|
||||||
|
📄 {isZh ? '中文版 API 文档' : 'API Documentation (Chinese)'}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<h3 className="text-xl font-semibold my-3">PAGE_AGENT_EXT.execute(task, config)</h3>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '使用 LLM 配置执行任务。返回一个 Promise,在任务完成时 resolve。可选的 hooks 参数用于监听任务执行过程中的事件。'
|
? '使用配置执行任务。返回一个 Promise,在任务完成时 resolve。config 参数包含 LLM 设置、选项和事件回调。'
|
||||||
: 'Execute a task with LLM configuration. Returns a Promise that resolves when the task completes. Optional hooks parameter for listening to events during task execution.'}
|
: 'Execute a task with configuration. Returns a Promise that resolves when the task completes. Config includes LLM settings, options, and event callbacks.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
code={
|
code={
|
||||||
isZh
|
isZh
|
||||||
? `// 使用 LLM 配置和 hooks 执行任务
|
? `// 使用配置执行任务
|
||||||
const result = await window.execute(
|
const result = await window.PAGE_AGENT_EXT.execute(
|
||||||
'在 GitHub 上搜索 "page-agent" 并打开第一个结果',
|
'在 GitHub 上搜索 "page-agent" 并打开第一个结果',
|
||||||
{
|
{
|
||||||
baseURL: 'https://api.openai.com/v1',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
apiKey: 'your-api-key',
|
apiKey: 'your-api-key',
|
||||||
model: 'gpt-5-2'
|
model: 'gpt-5-2',
|
||||||
},
|
// includeInitialTab: false, // 设为 false 排除初始标签页
|
||||||
{
|
|
||||||
onStatusChange: status => console.log('状态变化:', status),
|
onStatusChange: status => console.log('状态变化:', status),
|
||||||
onActivity: activity => console.log('活动:', activity),
|
onActivity: activity => console.log('活动:', activity),
|
||||||
onHistoryUpdate: history => console.log('历史更新:', history),
|
onHistoryUpdate: history => console.log('历史更新:', history),
|
||||||
@@ -149,15 +178,14 @@ const result = await window.execute(
|
|||||||
)
|
)
|
||||||
|
|
||||||
console.log(result) // 任务执行结果`
|
console.log(result) // 任务执行结果`
|
||||||
: `// Execute a task with LLM configuration and hooks
|
: `// Execute a task with configuration
|
||||||
const result = await window.execute(
|
const result = await window.PAGE_AGENT_EXT.execute(
|
||||||
'Search for "page-agent" on GitHub and open the first result',
|
'Search for "page-agent" on GitHub and open the first result',
|
||||||
{
|
{
|
||||||
baseURL: 'https://api.openai.com/v1',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
apiKey: 'your-api-key',
|
apiKey: 'your-api-key',
|
||||||
model: 'gpt-5-2'
|
model: 'gpt-5-2',
|
||||||
},
|
// includeInitialTab: false, // Set to false to exclude initial tab
|
||||||
{
|
|
||||||
onStatusChange: status => console.log('Status change:', status),
|
onStatusChange: status => console.log('Status change:', status),
|
||||||
onActivity: activity => console.log('Activity:', activity),
|
onActivity: activity => console.log('Activity:', activity),
|
||||||
onHistoryUpdate: history => console.log('History update:', history),
|
onHistoryUpdate: history => console.log('History update:', history),
|
||||||
@@ -170,7 +198,7 @@ console.log(result) // Task execution result`
|
|||||||
language="javascript"
|
language="javascript"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h3 className="text-xl font-semibold mt-6 mb-3">window.dispose()</h3>
|
<h3 className="text-xl font-semibold mt-6 mb-3">PAGE_AGENT_EXT.dispose()</h3>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '停止当前正在运行的任务。停止后 Agent 可以重新使用。'
|
? '停止当前正在运行的任务。停止后 Agent 可以重新使用。'
|
||||||
@@ -181,49 +209,34 @@ console.log(result) // Task execution result`
|
|||||||
code={
|
code={
|
||||||
isZh
|
isZh
|
||||||
? `// 停止当前任务
|
? `// 停止当前任务
|
||||||
window.dispose()`
|
window.PAGE_AGENT_EXT.dispose()`
|
||||||
: `// Stop current task execution
|
: `// Stop current task execution
|
||||||
window.dispose()`
|
window.PAGE_AGENT_EXT.dispose()`
|
||||||
}
|
}
|
||||||
language="javascript"
|
language="javascript"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* LLM Config */}
|
{/* ExecuteConfig */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'LLM 配置' : 'LLM Configuration'}</h2>
|
<h2 className="text-2xl font-bold mb-4">{isZh ? '执行配置' : 'Execute Configuration'}</h2>
|
||||||
|
|
||||||
<CodeEditor
|
|
||||||
code={
|
|
||||||
isZh
|
|
||||||
? `interface LLMConfig {
|
|
||||||
baseURL: string // LLM API 端点
|
|
||||||
apiKey: string // API 密钥
|
|
||||||
model: string // 模型名称
|
|
||||||
}`
|
|
||||||
: `interface LLMConfig {
|
|
||||||
baseURL: string // LLM API endpoint
|
|
||||||
apiKey: string // API key
|
|
||||||
model: string // Model name
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
language="typescript"
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Execute Hooks */}
|
|
||||||
<section>
|
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'Execute Hooks' : 'Execute Hooks'}</h2>
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '通过 hooks 参数,你可以监听任务执行过程中的各种事件,实现实时更新 UI、日志记录等功能。'
|
? 'config 参数包含 LLM 设置、选项和事件回调,用于控制任务执行行为。'
|
||||||
: 'With hooks parameter, you can listen to various events during task execution for real-time UI updates, logging, and more.'}
|
: 'The config parameter includes LLM settings, options, and event callbacks to control task execution behavior.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
code={
|
code={
|
||||||
isZh
|
isZh
|
||||||
? `interface ExecuteHooks {
|
? `interface ExecuteConfig {
|
||||||
|
baseURL: string // LLM API 端点
|
||||||
|
apiKey: string // API 密钥
|
||||||
|
model: string // 模型名称
|
||||||
|
|
||||||
|
// 是否将初始标签页包含在任务中,默认 true
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
|
||||||
// Agent 状态变化时调用(idle, running, error, completed 等)
|
// Agent 状态变化时调用(idle, running, error, completed 等)
|
||||||
onStatusChange?: (status: AgentStatus) => void
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
|
||||||
@@ -236,7 +249,14 @@ window.dispose()`
|
|||||||
// Agent 被停止时调用
|
// Agent 被停止时调用
|
||||||
onDispose?: () => void
|
onDispose?: () => void
|
||||||
}`
|
}`
|
||||||
: `interface ExecuteHooks {
|
: `interface ExecuteConfig {
|
||||||
|
baseURL: string // LLM API endpoint
|
||||||
|
apiKey: string // API key
|
||||||
|
model: string // Model name
|
||||||
|
|
||||||
|
// Whether to include the initial tab in the task, default true
|
||||||
|
includeInitialTab?: boolean
|
||||||
|
|
||||||
// Called when agent status changes (idle, running, error, completed, etc.)
|
// Called when agent status changes (idle, running, error, completed, etc.)
|
||||||
onStatusChange?: (status: AgentStatus) => void
|
onStatusChange?: (status: AgentStatus) => void
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
|
||||||
// "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
||||||
// "baseUrl": "src",
|
// "baseUrl": "src",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
|
|||||||
Reference in New Issue
Block a user