feat(ext): add includeInitialTab option; change main world API
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -11060,7 +11060,7 @@
|
|||||||
},
|
},
|
||||||
"packages/extension": {
|
"packages/extension": {
|
||||||
"name": "@page-agent/ext",
|
"name": "@page-agent/ext",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@page-agent/core": "1.1.0",
|
"@page-agent/core": "1.1.0",
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} from '@page-agent/core'
|
||||||
|
|
||||||
// Wait for extension injection (up to 1 second)
|
// Wait for extension injection (up to 1 second)
|
||||||
@@ -48,18 +47,13 @@ async function waitForExtension(timeout = 1000): Promise<boolean> {
|
|||||||
|
|
||||||
// Usage
|
// Usage
|
||||||
if (await waitForExtension()) {
|
if (await waitForExtension()) {
|
||||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {
|
||||||
'Click the login button',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
{
|
apiKey: 'your-api-key',
|
||||||
baseURL: 'https://api.openai.com/v1',
|
model: 'gpt-5.2',
|
||||||
apiKey: 'your-api-key',
|
onStatusChange: (status) => console.log('Status:', status),
|
||||||
model: 'gpt-5.2',
|
onActivity: (activity) => console.log('Activity:', activity),
|
||||||
},
|
})
|
||||||
{
|
|
||||||
onStatusChange: (status) => console.log('Status:', status),
|
|
||||||
onActivity: (activity) => console.log('Activity:', activity),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
console.log('Result:', result)
|
console.log('Result:', result)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -76,7 +70,7 @@ Extension version string (e.g., `"1.0.0"`). This is exposed separately to allow
|
|||||||
|
|
||||||
Main API namespace object containing:
|
Main API namespace object containing:
|
||||||
|
|
||||||
#### `PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)`
|
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||||
|
|
||||||
Execute an agent task.
|
Execute an agent task.
|
||||||
|
|
||||||
@@ -85,8 +79,7 @@ Execute an agent task.
|
|||||||
| Name | Type | Required | Description |
|
| Name | Type | Required | Description |
|
||||||
|------|------|----------|-------------|
|
|------|------|----------|-------------|
|
||||||
| `task` | `string` | Yes | Task description |
|
| `task` | `string` | Yes | Task description |
|
||||||
| `llmConfig` | `LLMConfig` | Yes | LLM configuration |
|
| `config` | `ExecuteConfig` | Yes | Execution configuration (LLM settings, options, and event callbacks) |
|
||||||
| `hooks` | `ExecuteHooks` | No | Event callbacks |
|
|
||||||
|
|
||||||
**Returns:** `Promise<ExecutionResult>`
|
**Returns:** `Promise<ExecutionResult>`
|
||||||
|
|
||||||
@@ -104,21 +97,26 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} from '@page-agent/core'
|
||||||
|
|
||||||
export interface ExecuteHooks {
|
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 = (
|
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
task: string,
|
|
||||||
llmConfig: LLMConfig,
|
|
||||||
hooks?: ExecuteHooks
|
|
||||||
) => Promise<ExecutionResult>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### AgentStatus
|
### AgentStatus
|
||||||
@@ -164,16 +162,6 @@ type HistoricalEvent =
|
|||||||
| { type: 'error'; message: string; rawResponse?: unknown }
|
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||||
```
|
```
|
||||||
|
|
||||||
### LLMConfig
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface LLMConfig {
|
|
||||||
baseURL: string // e.g. 'https://api.openai.com/v1'
|
|
||||||
apiKey: string
|
|
||||||
model: string // e.g. 'gpt-5.2'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ExecutionResult
|
### ExecutionResult
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -205,44 +193,63 @@ if (result.success) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Event Hooks
|
### 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
|
```typescript
|
||||||
await window.PAGE_AGENT_EXT!.execute(
|
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||||
'Navigate to the settings page',
|
'Open a new tab and search for page-agent on GitHub',
|
||||||
llmConfig,
|
|
||||||
{
|
{
|
||||||
onStatusChange: (status) => {
|
baseURL: 'https://api.openai.com/v1',
|
||||||
updateUI({ agentStatus: status })
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
},
|
model: 'gpt-5.2',
|
||||||
onActivity: (activity) => {
|
includeInitialTab: false, // Agent will open new tabs only
|
||||||
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)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
### Stop Execution
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Start a task
|
// Start a task
|
||||||
window.PAGE_AGENT_EXT!.execute('Scroll through all pages', llmConfig)
|
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
|
// Later, stop it
|
||||||
window.PAGE_AGENT_EXT!.dispose()
|
window.PAGE_AGENT_EXT!.dispose()
|
||||||
@@ -258,24 +265,25 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} 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 {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
PAGE_AGENT_EXT_VERSION?: string
|
PAGE_AGENT_EXT_VERSION?: string
|
||||||
PAGE_AGENT_EXT?: {
|
PAGE_AGENT_EXT?: {
|
||||||
version: string
|
version: string
|
||||||
execute: (
|
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
task: string,
|
|
||||||
llmConfig: LLMConfig,
|
|
||||||
hooks?: {
|
|
||||||
onStatusChange?: (status: AgentStatus) => void
|
|
||||||
onActivity?: (activity: AgentActivity) => void
|
|
||||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
|
||||||
onDispose?: () => void
|
|
||||||
}
|
|
||||||
) => Promise<ExecutionResult>
|
|
||||||
dispose: () => void
|
dispose: () => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} from '@page-agent/core'
|
||||||
|
|
||||||
// 等待插件注入(最多 1 秒)
|
// 等待插件注入(最多 1 秒)
|
||||||
@@ -48,18 +47,13 @@ async function waitForExtension(timeout = 1000): Promise<boolean> {
|
|||||||
|
|
||||||
// 使用
|
// 使用
|
||||||
if (await waitForExtension()) {
|
if (await waitForExtension()) {
|
||||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
const result = await window.PAGE_AGENT_EXT!.execute('点击登录按钮', {
|
||||||
'点击登录按钮',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
{
|
apiKey: 'your-api-key',
|
||||||
baseURL: 'https://api.openai.com/v1',
|
model: 'gpt-5.2',
|
||||||
apiKey: 'your-api-key',
|
onStatusChange: (status) => console.log('状态:', status),
|
||||||
model: 'gpt-5.2',
|
onActivity: (activity) => console.log('活动:', activity),
|
||||||
},
|
})
|
||||||
{
|
|
||||||
onStatusChange: (status) => console.log('状态:', status),
|
|
||||||
onActivity: (activity) => console.log('活动:', activity),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
console.log('结果:', result)
|
console.log('结果:', result)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -76,7 +70,7 @@ if (await waitForExtension()) {
|
|||||||
|
|
||||||
主 API 命名空间对象,包含:
|
主 API 命名空间对象,包含:
|
||||||
|
|
||||||
#### `PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)`
|
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||||
|
|
||||||
执行 Agent 任务。
|
执行 Agent 任务。
|
||||||
|
|
||||||
@@ -85,8 +79,7 @@ if (await waitForExtension()) {
|
|||||||
| 名称 | 类型 | 必填 | 说明 |
|
| 名称 | 类型 | 必填 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `task` | `string` | 是 | 任务描述 |
|
| `task` | `string` | 是 | 任务描述 |
|
||||||
| `llmConfig` | `LLMConfig` | 是 | LLM 配置 |
|
| `config` | `ExecuteConfig` | 是 | 执行配置(LLM 设置、选项和事件回调) |
|
||||||
| `hooks` | `ExecuteHooks` | 否 | 事件回调 |
|
|
||||||
|
|
||||||
**返回:** `Promise<ExecutionResult>`
|
**返回:** `Promise<ExecutionResult>`
|
||||||
|
|
||||||
@@ -104,21 +97,26 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} from '@page-agent/core'
|
||||||
|
|
||||||
export interface ExecuteHooks {
|
export interface ExecuteConfig {
|
||||||
|
baseURL: string
|
||||||
|
apiKey: string
|
||||||
|
model: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否将初始标签页(运行此脚本的页面)包含在任务中。
|
||||||
|
* @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 = (
|
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
task: string,
|
|
||||||
llmConfig: LLMConfig,
|
|
||||||
hooks?: ExecuteHooks
|
|
||||||
) => Promise<ExecutionResult>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### AgentStatus
|
### AgentStatus
|
||||||
@@ -164,16 +162,6 @@ type HistoricalEvent =
|
|||||||
| { type: 'error'; message: string; rawResponse?: unknown }
|
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||||
```
|
```
|
||||||
|
|
||||||
### LLMConfig
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface LLMConfig {
|
|
||||||
baseURL: string // 例如 'https://api.openai.com/v1'
|
|
||||||
apiKey: string
|
|
||||||
model: string // 例如 'gpt-5.2'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ExecutionResult
|
### ExecutionResult
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -205,44 +193,63 @@ if (result.success) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 排除初始标签页
|
||||||
|
|
||||||
|
默认情况下,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
|
```typescript
|
||||||
await window.PAGE_AGENT_EXT!.execute(
|
await window.PAGE_AGENT_EXT!.execute('导航到设置页面', {
|
||||||
'导航到设置页面',
|
baseURL: 'https://api.openai.com/v1',
|
||||||
llmConfig,
|
apiKey: process.env.OPENAI_API_KEY!,
|
||||||
{
|
model: 'gpt-5.2',
|
||||||
onStatusChange: (status) => {
|
onStatusChange: (status) => {
|
||||||
updateUI({ agentStatus: status })
|
updateUI({ agentStatus: status })
|
||||||
},
|
},
|
||||||
onActivity: (activity) => {
|
onActivity: (activity) => {
|
||||||
switch (activity.type) {
|
switch (activity.type) {
|
||||||
case 'thinking':
|
case 'thinking':
|
||||||
showSpinner('Agent 正在思考...')
|
showSpinner('Agent 正在思考...')
|
||||||
break
|
break
|
||||||
case 'executing':
|
case 'executing':
|
||||||
showSpinner(`正在执行: ${activity.tool}`)
|
showSpinner(`正在执行: ${activity.tool}`)
|
||||||
break
|
break
|
||||||
case 'executed':
|
case 'executed':
|
||||||
log(`${activity.tool} 完成,耗时 ${activity.duration}ms`)
|
log(`${activity.tool} 完成,耗时 ${activity.duration}ms`)
|
||||||
break
|
break
|
||||||
case 'error':
|
case 'error':
|
||||||
showError(activity.message)
|
showError(activity.message)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onHistoryUpdate: (history) => {
|
onHistoryUpdate: (history) => {
|
||||||
renderHistory(history)
|
renderHistory(history)
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 停止执行
|
### 停止执行
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 启动任务
|
// 启动任务
|
||||||
window.PAGE_AGENT_EXT!.execute('滚动浏览所有页面', llmConfig)
|
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_EXT!.dispose()
|
||||||
@@ -258,24 +265,25 @@ import type {
|
|||||||
AgentStatus,
|
AgentStatus,
|
||||||
ExecutionResult,
|
ExecutionResult,
|
||||||
HistoricalEvent,
|
HistoricalEvent,
|
||||||
LLMConfig,
|
|
||||||
} from '@page-agent/core'
|
} 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 {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
PAGE_AGENT_EXT_VERSION?: string
|
PAGE_AGENT_EXT_VERSION?: string
|
||||||
PAGE_AGENT_EXT?: {
|
PAGE_AGENT_EXT?: {
|
||||||
version: string
|
version: string
|
||||||
execute: (
|
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||||
task: string,
|
|
||||||
llmConfig: LLMConfig,
|
|
||||||
hooks?: {
|
|
||||||
onStatusChange?: (status: AgentStatus) => void
|
|
||||||
onActivity?: (activity: AgentActivity) => void
|
|
||||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
|
||||||
onDispose?: () => void
|
|
||||||
}
|
|
||||||
) => Promise<ExecutionResult>
|
|
||||||
dispose: () => void
|
dispose: () => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@page-agent/ext",
|
"name": "@page-agent/ext",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function detectLanguage(): 'en-US' | 'zh-CN' {
|
|||||||
* - can be used from a side panel or a content script
|
* - can be used from a side panel or a content script
|
||||||
*/
|
*/
|
||||||
export class MultiPageAgent extends PageAgentCore {
|
export class MultiPageAgent extends PageAgentCore {
|
||||||
constructor(config: Omit<PageAgentConfig, 'pageController'>) {
|
constructor(config: Omit<PageAgentConfig, 'pageController'> & { includeInitialTab?: boolean }) {
|
||||||
// multi page controller
|
// multi page controller
|
||||||
const tabsController = new TabsController()
|
const tabsController = new TabsController()
|
||||||
const pageController = new RemotePageController(tabsController)
|
const pageController = new RemotePageController(tabsController)
|
||||||
@@ -31,6 +31,9 @@ export class MultiPageAgent extends PageAgentCore {
|
|||||||
`Default working language: **${targetLanguage}**`
|
`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.
|
||||||
@@ -47,7 +50,7 @@ export class MultiPageAgent extends PageAgentCore {
|
|||||||
customSystemPrompt: systemPrompt,
|
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')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,19 +1,24 @@
|
|||||||
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(() => {
|
||||||
let _lastId = 0
|
let _lastId = 0
|
||||||
function getId() {
|
function getId() {
|
||||||
@@ -21,13 +26,13 @@ export default defineUnlistedScript(() => {
|
|||||||
return _lastId
|
return _lastId
|
||||||
}
|
}
|
||||||
|
|
||||||
const execute: Execute = async (task, llmConfig, hooks) => {
|
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()
|
||||||
|
|
||||||
@@ -40,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) {
|
||||||
@@ -73,6 +79,7 @@ export default defineUnlistedScript(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @note will be removed on dispose or result
|
||||||
window.addEventListener('message', handleMessage)
|
window.addEventListener('message', handleMessage)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -81,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,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'*'
|
'*'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -152,27 +152,24 @@ localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h3 className="text-xl font-semibold my-3">
|
<h3 className="text-xl font-semibold my-3">PAGE_AGENT_EXT.execute(task, config)</h3>
|
||||||
PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)
|
|
||||||
</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.PAGE_AGENT_EXT.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),
|
||||||
@@ -181,15 +178,14 @@ const result = await window.PAGE_AGENT_EXT.execute(
|
|||||||
)
|
)
|
||||||
|
|
||||||
console.log(result) // 任务执行结果`
|
console.log(result) // 任务执行结果`
|
||||||
: `// Execute a task with LLM configuration and hooks
|
: `// Execute a task with configuration
|
||||||
const result = await window.PAGE_AGENT_EXT.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),
|
||||||
@@ -221,41 +217,26 @@ window.PAGE_AGENT_EXT.dispose()`
|
|||||||
/>
|
/>
|
||||||
</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
|
||||||
|
|
||||||
@@ -268,7 +249,14 @@ window.PAGE_AGENT_EXT.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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user