Compare commits

...

7 Commits

Author SHA1 Message Date
Simon bdf79b7c10 chore(version): bump version to 1.5.11 2026-03-18 20:45:19 +08:00
Simon 24eefdef23 chore(version): bump version to 1.5.10 2026-03-18 20:36:29 +08:00
Simon 252f550c04 style(ext): fix ai-motion overflow; wording; launcher info 2026-03-18 20:25:54 +08:00
Simon 628c249d45 fix(ext): init without initialTab throws error 2026-03-18 19:36:02 +08:00
Simon 61d598142d feat(ext): ask user approval for MCP task 2026-03-18 19:14:44 +08:00
Simon c0510b2235 Merge pull request #294 from octo-patch/feature/upgrade-minimax-m27
feat: upgrade MiniMax default model to M2.7
2026-03-18 15:21:19 +08:00
Octopus ca0005c825 feat: upgrade MiniMax default model to M2.7
- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list
- Set MiniMax-M2.7 as default model in code example
- Keep all previous models as alternatives
2026-03-18 02:12:58 -05:00
23 changed files with 410 additions and 170 deletions
+2 -2
View File
@@ -49,8 +49,8 @@ Fastest way to try PageAgent with our free Demo LLM:
| Mirrors | URL | | Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- | | ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.9/dist/iife/page-agent.demo.js | | Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.11/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.5.9/files/dist/iife/page-agent.demo.js | | China | https://registry.npmmirror.com/page-agent/1.5.11/files/dist/iife/page-agent.demo.js |
### NPM Installation ### NPM Installation
+2 -2
View File
@@ -49,8 +49,8 @@
| Mirrors | URL | | Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- | | ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.9/dist/iife/page-agent.demo.js | | Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.11/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.5.9/files/dist/iife/page-agent.demo.js | | China | https://registry.npmmirror.com/page-agent/1.5.11/files/dist/iife/page-agent.demo.js |
### NPM 安装 ### NPM 安装
+20 -21
View File
@@ -1,12 +1,12 @@
{ {
"name": "root", "name": "root",
"version": "1.5.9", "version": "1.5.11",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "root", "name": "root",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"packages/page-controller", "packages/page-controller",
@@ -11799,11 +11799,11 @@
}, },
"packages/core": { "packages/core": {
"name": "@page-agent/core", "name": "@page-agent/core",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9", "@page-agent/page-controller": "1.5.11",
"chalk": "^5.6.2" "chalk": "^5.6.2"
}, },
"devDependencies": { "devDependencies": {
@@ -11815,13 +11815,13 @@
}, },
"packages/extension": { "packages/extension": {
"name": "@page-agent/ext", "name": "@page-agent/ext",
"version": "1.5.9", "version": "1.5.11",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@page-agent/core": "1.5.9", "@page-agent/core": "1.5.11",
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9", "@page-agent/page-controller": "1.5.11",
"@page-agent/ui": "1.5.9", "@page-agent/ui": "1.5.11",
"ai-motion": "^0.4.8", "ai-motion": "^0.4.8",
"chalk": "^5.6.2" "chalk": "^5.6.2"
}, },
@@ -11832,7 +11832,6 @@
"@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/chrome": "^0.1.37", "@types/chrome": "^0.1.37",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.1", "@types/react-dom": "^19.2.1",
@@ -11859,7 +11858,7 @@
}, },
"packages/llms": { "packages/llms": {
"name": "@page-agent/llms", "name": "@page-agent/llms",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chalk": "^5.6.2" "chalk": "^5.6.2"
@@ -11873,7 +11872,7 @@
}, },
"packages/mcp": { "packages/mcp": {
"name": "@page-agent/mcp", "name": "@page-agent/mcp",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.27.1", "@modelcontextprotocol/sdk": "^1.27.1",
@@ -11888,13 +11887,13 @@
} }
}, },
"packages/page-agent": { "packages/page-agent": {
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@page-agent/core": "1.5.9", "@page-agent/core": "1.5.11",
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9", "@page-agent/page-controller": "1.5.11",
"@page-agent/ui": "1.5.9", "@page-agent/ui": "1.5.11",
"chalk": "^5.6.2" "chalk": "^5.6.2"
}, },
"devDependencies": { "devDependencies": {
@@ -11906,7 +11905,7 @@
}, },
"packages/page-controller": { "packages/page-controller": {
"name": "@page-agent/page-controller", "name": "@page-agent/page-controller",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ai-motion": "^0.4.8" "ai-motion": "^0.4.8"
@@ -11914,12 +11913,12 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@page-agent/ui", "name": "@page-agent/ui",
"version": "1.5.9", "version": "1.5.11",
"license": "MIT" "license": "MIT"
}, },
"packages/website": { "packages/website": {
"name": "@page-agent/website", "name": "@page-agent/website",
"version": "1.5.9", "version": "1.5.11",
"devDependencies": { "devDependencies": {
"@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-separator": "^1.1.8",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "root", "name": "root",
"private": true, "private": true,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"workspaces": [ "workspaces": [
"packages/page-controller", "packages/page-controller",
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "@page-agent/core", "name": "@page-agent/core",
"private": false, "private": false,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"main": "./dist/esm/page-agent-core.js", "main": "./dist/esm/page-agent-core.js",
"module": "./dist/esm/page-agent-core.js", "module": "./dist/esm/page-agent-core.js",
@@ -44,8 +44,8 @@
}, },
"dependencies": { "dependencies": {
"chalk": "^5.6.2", "chalk": "^5.6.2",
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9" "@page-agent/page-controller": "1.5.11"
}, },
"peerDependencies": { "peerDependencies": {
"zod": "^3.25.0 || ^4.0.0" "zod": "^3.25.0 || ^4.0.0"
+5 -6
View File
@@ -1,7 +1,7 @@
{ {
"name": "@page-agent/ext", "name": "@page-agent/ext",
"private": true, "private": true,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "wxt", "dev": "wxt",
@@ -16,7 +16,6 @@
"@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/chrome": "^0.1.37", "@types/chrome": "^0.1.37",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.1", "@types/react-dom": "^19.2.1",
@@ -38,10 +37,10 @@
"wxt": "^0.20.19" "wxt": "^0.20.19"
}, },
"dependencies": { "dependencies": {
"@page-agent/core": "1.5.9", "@page-agent/core": "1.5.11",
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9", "@page-agent/page-controller": "1.5.11",
"@page-agent/ui": "1.5.9", "@page-agent/ui": "1.5.11",
"ai-motion": "^0.4.8", "ai-motion": "^0.4.8",
"chalk": "^5.6.2" "chalk": "^5.6.2"
}, },
@@ -75,6 +75,7 @@ export class MultiPageAgent extends PageAgentCore {
}, },
onBeforeStep: async (agent) => { onBeforeStep: async (agent) => {
if (!tabsController.currentTabId) return
// make sure the current tab is loaded before the step starts // make sure the current tab is loaded before the step starts
await tabsController.waitUntilTabLoaded(tabsController.currentTabId!) await tabsController.waitUntilTabLoaded(tabsController.currentTabId!)
}, },
@@ -58,8 +58,6 @@ export class RemotePageController {
} }
async getBrowserState(): Promise<BrowserState> { async getBrowserState(): Promise<BrowserState> {
if (!this.currentTabId) throw new Error('tabsController not initialized.')
let browserState = {} as BrowserState let browserState = {} as BrowserState
debug('getBrowserState', this.currentTabId) debug('getBrowserState', this.currentTabId)
@@ -178,7 +176,7 @@ interface DomActionReturn {
/** /**
* Check if a URL can run content scripts. * Check if a URL can run content scripts.
*/ */
function isContentScriptAllowed(url: string | undefined): boolean { export function isContentScriptAllowed(url: string | undefined): boolean {
if (!url) return false if (!url) return false
const restrictedPatterns = [ const restrictedPatterns = [
+14 -10
View File
@@ -1,3 +1,5 @@
import { isContentScriptAllowed } from './RemotePageController'
const PREFIX = '[TabsController]' const PREFIX = '[TabsController]'
function debug(...messages: any[]) { function debug(...messages: any[]) {
@@ -49,23 +51,25 @@ export class TabsController extends EventTarget {
} }
if (includeInitialTab) { if (includeInitialTab) {
this.currentTabId = this.initialTabId
const info = await sendMessage({ const info = await sendMessage({
type: 'TAB_CONTROL', type: 'TAB_CONTROL',
action: 'get_tab_info', action: 'get_tab_info',
payload: { tabId: this.initialTabId }, payload: { tabId: this.initialTabId },
}) })
this.tabs.push({ if (isContentScriptAllowed(info.url)) {
id: result.tabId, this.currentTabId = this.initialTabId
isInitial: true,
url: info.url,
title: info.title,
status: info.status,
})
await this.createTabGroup([this.initialTabId]) this.tabs.push({
id: result.tabId,
isInitial: true,
url: info.url,
title: info.title,
status: info.status,
})
await this.createTabGroup([this.initialTabId])
}
} }
await this.updateCurrentTabId(this.currentTabId) await this.updateCurrentTabId(this.currentTabId)
@@ -1,13 +1,15 @@
import { import {
ChevronDown,
Copy, Copy,
CornerUpLeft, CornerUpLeft,
ExternalLink,
Eye, Eye,
EyeOff, EyeOff,
FoldVertical,
HatGlasses, HatGlasses,
Home, Home,
Loader2, Loader2,
Scale, Scale,
UnfoldVertical,
} from 'lucide-react' } from 'lucide-react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { siGithub } from 'simple-icons' import { siGithub } from 'simple-icons'
@@ -153,6 +155,16 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
</div> </div>
</div> </div>
{/* Hub link */}
<a
href="/hub.html"
target="_blank"
className="flex items-center justify-between p-3 rounded-md border bg-muted/50 text-xs font-medium text-muted-foreground hover:text-foreground hover:border-foreground/20 transition-colors"
>
Manage Page Agent Hub
<ExternalLink className="size-3" />
</a>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<label className="text-xs text-muted-foreground">Base URL</label> <label className="text-xs text-muted-foreground">Base URL</label>
<Input <Input
@@ -167,7 +179,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
{isTestingEndpoint(baseURL) && ( {isTestingEndpoint(baseURL) && (
<div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[11px] text-muted-foreground leading-relaxed"> <div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[11px] text-muted-foreground leading-relaxed">
<Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" /> <Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" />
You are using the free testing API. By using this service you agree to the{' '} You are using our testing API. By using this you agree to the{' '}
<a <a
href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md" href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md"
target="_blank" target="_blank"
@@ -176,7 +188,6 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
> >
Terms of Use & Privacy Policy Terms of Use & Privacy Policy
</a> </a>
. No sensitive data. No guaranteed availability.
</div> </div>
)} )}
@@ -212,7 +223,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<label className="text-xs text-muted-foreground">Language</label> <label className="text-xs text-muted-foreground">Response Language</label>
<select <select
value={language ?? ''} value={language ?? ''}
onChange={(e) => setLanguage((e.target.value || undefined) as LanguagePreference)} onChange={(e) => setLanguage((e.target.value || undefined) as LanguagePreference)}
@@ -231,10 +242,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground cursor-pointer mt-1 font-bold" className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground cursor-pointer mt-1 font-bold"
> >
Advanced Advanced
<ChevronDown {advancedOpen ? <FoldVertical className="size-3" /> : <UnfoldVertical className="size-3" />}
className="size-3 transition-transform"
style={{ transform: advancedOpen ? 'rotate(0deg)' : 'rotate(90deg)' }}
/>
</button> </button>
{advancedOpen && ( {advancedOpen && (
+1 -1
View File
@@ -84,7 +84,7 @@ export function MotionOverlay({ active }: { active: boolean }) {
return ( return (
<div <div
ref={containerRef} ref={containerRef}
className="pointer-events-none absolute inset-0 z-10 opacity-60" className="pointer-events-none absolute inset-0 z-10 opacity-60 overflow-hidden"
style={{ display: active ? undefined : 'none' }} style={{ display: active ? undefined : 'none' }}
/> />
) )
@@ -8,7 +8,7 @@ function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimi
<SwitchPrimitive.Root <SwitchPrimitive.Root
data-slot="switch" data-slot="switch"
className={cn( className={cn(
'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', 'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 cursor-pointer items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className className
)} )}
{...props} {...props}
+155 -56
View File
@@ -1,10 +1,11 @@
import { Plug, PlugZap, Square, Unplug } from 'lucide-react' import { FoldVertical, Plug, PlugZap, Square, UnfoldVertical, Unplug } from 'lucide-react'
import { useEffect, useRef } from 'react' import { useEffect, useRef, useState } from 'react'
import { useAgent } from '@/agent/useAgent' import { useAgent } from '@/agent/useAgent'
import { ActivityCard, EventCard } from '@/components/cards' import { ActivityCard, EventCard } from '@/components/cards'
import { Logo, MotionOverlay, StatusDot } from '@/components/misc' import { Logo, MotionOverlay, StatusDot } from '@/components/misc'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Switch } from '@/components/ui/switch'
import { useHubWs } from './hub-ws' import { useHubWs } from './hub-ws'
@@ -25,33 +26,72 @@ export default function App() {
const wsLabel = { const wsLabel = {
connected: 'Connected', connected: 'Connected',
connecting: 'Connecting…', connecting: 'Connecting…',
disconnected: new URLSearchParams(location.search).get('ws') ? 'Disconnected' : 'Standalone', disconnected: new URLSearchParams(location.search).get('ws') ? 'Disconnected' : 'No connection',
}[wsState] }[wsState]
return ( return (
<div className="flex h-screen bg-background"> <div className="flex h-screen bg-background">
<MotionOverlay active={isRunning} />
{/* Left — Protocol docs */} {/* Left — Protocol docs */}
<aside className="w-80 shrink-0 border-r flex flex-col bg-muted/20"> <aside className="w-80 shrink-0 border-r flex flex-col bg-muted/20">
<div className="flex items-center gap-2 px-5 py-4 border-b"> <a
href="https://alibaba.github.io/page-agent/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 px-5 h-12 border-b hover:bg-muted/30 transition-colors"
>
<Logo className="size-5" /> <Logo className="size-5" />
<span className="text-sm font-semibold tracking-tight">Page Agent Hub</span> <span className="text-sm font-semibold tracking-tight">Page Agent Hub</span>
<span className="text-[9px] font-medium uppercase tracking-wider text-amber-600 bg-amber-500/10 border border-amber-500/30 rounded px-1.5 py-0.5">
Beta
</span>
</a>
<div className="flex-1 overflow-y-auto px-5 py-4 space-y-6">
<div className="text-xs text-muted-foreground leading-relaxed space-y-2">
<p>
Page Agent Hub lets local apps (e.g. MCP servers) control the Page Agent extension via
WebSocket.
</p>
<p>
Check out the official{' '}
<a
href="https://github.com/alibaba/page-agent/tree/main/packages/mcp"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
MCP server package
</a>
.
</p>
</div>
<HubConfig />
<ProtocolDocsCollapsible />
</div> </div>
<div className="flex-1 overflow-y-auto px-5 py-4"> <div className="border-t px-5 py-3 text-[10px] text-muted-foreground/60 flex items-center justify-between">
<ProtocolDocs /> <span className="font-mono">v{__VERSION__}</span>
</div> <span>
Built with by{' '}
<div className="border-t px-5 py-3 text-[11px] text-muted-foreground/60"> <a
Connect via <code className="text-[10px]">hub.html?ws=PORT</code> href="https://github.com/gaomeng1900"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
@Simon
</a>
</span>
</div> </div>
</aside> </aside>
{/* Right — Live session */} {/* Right — Live session */}
<main className="flex-1 flex flex-col min-w-0"> <main className="flex-1 flex flex-col min-w-0 relative">
{/* Header bar */} <MotionOverlay active={isRunning} />
<header className="flex items-center justify-between border-b px-5 py-3">
<header className="flex items-center justify-between border-b px-5 h-12">
<div className="flex items-center gap-2 text-xs text-muted-foreground"> <div className="flex items-center gap-2 text-xs text-muted-foreground">
<WsIcon className="size-3.5" /> <WsIcon className="size-3.5" />
<span>{wsLabel}</span> <span>{wsLabel}</span>
@@ -104,49 +144,108 @@ export default function App() {
) )
} }
function ProtocolDocs() { function HubConfig() {
const [allowAll, setAllowAll] = useState(false)
useEffect(() => {
chrome.storage.local.get('allowAllHubConnection').then((r) => {
setAllowAll(r.allowAllHubConnection === true)
})
}, [])
const toggle = (checked: boolean) => {
setAllowAll(checked)
chrome.storage.local.set({ allowAllHubConnection: checked })
}
return ( return (
<div className="space-y-5 text-xs text-muted-foreground"> <div>
<section> <h3 className="text-[11px] font-semibold text-foreground/80 uppercase tracking-wider mb-2">
<h3 className="text-[11px] font-semibold text-foreground/80 uppercase tracking-wider mb-2"> Config
Caller Hub </h3>
</h3> <div className="group/hub relative">
<pre className="bg-muted/50 rounded-md p-3 font-mono text-[10px] leading-relaxed whitespace-pre-wrap"> <label
{`{ type: "execute", task: string, config?: object } className={`flex items-center justify-between p-3 rounded-md border cursor-pointer text-xs ${allowAll ? 'bg-amber-500/10 border-amber-500/30 text-amber-600' : 'bg-muted/50 text-muted-foreground'}`}
{ type: "stop" }`} >
</pre> Auto-approve connections
</section> <Switch
checked={allowAll}
onCheckedChange={toggle}
className={allowAll ? 'data-[state=checked]:bg-amber-500' : ''}
/>
</label>
<section> {/* hide with invisible absolute opacity-0*/}
<h3 className="text-[11px] font-semibold text-foreground/80 uppercase tracking-wider mb-2"> <div className="group-hover/hub:visible group-hover/hub:opacity-100 transition-opacity duration-150 left-0 right-0 top-full z-10 pt-2">
Hub Caller <div className="relative p-2.5 rounded-md border border-border bg-background/60 backdrop-blur-md shadow-2xl text-muted-foreground text-xs leading-relaxed">
</h3> <div className="absolute -top-1.5 left-5 size-3 rotate-45 rounded-[1px] border-l border-t border-border bg-background/60 backdrop-blur-md" />
<pre className="bg-muted/50 rounded-md p-3 font-mono text-[10px] leading-relaxed whitespace-pre-wrap"> By default, each connection requires your approval before running tasks. <br />
{`{ type: "ready" } Enable this to skip per-session approval.
{ type: "result", success: boolean, data: string } <br />
{ type: "error", message: string }`} <span className="font-semibold">* Use with caution!</span>
</pre> </div>
</section> </div>
</div>
<section> </div>
<h3 className="text-[11px] font-semibold text-foreground/80 uppercase tracking-wider mb-2"> )
Flow }
</h3>
<ol className="list-decimal list-inside space-y-1 text-[11px] leading-relaxed"> function ProtocolDocsCollapsible() {
<li>Hub opens WS to caller's server</li> const [open, setOpen] = useState(false)
<li>
Sends <code className="text-[10px]">ready</code> return (
</li> <div>
<li> <button
Caller sends <code className="text-[10px]">execute</code> with task type="button"
</li> onClick={() => setOpen(!open)}
<li>Hub runs agent, streams events</li> className="flex items-center gap-1 text-[11px] font-semibold text-foreground/80 uppercase tracking-wider cursor-pointer"
<li> >
Hub sends <code className="text-[10px]">result</code> or{' '} Docs
<code className="text-[10px]">error</code> {open ? <FoldVertical className="size-3" /> : <UnfoldVertical className="size-3" />}
</li> </button>
</ol>
</section> {open && (
<div className="mt-3 space-y-4 text-xs text-muted-foreground">
<p className="text-[10px]">
Connect via <code className="text-[10px]">hub.html?ws=PORT</code>
</p>
<section>
<h4 className="text-[11px] font-medium text-foreground/60 mb-1.5">Flow</h4>
<ol className="list-decimal list-inside space-y-1 text-[11px] leading-relaxed">
<li>Hub opens WS to caller's server</li>
<li>
Sends <code className="text-[10px]">ready</code>
</li>
<li>
Caller sends <code className="text-[10px]">execute</code> with task
</li>
<li>Hub runs agent, streams events</li>
<li>
Hub sends <code className="text-[10px]">result</code> or{' '}
<code className="text-[10px]">error</code>
</li>
</ol>
</section>
<section>
<h4 className="text-[11px] font-medium text-foreground/60 mb-1.5">Caller Hub</h4>
<pre className="bg-muted/50 rounded-md p-3 font-mono text-[10px] leading-relaxed whitespace-pre-wrap">
{`{ type: "execute", task: string, config?: object }
{ type: "stop" }`}
</pre>
</section>
<section>
<h4 className="text-[11px] font-medium text-foreground/60 mb-1.5">Hub Caller</h4>
<pre className="bg-muted/50 rounded-md p-3 font-mono text-[10px] leading-relaxed whitespace-pre-wrap">
{`{ type: "ready" }
{ type: "result", success: boolean, data: string }
{ type: "error", message: string }`}
</pre>
</section>
</div>
)}
</div> </div>
) )
} }
@@ -70,6 +70,7 @@ export class HubWs {
#ws: WebSocket | null = null #ws: WebSocket | null = null
#state: HubWsState = 'disconnected' #state: HubWsState = 'disconnected'
#busy = false #busy = false
#approved = false
#handlers: HubWsHandlers #handlers: HubWsHandlers
#port: number #port: number
#onStateChange: (state: HubWsState) => void #onStateChange: (state: HubWsState) => void
@@ -103,6 +104,7 @@ export class HubWs {
ws.addEventListener('close', () => { ws.addEventListener('close', () => {
this.#ws = null this.#ws = null
this.#busy = false this.#busy = false
this.#approved = false
this.#setState('disconnected') this.#setState('disconnected')
}) })
@@ -115,6 +117,7 @@ export class HubWs {
this.#ws?.close() this.#ws?.close()
this.#ws = null this.#ws = null
this.#busy = false this.#busy = false
this.#approved = false
this.#setState('disconnected') this.#setState('disconnected')
} }
@@ -130,7 +133,7 @@ export class HubWs {
} }
} }
#handleMessage(raw: string) { async #handleMessage(raw: string) {
let msg: InboundMessage let msg: InboundMessage
try { try {
msg = JSON.parse(raw) msg = JSON.parse(raw)
@@ -138,6 +141,11 @@ export class HubWs {
return return
} }
if (!(await this.#checkApproval())) {
this.#send({ type: 'error', message: 'User denied the connection request.' })
return
}
switch (msg.type) { switch (msg.type) {
case 'execute': case 'execute':
this.#handleExecute(msg) this.#handleExecute(msg)
@@ -148,6 +156,22 @@ export class HubWs {
} }
} }
async #checkApproval(): Promise<boolean> {
if (this.#approved) return true
const { allowAllHubConnection } = await chrome.storage.local.get('allowAllHubConnection')
if (allowAllHubConnection === true) {
this.#approved = true
return true
}
const ok = window.confirm(
'An external application is requesting to control your browser via Page Agent Ext.\nAllow this session?'
)
if (ok) this.#approved = true
return ok
}
async #handleExecute(msg: ExecuteMessage) { async #handleExecute(msg: ExecuteMessage) {
if (this.#busy) { if (this.#busy) {
this.#send({ type: 'error', message: 'Hub is busy with another task' }) this.#send({ type: 'error', message: 'Hub is busy with another task' })
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@page-agent/llms", "name": "@page-agent/llms",
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"main": "./dist/lib/page-agent-llms.js", "main": "./dist/lib/page-agent-llms.js",
"module": "./dist/lib/page-agent-llms.js", "module": "./dist/lib/page-agent-llms.js",
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "@page-agent/mcp", "name": "@page-agent/mcp",
"private": true, "private": false,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"bin": { "bin": {
"page-agent-mcp": "src/index.js" "page-agent-mcp": "src/index.js"
+148 -40
View File
@@ -1,9 +1,10 @@
<!doctype html> <!doctype html>
<html lang="en"> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Page Agent Connecting</title> <link rel="icon" href="https://img.alicdn.com/imgextra/i1/O1CN01mRGret1QrKiu7CFJI_!!6000000002029-2-tps-64-64.png" />
<title>Page Agent MCP Launcher</title>
<style> <style>
* { * {
margin: 0; margin: 0;
@@ -15,7 +16,7 @@
system-ui, system-ui,
-apple-system, -apple-system,
sans-serif; sans-serif;
background: #0a0a0a; background: #09090b;
color: #e5e5e5; color: #e5e5e5;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -24,23 +25,37 @@
} }
.card { .card {
text-align: center; text-align: center;
max-width: 420px; max-width: 480px;
padding: 3rem 2rem; padding: 3rem 2rem;
} }
.logo {
width: 72px;
height: 72px;
border-radius: 18px;
margin-bottom: 1rem;
}
.badge {
font-size: 0.6875rem;
font-weight: 500;
color: #52525b;
letter-spacing: 0.05em;
text-transform: uppercase;
margin-bottom: 1.5rem;
}
h1 { h1 {
font-size: 1.25rem; font-size: 1.35rem;
font-weight: 600; font-weight: 600;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
p { .sub {
font-size: 0.875rem; font-size: 0.875rem;
color: #a3a3a3; color: #a1a1aa;
line-height: 1.6; line-height: 1.7;
} }
.spinner { .spinner {
width: 32px; width: 32px;
height: 32px; height: 32px;
border: 3px solid #333; border: 3px solid #27272a;
border-top-color: #fff; border-top-color: #fff;
border-radius: 50%; border-radius: 50%;
animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite;
@@ -58,72 +73,165 @@
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
.install { .install {
display: none; display: none;
} }
.install.show { .install.show {
display: block; display: block;
} }
.btn {
display: inline-block; .tips {
margin-top: 1rem; margin-top: 1.75rem;
padding: 0.5rem 1.25rem; text-align: left;
background: #18181b;
border: 1px solid #27272a;
border-radius: 12px;
padding: 1.25rem 1.5rem;
}
.tips li {
font-size: 0.8125rem;
color: #a1a1aa;
line-height: 1.7;
margin-left: 1rem;
}
.tips li + li {
margin-top: 0.35rem;
}
.store-btn {
display: inline-flex;
align-items: center;
gap: 0.625rem;
margin-top: 1.5rem;
padding: 0.625rem 1.5rem;
background: #2563eb; background: #2563eb;
color: #fff; color: #fff;
border-radius: 0.5rem; border-radius: 10px;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 500; font-weight: 500;
transition: background 0.15s;
} }
.btn:hover { .store-btn:hover {
background: #1d4ed8; background: #1d4ed8;
text-decoration: none; text-decoration: none;
} }
.store-btn img {
width: 20px;
height: 20px;
}
.links {
margin-top: 1.75rem;
display: flex;
justify-content: center;
gap: 1.5rem;
font-size: 0.8125rem;
}
.links a {
color: #71717a;
transition: color 0.15s;
}
.links a:hover {
color: #a1a1aa;
text-decoration: none;
}
</style> </style>
</head> </head>
<body> <body>
<div class="card"> <div class="card">
<img
class="logo"
src="https://img.alicdn.com/imgextra/i3/O1CN01JPT4Fj1FJTfmHfNxO_!!6000000000466-49-tps-512-512.webp"
alt="Page Agent"
/>
<div class="badge">Page Agent MCP Launcher</div>
<div id="connecting"> <div id="connecting">
<div class="spinner"></div> <div class="spinner"></div>
<h1>Connecting to Page Agent</h1> <h1 data-i18n="connecting_title">Connecting to Page Agent</h1>
<p>Opening the hub in your browser...</p> <p class="sub" data-i18n="connecting_sub">Opening the hub in your browser</p>
</div> </div>
<div id="install" class="install"> <div id="install" class="install">
<h1>Extension Not Found</h1> <h1 data-i18n="install_title">Extension Required</h1>
<p>The Page Agent browser extension is required.<br />Install it and try again.</p> <p class="sub" data-i18n="install_sub">
<a class="btn" href="__STORE_URL__" target="_blank">Install Extension</a> Page Agent requires the latest browser extension to work.
<p style="margin-top: 1.5rem; font-size: 0.75rem">
After installing, restart the MCP server or refresh this page.
</p> </p>
<a class="store-btn" href="__STORE_URL__" target="_blank">
<img
src="https://img.alicdn.com/imgextra/i3/O1CN01JpW0Vo1sR3FpiZKFM_!!6000000005762-55-tps-192-192.svg"
alt=""
/>
<span data-i18n="install_btn">Install from Chrome Web Store</span>
</a>
<ul class="tips">
<li data-i18n="tip_outdated">
If the extension is outdated, please update it to the latest version.
</li>
<li data-i18n="tip_other_browser">
If the extension is not installed in this browser, open this page from the
browser that has it installed.
</li>
<li data-i18n="tip_refresh">Refresh this page after installing or updating.</li>
</ul>
</div>
<div class="links">
<a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank" data-i18n="link_docs">Docs</a>
<a href="https://github.com/alibaba/page-agent/issues" target="_blank" data-i18n="link_issues">Report an Issue</a>
</div> </div>
</div> </div>
<script>
;(function () {
var EXT_ID = '__EXT_ID__'
var wsPort = __WS_PORT__
function showInstall() { <script>
{
const EXT_ID = '__EXT_ID__'
const wsPort = __WS_PORT__
const zh = {
connecting_title: '正在连接 Page Agent',
connecting_sub: '正在浏览器中打开 Hub…',
install_title: '需要安装浏览器插件',
install_sub: 'Page Agent 需要安装最新版浏览器插件才能运行。',
install_btn: '从 Chrome 应用商店安装',
tip_outdated: '如果插件版本过旧,请更新到最新版本。',
tip_other_browser:
'如果该浏览器中未安装插件,请从装有插件的浏览器打开此页面。',
tip_refresh: '安装或更新后,请刷新此页面。',
link_docs: '文档',
link_issues: '问题反馈',
}
if (/^zh\b/i.test(navigator.language)) {
document.querySelectorAll('[data-i18n]').forEach((el) => {
const key = el.getAttribute('data-i18n')
if (zh[key]) el.textContent = zh[key]
})
}
const showInstall = () => {
document.getElementById('connecting').style.display = 'none' document.getElementById('connecting').style.display = 'none'
document.getElementById('install').classList.add('show') document.getElementById('install').classList.add('show')
} }
try { try {
if (typeof chrome === 'undefined' || !chrome.runtime || !chrome.runtime.sendMessage) { if (!globalThis.chrome?.runtime?.sendMessage) {
showInstall() showInstall()
return } else {
} chrome.runtime.sendMessage(
chrome.runtime.sendMessage( EXT_ID,
EXT_ID, { type: 'OPEN_HUB', wsPort },
{ type: 'OPEN_HUB', wsPort: wsPort }, (response) => {
function (response) { if (chrome.runtime.lastError || !response?.ok) showInstall()
if (chrome.runtime.lastError || !response || !response.ok) {
showInstall()
} }
} )
) }
} catch (e) { } catch {
showInstall() showInstall()
} }
})() }
</script> </script>
</body> </body>
</html> </html>
+5 -5
View File
@@ -1,7 +1,7 @@
{ {
"name": "page-agent", "name": "page-agent",
"private": false, "private": false,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"main": "./dist/esm/page-agent.js", "main": "./dist/esm/page-agent.js",
"module": "./dist/esm/page-agent.js", "module": "./dist/esm/page-agent.js",
@@ -44,10 +44,10 @@
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\"" "postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
}, },
"dependencies": { "dependencies": {
"@page-agent/core": "1.5.9", "@page-agent/core": "1.5.11",
"@page-agent/llms": "1.5.9", "@page-agent/llms": "1.5.11",
"@page-agent/page-controller": "1.5.9", "@page-agent/page-controller": "1.5.11",
"@page-agent/ui": "1.5.9", "@page-agent/ui": "1.5.11",
"chalk": "^5.6.2" "chalk": "^5.6.2"
}, },
"peerDependencies": { "peerDependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@page-agent/page-controller", "name": "@page-agent/page-controller",
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"main": "./dist/lib/page-controller.js", "main": "./dist/lib/page-controller.js",
"module": "./dist/lib/page-controller.js", "module": "./dist/lib/page-controller.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@page-agent/ui", "name": "@page-agent/ui",
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"main": "./dist/lib/page-agent-ui.js", "main": "./dist/lib/page-agent-ui.js",
"module": "./dist/lib/page-agent-ui.js", "module": "./dist/lib/page-agent-ui.js",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@page-agent/website", "name": "@page-agent/website",
"private": true, "private": true,
"version": "1.5.9", "version": "1.5.11",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host 0.0.0.0", "dev": "vite --host 0.0.0.0",
+2 -2
View File
@@ -1,8 +1,8 @@
// Demo build (auto-init with demo LLM, for quick testing) // Demo build (auto-init with demo LLM, for quick testing)
export const CDN_DEMO_URL = export const CDN_DEMO_URL =
'https://cdn.jsdelivr.net/npm/page-agent@1.5.9/dist/iife/page-agent.demo.js' 'https://cdn.jsdelivr.net/npm/page-agent@1.5.11/dist/iife/page-agent.demo.js'
export const CDN_DEMO_CN_URL = export const CDN_DEMO_CN_URL =
'https://registry.npmmirror.com/page-agent/1.5.9/files/dist/iife/page-agent.demo.js' 'https://registry.npmmirror.com/page-agent/1.5.11/files/dist/iife/page-agent.demo.js'
// Demo LLM for website testing (homepage quick trial uses flash) // Demo LLM for website testing (homepage quick trial uses flash)
export const DEMO_MODEL = 'qwen3.5-flash' export const DEMO_MODEL = 'qwen3.5-flash'
@@ -34,7 +34,7 @@ const MODEL_GROUPS: Record<string, string[]> = {
'claude-sonnet-3.5', 'claude-sonnet-3.5',
], ],
xAI: ['grok-4.1-fast', 'grok-4', 'grok-code-fast'], xAI: ['grok-4.1-fast', 'grok-4', 'grok-code-fast'],
MiniMax: ['MiniMax-M2.5', 'MiniMax-M2.5-highspeed'], MiniMax: ['MiniMax-M2.7', 'MiniMax-M2.7-highspeed', 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed'],
MoonshotAI: ['kimi-k2.5'], MoonshotAI: ['kimi-k2.5'],
'Z.AI': ['glm-5', 'glm-4.7'], 'Z.AI': ['glm-5', 'glm-4.7'],
} }
@@ -126,7 +126,7 @@ const pageAgent = new PageAgent({
const pageAgent = new PageAgent({ const pageAgent = new PageAgent({
baseURL: 'https://api.minimax.io/v1', baseURL: 'https://api.minimax.io/v1',
apiKey: 'your-minimax-api-key', apiKey: 'your-minimax-api-key',
model: 'MiniMax-M2.5' model: 'MiniMax-M2.7'
}); });
// Self-hosted models (e.g., Ollama) // Self-hosted models (e.g., Ollama)