Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1751ba325d | |||
| 066b045822 | |||
| 3b21c42cdb | |||
| 8b1e91d665 | |||
| aa5909264a | |||
| b6232d4e21 | |||
| 0d3cef5578 | |||
| 1e737f3a71 | |||
| 2f2bdb4697 | |||
| 3fa7c8b85a | |||
| 7efce91a91 | |||
| a11d370f2b | |||
| d7bfa42f85 | |||
| 551ba49280 | |||
| adab24bb22 | |||
| 64aea7e84c | |||
| 67f6bd619e | |||
| 35d1fd1166 | |||
| d5b8019fb1 | |||
| 526cb4dbb3 | |||
| 6dc56c57c6 | |||
| a3263fdd3a | |||
| 14974c0257 | |||
| e70ae40096 |
@@ -9,9 +9,10 @@ This is a **monorepo** with npm workspaces:
|
||||
|
||||
Internal packages:
|
||||
|
||||
- **CDN** (`packages/cdn/`) - IIFE builds for script tag usage (npm: `@page-agent/cdn`)
|
||||
- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model
|
||||
- **Page Controller** (`packages/page-controller/`) - DOM operations, independent of LLM
|
||||
- **UI** (`packages/ui/`) - Panel, SimulatorMask, i18n. Decoupled from PageAgent
|
||||
- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM
|
||||
- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent
|
||||
|
||||
## Development Commands
|
||||
|
||||
@@ -31,6 +32,7 @@ Simple monorepo solution: TypeScript references + Vite aliases. Update tsconfig
|
||||
```
|
||||
packages/
|
||||
├── page-agent/ # npm: "page-agent" ⭐ MAIN
|
||||
├── cdn/ # npm: "@page-agent/cdn" (IIFE builds)
|
||||
├── website/ # @page-agent/website (private)
|
||||
├── llms/ # @page-agent/llms
|
||||
├── page-controller/ # @page-agent/page-controller
|
||||
@@ -43,8 +45,8 @@ packages/
|
||||
|
||||
- **Page Agent**: Core lib. Imports from `@page-agent/llms`, `@page-agent/page-controller`, `@page-agent/ui`
|
||||
- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent
|
||||
- **UI**: Panel, Mask, i18n. No dependency on page-agent
|
||||
- **Page Controller**: Pure DOM operations. No LLM or UI dependency
|
||||
- **UI**: Panel and i18n. No dependency on page-agent
|
||||
- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config
|
||||
|
||||
### PageController ↔ PageAgent Communication
|
||||
|
||||
@@ -69,41 +71,43 @@ const pageInfo = await this.pageController.getPageInfo()
|
||||
3. **LLM Processing**: AI returns action plans (page-agent)
|
||||
4. **Indexed Operations**: PageAgent calls PageController by element index
|
||||
|
||||
### CDN Auto-Injection
|
||||
### CDN Builds (`packages/cdn/`)
|
||||
|
||||
Library auto-initializes via script tag:
|
||||
Two IIFE builds for script tag usage:
|
||||
|
||||
```html
|
||||
<script src="page-agent.js?model=gpt-4"></script>
|
||||
```
|
||||
| Build | File | Description |
|
||||
| ----- | -------------------- | -------------------------------- |
|
||||
| Demo | `page-agent.demo.js` | Auto-init with built-in test API |
|
||||
| Full | `page-agent.js` | Exposes `PageAgent` class only |
|
||||
|
||||
Query params configure `PageAgentConfig` in `src/umd.ts`.
|
||||
Demo build supports query params (e.g., `?model=gpt-4&lang=en-US`).
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
### Page Agent (`packages/page-agent/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/PageAgent.ts` | ⭐ Main AI agent class |
|
||||
| `src/umd.ts` | CDN/UMD entry with auto-init |
|
||||
| `src/tools/` | Tool definitions calling PageController |
|
||||
| File | Description |
|
||||
| ------------------ | --------------------------------------- |
|
||||
| `src/PageAgent.ts` | ⭐ Main AI agent class |
|
||||
| `src/umd.ts` | CDN/UMD entry with auto-init |
|
||||
| `src/tools/` | Tool definitions calling PageController |
|
||||
|
||||
### LLMs (`packages/llms/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/index.ts` | ⭐ LLM class with retry logic |
|
||||
| `src/types.ts` | MacroToolInput, AgentBrain, LLMConfig |
|
||||
| `src/OpenAILenientClient.ts` | OpenAI-compatible client |
|
||||
| File | Description |
|
||||
| ---------------------------- | ------------------------------------- |
|
||||
| `src/index.ts` | ⭐ LLM class with retry logic |
|
||||
| `src/types.ts` | MacroToolInput, AgentBrain, LLMConfig |
|
||||
| `src/OpenAILenientClient.ts` | OpenAI-compatible client |
|
||||
|
||||
### Page Controller (`packages/page-controller/`)
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `src/PageController.ts` | ⭐ Main controller class |
|
||||
| `src/actions.ts` | Element interactions (click, input, scroll) |
|
||||
| `src/dom/dom_tree/index.js` | Core DOM extraction engine |
|
||||
| File | Description |
|
||||
| --------------------------- | ---------------------------------------------------------- |
|
||||
| `src/PageController.ts` | ⭐ Main controller class with optional mask support |
|
||||
| `src/SimulatorMask.ts` | Visual overlay blocking user interaction during automation |
|
||||
| `src/actions.ts` | Element interactions (click, input, scroll) |
|
||||
| `src/dom/dom_tree/index.js` | Core DOM extraction engine |
|
||||
|
||||
## Adding New Features
|
||||
|
||||
@@ -123,4 +127,5 @@ Query params configure `PageAgentConfig` in `src/umd.ts`.
|
||||
|
||||
- Explicit typing for exported/public APIs
|
||||
- ESLint relaxes some unsafe rules for rapid iteration
|
||||
- Every change you make should not only implement the desired functionality but also improve the quality of the codebase
|
||||
- All code and comments must be in English.
|
||||
|
||||
+3
-2
@@ -79,8 +79,9 @@ PageAgent adopts a simplified monorepo structure:
|
||||
packages/
|
||||
├── page-agent/ # AI agent (npm: page-agent)
|
||||
├── llms/ # LLM 客户端 (npm: @page-agent/llms)
|
||||
├── page-controller/ # DOM 操作 (npm: @page-agent/page-controller)
|
||||
├── ui/ # 面板 & 蒙层 & 模拟鼠标 (npm: @page-agent/ui)
|
||||
├── page-controller/ # DOM 操作 & 蒙层 & 模拟鼠标 (npm: @page-agent/page-controller)
|
||||
├── ui/ # 面板 & i18n (npm: @page-agent/ui)
|
||||
├── cdn/ # CDN IIFE builds (npm: @page-agent/cdn)
|
||||
└── website/ # 文档站点
|
||||
```
|
||||
|
||||
|
||||
@@ -79,8 +79,9 @@ PageAgent adopts a simplified monorepo structure:
|
||||
packages/
|
||||
├── page-agent/ # AI agent (npm: page-agent)
|
||||
├── llms/ # LLM client (npm: @page-agent/llms)
|
||||
├── page-controller/ # DOM operations (npm: @page-agent/page-controller)
|
||||
├── ui/ # Panel & Mask & Mouse Animation (npm: @page-agent/ui)
|
||||
├── page-controller/ # DOM operations & Visual Mask (npm: @page-agent/page-controller)
|
||||
├── ui/ # Panel & i18n (npm: @page-agent/ui)
|
||||
├── cdn/ # CDN IIFE builds (npm: @page-agent/cdn)
|
||||
└── website/ # Demo & Documentation site
|
||||
```
|
||||
|
||||
|
||||
Generated
+264
-251
@@ -1,43 +1,44 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "root",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
"packages/ui",
|
||||
"packages/llms",
|
||||
"packages/page-agent",
|
||||
"packages/cdn",
|
||||
"packages/website"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^20.3.0",
|
||||
"@commitlint/config-conventional": "^20.3.0",
|
||||
"@commitlint/cli": "^20.3.1",
|
||||
"@commitlint/config-conventional": "^20.3.1",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@microsoft/api-extractor": "^7.55.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
|
||||
"@types/node": "^25.0.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/node": "^25.0.7",
|
||||
"concurrently": "^9.2.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-react-dom": "^2.5.1",
|
||||
"eslint-plugin-react-dom": "^2.5.7",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.26",
|
||||
"eslint-plugin-react-x": "^2.5.1",
|
||||
"globals": "^16.4.0",
|
||||
"eslint-plugin-react-x": "^2.5.7",
|
||||
"globals": "^17.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.4",
|
||||
"prettier": "^3.7.4",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.52.0",
|
||||
"typescript-eslint": "^8.53.0",
|
||||
"unplugin-dts": "^1.0.0-beta.6",
|
||||
"vite": "^7.3.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-css-injected-by-js": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -316,17 +317,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/cli": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.3.0.tgz",
|
||||
"integrity": "sha512-HXO8YVfqdBK+MnlX2zqNrv6waGYPs6Ysjm5W2Y0GMagWXwiIKx7C8dcIX9ca+QdHq4WA0lcMnZLQ0pzQh1piZg==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.3.1.tgz",
|
||||
"integrity": "sha512-NtInjSlyev/+SLPvx/ulz8hRE25Wf5S9dLNDcIwazq0JyB4/w1ROF/5nV0ObPTX8YpRaKYeKtXDYWqumBNHWsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/format": "^20.2.0",
|
||||
"@commitlint/lint": "^20.3.0",
|
||||
"@commitlint/load": "^20.3.0",
|
||||
"@commitlint/read": "^20.2.0",
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/format": "^20.3.1",
|
||||
"@commitlint/lint": "^20.3.1",
|
||||
"@commitlint/load": "^20.3.1",
|
||||
"@commitlint/read": "^20.3.1",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"tinyexec": "^1.0.0",
|
||||
"yargs": "^17.0.0"
|
||||
},
|
||||
@@ -338,13 +339,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/config-conventional": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.0.tgz",
|
||||
"integrity": "sha512-g1OXVl6E2v0xF1Ru2RpxQ+Vfy7XUcUsCmLKzGUrhFLS4hSNykje0QSy6djBtzOiOBQCepBrmIlqx/gRlzrSh5A==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.1.tgz",
|
||||
"integrity": "sha512-NCzwvxepstBZbmVXsvg49s+shCxlJDJPWxXqONVcAtJH9wWrOlkMQw/zyl+dJmt8lyVopt5mwQ3mR5M2N2rUWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"conventional-changelog-conventionalcommits": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -352,13 +353,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/config-validator": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.2.0.tgz",
|
||||
"integrity": "sha512-SQCBGsL9MFk8utWNSthdxd9iOD1pIVZSHxGBwYIGfd67RTjxqzFOSAYeQVXOu3IxRC3YrTOH37ThnTLjUlyF2w==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.3.1.tgz",
|
||||
"integrity": "sha512-ErVLC/IsHhcvxCyh+FXo7jy12/nkQySjWXYgCoQbZLkFp4hysov8KS6CdxBB0cWjbZWjvNOKBMNoUVqkmGmahw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"ajv": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -366,13 +367,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/ensure": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.2.0.tgz",
|
||||
"integrity": "sha512-+8TgIGv89rOWyt3eC6lcR1H7hqChAKkpawytlq9P1i/HYugFRVqgoKJ8dhd89fMnlrQTLjA5E97/4sF09QwdoA==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.3.1.tgz",
|
||||
"integrity": "sha512-h664FngOEd7bHAm0j8MEKq+qm2mH+V+hwJiIE2bWcw3pzJMlO0TPKtk0ATyRAtV6jQw+xviRYiIjjSjfajiB5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"lodash.snakecase": "^4.1.1",
|
||||
@@ -394,13 +395,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/format": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.2.0.tgz",
|
||||
"integrity": "sha512-PhNoLNhxpfIBlW/i90uZ3yG3hwSSYx7n4d9Yc+2FAorAHS0D9btYRK4ZZXX+Gm3W5tDtu911ow/eWRfcRVgNWg==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.3.1.tgz",
|
||||
"integrity": "sha512-jfsjGPFTd2Yti2YHwUH4SPRPbWKAJAwrfa3eNa9bXEdrXBb9mCwbIrgYX38LdEJK9zLJ3AsLBP4/FLEtxyu2AA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"chalk": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -408,13 +409,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/is-ignored": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.2.0.tgz",
|
||||
"integrity": "sha512-Lz0OGeZCo/QHUDLx5LmZc0EocwanneYJUM8z0bfWexArk62HKMLfLIodwXuKTO5y0s6ddXaTexrYHs7v96EOmw==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.3.1.tgz",
|
||||
"integrity": "sha512-tWwAoh93QvAhxgp99CzCuHD86MgxE4NBtloKX+XxQxhfhSwHo7eloiar/yzx53YW9eqSLP95zgW2KDDk4/WX+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"semver": "^7.6.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -422,32 +423,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/lint": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.3.0.tgz",
|
||||
"integrity": "sha512-X19HOGU5nRo6i9DIY0kG0mhgtvpn1UGO1D6aLX1ILLyeqSM5yJyMcrRqNj8SLgeSeUDODhLY9QYsBIG0LdNHkA==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.3.1.tgz",
|
||||
"integrity": "sha512-LaOtrQ24+6SfUaWg8A+a+Wc77bvLbO5RIr6iy9F7CI3/0iq1uPEWgGRCwqWTuLGHkZDAcwaq0gZ01zpwZ1jCGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/is-ignored": "^20.2.0",
|
||||
"@commitlint/parse": "^20.2.0",
|
||||
"@commitlint/rules": "^20.3.0",
|
||||
"@commitlint/types": "^20.2.0"
|
||||
"@commitlint/is-ignored": "^20.3.1",
|
||||
"@commitlint/parse": "^20.3.1",
|
||||
"@commitlint/rules": "^20.3.1",
|
||||
"@commitlint/types": "^20.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v18"
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/load": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.3.0.tgz",
|
||||
"integrity": "sha512-amkdVZTXp5R65bsRXRSCwoNXbJHR2aAIY/RGFkoyd63t8UEwqEgT3f0MgeLqYw4hwXyq+TYXKdaW133E29pnGQ==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.3.1.tgz",
|
||||
"integrity": "sha512-YDD9XA2XhgYgbjju8itZ/weIvOOobApDqwlPYCX5NLO/cPtw2UMO5Cmn44Ks8RQULUVI5fUT6roKvyxcoLbNmw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/config-validator": "^20.2.0",
|
||||
"@commitlint/config-validator": "^20.3.1",
|
||||
"@commitlint/execute-rule": "^20.0.0",
|
||||
"@commitlint/resolve-extends": "^20.2.0",
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/resolve-extends": "^20.3.1",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"chalk": "^5.3.0",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"cosmiconfig-typescript-loader": "^6.1.0",
|
||||
@@ -470,13 +471,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/parse": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.2.0.tgz",
|
||||
"integrity": "sha512-LXStagGU1ivh07X7sM+hnEr4BvzFYn1iBJ6DRg2QsIN8lBfSzyvkUcVCDwok9Ia4PWiEgei5HQjju6xfJ1YaSQ==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.3.1.tgz",
|
||||
"integrity": "sha512-TuUTdbLpyUNLgDzLDYlI2BeTE6V/COZbf3f8WwsV0K6eq/2nSpNTMw7wHtXb+YxeY9wwxBp/Ldad4P+YIxHJoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"conventional-changelog-angular": "^7.0.0",
|
||||
"conventional-commits-parser": "^5.0.0"
|
||||
},
|
||||
@@ -485,14 +486,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/read": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.2.0.tgz",
|
||||
"integrity": "sha512-+SjF9mxm5JCbe+8grOpXCXMMRzAnE0WWijhhtasdrpJoAFJYd5UgRTj/oCq5W3HJTwbvTOsijEJ0SUGImECD7Q==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.3.1.tgz",
|
||||
"integrity": "sha512-nCmJAdIg3OdNVUpQW0Idk/eF/vfOo2W2xzmvRmNeptLrzFK7qhwwl/kIwy1Q1LZrKHUFNj7PGNpIT5INbgZWzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/top-level": "^20.0.0",
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"git-raw-commits": "^4.0.0",
|
||||
"minimist": "^1.2.8",
|
||||
"tinyexec": "^1.0.0"
|
||||
@@ -502,14 +503,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/resolve-extends": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.2.0.tgz",
|
||||
"integrity": "sha512-KVoLDi9BEuqeq+G0wRABn4azLRiCC22/YHR2aCquwx6bzCHAIN8hMt3Nuf1VFxq/c8ai6s8qBxE8+ZD4HeFTlQ==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.3.1.tgz",
|
||||
"integrity": "sha512-iGTGeyaoDyHDEZNjD8rKeosjSNs8zYanmuowY4ful7kFI0dnY4b5QilVYaFQJ6IM27S57LAeH5sKSsOHy4bw5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/config-validator": "^20.2.0",
|
||||
"@commitlint/types": "^20.2.0",
|
||||
"@commitlint/config-validator": "^20.3.1",
|
||||
"@commitlint/types": "^20.3.1",
|
||||
"global-directory": "^4.0.1",
|
||||
"import-meta-resolve": "^4.0.0",
|
||||
"lodash.mergewith": "^4.6.2",
|
||||
@@ -520,16 +521,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/rules": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.3.0.tgz",
|
||||
"integrity": "sha512-TGgXN/qBEhbzVD13crE1l7YSMJRrbPbUL0OBZALbUM5ER36RZmiZRu2ud2W/AA7HO9YLBRbyx6YVi2t/2Be0yQ==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.3.1.tgz",
|
||||
"integrity": "sha512-/uic4P+4jVNpqQxz02+Y6vvIC0A2J899DBztA1j6q3f3MOKwydlNrojSh0dQmGDxxT1bXByiRtDhgFnOFnM6Pg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@commitlint/ensure": "^20.2.0",
|
||||
"@commitlint/ensure": "^20.3.1",
|
||||
"@commitlint/message": "^20.0.0",
|
||||
"@commitlint/to-lines": "^20.0.0",
|
||||
"@commitlint/types": "^20.2.0"
|
||||
"@commitlint/types": "^20.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v18"
|
||||
@@ -559,9 +560,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/types": {
|
||||
"version": "20.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.2.0.tgz",
|
||||
"integrity": "sha512-KTy0OqRDLR5y/zZMnizyx09z/rPlPC/zKhYgH8o/q6PuAjoQAKlRfY4zzv0M64yybQ//6//4H1n14pxaLZfUnA==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.3.1.tgz",
|
||||
"integrity": "sha512-VmIFV/JkBRhDRRv7N5B7zEUkNZIx9Mp+8Pe65erz0rKycXLsi8Epcw0XJ+btSeRXgTzE7DyOyA9bkJ9mn/yqVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1057,16 +1058,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-react/ast": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-2.5.1.tgz",
|
||||
"integrity": "sha512-l+pEb3kS/RhFN+8ZOhL5+P5+Yj7iw+ta7+abUBNH+5PMyiixlnAJlJ7Wy+rsTGxjUJtzEff3CzSr06V7qwJuwQ==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-2.5.7.tgz",
|
||||
"integrity": "sha512-yQvKvyIzo9DPKFUOtFSEfpX0He763G1jFf/LQcIY9xoSOWP9pQ2X8ADNE6ZnmIc0L5zZLQ9nF/H0+MGh2NF3fA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@typescript-eslint/types": "^8.51.0",
|
||||
"@typescript-eslint/typescript-estree": "^8.51.0",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "^8.53.0",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"string-ts": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1078,19 +1079,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-react/core": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-2.5.1.tgz",
|
||||
"integrity": "sha512-48YQYhjHga0TAv7FI0uGGT+X3SITec89fJLKSTqIEa7jTLpXTV56B+B/V/IcfZ0s8I0lNVEmtHFTXaibAxmfyw==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-2.5.7.tgz",
|
||||
"integrity": "sha512-LqY4YV4Z/uiymdy2OLXkjsEVnoDjoFsxJZI4GWsZ9aBgGWYAsuoKKUG0bdMLp1U9jwMaY0OY9bYgBB5R1ANkoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/ast": "2.5.1",
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@eslint-react/shared": "2.5.1",
|
||||
"@eslint-react/var": "2.5.1",
|
||||
"@typescript-eslint/scope-manager": "^8.51.0",
|
||||
"@typescript-eslint/types": "^8.51.0",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/ast": "2.5.7",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@eslint-react/shared": "2.5.7",
|
||||
"@eslint-react/var": "2.5.7",
|
||||
"@typescript-eslint/scope-manager": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"birecord": "^0.1.1",
|
||||
"ts-pattern": "^5.9.0"
|
||||
},
|
||||
@@ -1103,9 +1104,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-react/eff": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-2.5.1.tgz",
|
||||
"integrity": "sha512-henDz7sTB67Q+YPb+Kiy0UpD7eHLvDtB2nOrRtHLTatS/LFr7wVr4J2muQPkPzY9KFF8pv811nrwW58FuDphPg==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-2.5.7.tgz",
|
||||
"integrity": "sha512-oxd8eP+dykyOUmQUEItnOZhitlKyo9yFq8Ne0k9+CJeV0Pk1db1+lNcDWswWYoeb1IFEloqHo6y63fVxQXvJew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1113,14 +1114,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-react/shared": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-2.5.1.tgz",
|
||||
"integrity": "sha512-JSTnpZdBu57ZvrJkBwUZXgkBW8iPiQfSiQMYHEjVFR2NHWFgcOd9qYJLX7mJLrdcw/j+GgRQeOAGok0a0xcxGA==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-2.5.7.tgz",
|
||||
"integrity": "sha512-ZjKO3rNDoLN2ZTn3PxmMcgoASPNiL3umK1BHQvYwCXqqnXlLEouu7sxK6htOvDu1NC+Dbwsqpu4JGUY7t0XQTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"ts-pattern": "^5.9.0",
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
@@ -1133,17 +1134,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-react/var": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-2.5.1.tgz",
|
||||
"integrity": "sha512-O3rbdK8pHKZ4NYPD39Zq7zhWu76dvhyeroqG8nQzf667F/FdHGpGhsCPJOeIU5L/ooYO/3/Qz2vJD1pB5JeROQ==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-2.5.7.tgz",
|
||||
"integrity": "sha512-VWoyZENbK+OV70s/sE9E6T4HD0WO6lsg2CnYjjpKBKBDOBW67SRdIlM7dUPKzqQKsj6p4yNh59eQ5iKaH82LLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/ast": "2.5.1",
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@typescript-eslint/scope-manager": "^8.51.0",
|
||||
"@typescript-eslint/types": "^8.51.0",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/ast": "2.5.7",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@typescript-eslint/scope-manager": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"ts-pattern": "^5.9.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1597,6 +1598,10 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@page-agent/cdn": {
|
||||
"resolved": "packages/cdn",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@page-agent/llms": {
|
||||
"resolved": "packages/llms",
|
||||
"link": true
|
||||
@@ -3458,9 +3463,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.1.tgz",
|
||||
"integrity": "sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.2.tgz",
|
||||
"integrity": "sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -3556,9 +3561,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
||||
"version": "25.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.7.tgz",
|
||||
"integrity": "sha512-C/er7DlIZgRJO7WtTdYovjIFzGsz0I95UlMyR9anTb4aCpBSRWe5Jc1/RvLKUfzmOxHPGjSE5+63HgLtndxU4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3566,9 +3571,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
|
||||
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3586,17 +3591,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz",
|
||||
"integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz",
|
||||
"integrity": "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.52.0",
|
||||
"@typescript-eslint/type-utils": "8.52.0",
|
||||
"@typescript-eslint/utils": "8.52.0",
|
||||
"@typescript-eslint/visitor-keys": "8.52.0",
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/type-utils": "8.53.0",
|
||||
"@typescript-eslint/utils": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
@@ -3609,7 +3614,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.52.0",
|
||||
"@typescript-eslint/parser": "^8.53.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
@@ -3625,16 +3630,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz",
|
||||
"integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.0.tgz",
|
||||
"integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.52.0",
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/typescript-estree": "8.52.0",
|
||||
"@typescript-eslint/visitor-keys": "8.52.0",
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3650,14 +3655,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz",
|
||||
"integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.0.tgz",
|
||||
"integrity": "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.52.0",
|
||||
"@typescript-eslint/types": "^8.52.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3672,14 +3677,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz",
|
||||
"integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz",
|
||||
"integrity": "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/visitor-keys": "8.52.0"
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -3690,9 +3695,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz",
|
||||
"integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz",
|
||||
"integrity": "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3707,15 +3712,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz",
|
||||
"integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz",
|
||||
"integrity": "sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/typescript-estree": "8.52.0",
|
||||
"@typescript-eslint/utils": "8.52.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0",
|
||||
"@typescript-eslint/utils": "8.53.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
@@ -3732,9 +3737,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz",
|
||||
"integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.0.tgz",
|
||||
"integrity": "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3746,16 +3751,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz",
|
||||
"integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz",
|
||||
"integrity": "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.52.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.52.0",
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/visitor-keys": "8.52.0",
|
||||
"@typescript-eslint/project-service": "8.53.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^9.0.5",
|
||||
"semver": "^7.7.3",
|
||||
@@ -3800,16 +3805,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz",
|
||||
"integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.0.tgz",
|
||||
"integrity": "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.52.0",
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/typescript-estree": "8.52.0"
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -3824,13 +3829,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz",
|
||||
"integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz",
|
||||
"integrity": "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.52.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3911,9 +3916,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ai-motion": {
|
||||
"version": "0.4.7",
|
||||
"resolved": "https://registry.npmjs.org/ai-motion/-/ai-motion-0.4.7.tgz",
|
||||
"integrity": "sha512-yuFtVgWeaxKyvfSFocA01Mr4UbRvwwy7HWZ6Mq9NM/yccAkqp3UCGdWX6AflNB8cU27PgwTBiMRfx6C9di7xkQ==",
|
||||
"version": "0.4.8",
|
||||
"resolved": "https://registry.npmjs.org/ai-motion/-/ai-motion-0.4.8.tgz",
|
||||
"integrity": "sha512-9nQVjf8lrYQWSW0sZdb9bES4x8Ei1w9C0S62po2KvRbMjslmjm0WMeszyW771SEauDnu4YGWqurLZ/gCkCTosQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
@@ -4860,20 +4865,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react-dom": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-dom/-/eslint-plugin-react-dom-2.5.1.tgz",
|
||||
"integrity": "sha512-7x3k0bQwtTOSxjReH11buYL6aSMopBzSaWr2bU/LPOuy1dDxvqE6HC2Y6QPEyZjLJ1ppUSKwRmcX85uiF0jHAA==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-dom/-/eslint-plugin-react-dom-2.5.7.tgz",
|
||||
"integrity": "sha512-KHCU7KRXeLYlrrXKtInpCqHzt+nYhIGpYIuMM4Q0FyGBOcswRS2Gv9PhN+yQqDI9PRBhnjCuRf+q2Y3ZrYWjsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/ast": "2.5.1",
|
||||
"@eslint-react/core": "2.5.1",
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@eslint-react/shared": "2.5.1",
|
||||
"@eslint-react/var": "2.5.1",
|
||||
"@typescript-eslint/scope-manager": "^8.51.0",
|
||||
"@typescript-eslint/types": "^8.51.0",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/ast": "2.5.7",
|
||||
"@eslint-react/core": "2.5.7",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@eslint-react/shared": "2.5.7",
|
||||
"@eslint-react/var": "2.5.7",
|
||||
"@typescript-eslint/scope-manager": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"compare-versions": "^6.1.1",
|
||||
"string-ts": "^2.3.1",
|
||||
"ts-pattern": "^5.9.0"
|
||||
@@ -4917,21 +4922,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react-x": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-x/-/eslint-plugin-react-x-2.5.1.tgz",
|
||||
"integrity": "sha512-w3KtEG8M7sFRh/psUmep3H+i2O/5Nn9wI8GXjlqzwp8zLb4ax66Ue+1oecLTzY/0j/M1xRfB3hzk5oio54RwLA==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-x/-/eslint-plugin-react-x-2.5.7.tgz",
|
||||
"integrity": "sha512-EU+h7eUHNg9ikLaGOa0Tjsc/o4FO2KdyT3M97TkQmaKc9tzpcy8nlPQp/czBMbUuxAgbj2495wzOvCZ0fEK4mw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-react/ast": "2.5.1",
|
||||
"@eslint-react/core": "2.5.1",
|
||||
"@eslint-react/eff": "2.5.1",
|
||||
"@eslint-react/shared": "2.5.1",
|
||||
"@eslint-react/var": "2.5.1",
|
||||
"@typescript-eslint/scope-manager": "^8.51.0",
|
||||
"@typescript-eslint/type-utils": "^8.51.0",
|
||||
"@typescript-eslint/types": "^8.51.0",
|
||||
"@typescript-eslint/utils": "^8.51.0",
|
||||
"@eslint-react/ast": "2.5.7",
|
||||
"@eslint-react/core": "2.5.7",
|
||||
"@eslint-react/eff": "2.5.7",
|
||||
"@eslint-react/shared": "2.5.7",
|
||||
"@eslint-react/var": "2.5.7",
|
||||
"@typescript-eslint/scope-manager": "^8.53.0",
|
||||
"@typescript-eslint/type-utils": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"@typescript-eslint/utils": "^8.53.0",
|
||||
"compare-versions": "^6.1.1",
|
||||
"is-immutable-type": "^5.0.1",
|
||||
"string-ts": "^2.3.1",
|
||||
@@ -5310,13 +5315,13 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.24.0",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.0.tgz",
|
||||
"integrity": "sha512-ggTMRkIDPc76lHmM+dRT1MmVfFV6t/y+jkWjWuzR7FG5xRvnAAl/5wFPjzSkLE8Nu5E5uIQRCNxmIXtWJVo6XQ==",
|
||||
"version": "12.26.1",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.26.1.tgz",
|
||||
"integrity": "sha512-Uzc8wGldU4FpmGotthjjcj0SZhigcODjqvKT7lzVZHsmYkzQMFfMIv0vHQoXCeoe/Ahxqp4by4A6QbzFA/lblw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.24.0",
|
||||
"motion-utils": "^12.23.28",
|
||||
"motion-dom": "^12.24.11",
|
||||
"motion-utils": "^12.24.10",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -5457,9 +5462,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
|
||||
"integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz",
|
||||
"integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5543,9 +5548,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.7.3",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.3.tgz",
|
||||
"integrity": "sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==",
|
||||
"version": "25.7.4",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.4.tgz",
|
||||
"integrity": "sha512-hRkpEblXXcXSNbw8mBNq9042OEetgyB/ahc/X17uV/khPwzV+uB8RHceHh3qavyrkPJvmXFKXME2Sy1E0KjAfw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -6523,12 +6528,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/motion": {
|
||||
"version": "12.24.0",
|
||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.24.0.tgz",
|
||||
"integrity": "sha512-FAnpl/DhCFct3p+T2hCjAY95w+3EggUk3b8Ql4jQ6mmdRbEaGD1000goqWeEYVoD7mqx9H0As9ORmRADD4LQAw==",
|
||||
"version": "12.26.1",
|
||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.26.1.tgz",
|
||||
"integrity": "sha512-IVhzx9HOQTiJ9ykthMOlZPnLwrkXziN5Q/yebsqBYlFJb2rHP8yhmKc8O/YUT9byPJlxOeqkzfNYCrVKZx8vqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.24.0",
|
||||
"framer-motion": "^12.26.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -6549,18 +6554,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.24.0",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.0.tgz",
|
||||
"integrity": "sha512-RD2kZkFd/GH4fITI8IJvypGgn0vIu5vkrJaXIAkYqORGs5P0CKDHKNvswmoY1H+tbUAOPSh6VtUqoAmc/3Gvig==",
|
||||
"version": "12.24.11",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.11.tgz",
|
||||
"integrity": "sha512-DlWOmsXMJrV8lzZyd+LKjG2CXULUs++bkq8GZ2Sr0R0RRhs30K2wtY+LKiTjhmJU3W61HK+rB0GLz6XmPvTA1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.28"
|
||||
"motion-utils": "^12.24.10"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.28",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.28.tgz",
|
||||
"integrity": "sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==",
|
||||
"version": "12.24.10",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz",
|
||||
"integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
@@ -6935,9 +6940,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "16.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.1.tgz",
|
||||
"integrity": "sha512-Hks6UIRZWW4c+qDAnx1csVsCGYeIR4MoBGQgJ+NUoNnO6qLxXuf8zu0xdcinyXUORgGzCdRsexxO1Xzv3sTdnw==",
|
||||
"version": "16.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.2.tgz",
|
||||
"integrity": "sha512-GG/SBVxx9dvrO1uCs8VYdKfOP8NEBUhNP+2VDQLCifRJ8DL1qPq296k2ACNGyZMDe7iyIlz/LMJTQOs8HXSRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7174,9 +7179,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/simple-icons": {
|
||||
"version": "16.4.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.4.0.tgz",
|
||||
"integrity": "sha512-8CKtCvx1Zq3L0CBsR4RR1MjGCXkXbzdspwl2yCxs8oWkstbzj2+DatRKDee/tuj3Ffd/2CDzwEky9RgG2yggew==",
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.5.0.tgz",
|
||||
"integrity": "sha512-72nn0oHADKx6Hknu7q6M0vfL8LiCUMKABOHane2+4xdqaFBSHfNNBjuZioihiqVQMz7IvVle4NKAM0IlXvl/9A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -7577,16 +7582,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz",
|
||||
"integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.0.tgz",
|
||||
"integrity": "sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.52.0",
|
||||
"@typescript-eslint/parser": "8.52.0",
|
||||
"@typescript-eslint/typescript-estree": "8.52.0",
|
||||
"@typescript-eslint/utils": "8.52.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.53.0",
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0",
|
||||
"@typescript-eslint/utils": "8.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -7772,9 +7777,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
|
||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8140,9 +8145,17 @@
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/cdn": {
|
||||
"name": "@page-agent/cdn",
|
||||
"version": "0.0.22",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"page-agent": "0.0.22"
|
||||
}
|
||||
},
|
||||
"packages/llms": {
|
||||
"name": "@page-agent/llms",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
@@ -8150,32 +8163,32 @@
|
||||
}
|
||||
},
|
||||
"packages/page-agent": {
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/llms": "0.0.20",
|
||||
"@page-agent/page-controller": "0.0.20",
|
||||
"@page-agent/ui": "0.0.20",
|
||||
"@page-agent/llms": "0.0.22",
|
||||
"@page-agent/page-controller": "0.0.22",
|
||||
"@page-agent/ui": "0.0.22",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
},
|
||||
"packages/page-controller": {
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "0.0.20",
|
||||
"license": "MIT"
|
||||
"version": "0.0.22",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
}
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@page-agent/ui",
|
||||
"version": "0.0.20",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.7"
|
||||
}
|
||||
"version": "0.0.22",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/website": {
|
||||
"name": "@page-agent/website",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
@@ -8185,23 +8198,23 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.562.0",
|
||||
"motion": "^12.24.0",
|
||||
"motion": "^12.26.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"rough-notation": "^0.5.1",
|
||||
"simple-icons": "^16.4.0",
|
||||
"simple-icons": "^16.5.0",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.1",
|
||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||
"i18next": "^25.7.3",
|
||||
"i18next": "^25.7.4",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-i18next": "^16.5.1",
|
||||
"react-i18next": "^16.5.2",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"wouter": "^3.9.0"
|
||||
|
||||
+11
-10
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
"packages/ui",
|
||||
"packages/llms",
|
||||
"packages/page-agent",
|
||||
"packages/cdn",
|
||||
"packages/website"
|
||||
],
|
||||
"description": "AI-powered UI agent for web applications",
|
||||
@@ -34,28 +35,28 @@
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^20.3.0",
|
||||
"@commitlint/config-conventional": "^20.3.0",
|
||||
"@commitlint/cli": "^20.3.1",
|
||||
"@commitlint/config-conventional": "^20.3.1",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@microsoft/api-extractor": "^7.55.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
|
||||
"@types/node": "^25.0.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/node": "^25.0.7",
|
||||
"concurrently": "^9.2.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-react-dom": "^2.5.1",
|
||||
"eslint-plugin-react-dom": "^2.5.7",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.26",
|
||||
"eslint-plugin-react-x": "^2.5.1",
|
||||
"globals": "^16.4.0",
|
||||
"eslint-plugin-react-x": "^2.5.7",
|
||||
"globals": "^17.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.4",
|
||||
"prettier": "^3.7.4",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.52.0",
|
||||
"typescript-eslint": "^8.53.0",
|
||||
"unplugin-dts": "^1.0.0-beta.6",
|
||||
"vite": "^7.3.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-css-injected-by-js": "^3.5.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@page-agent/cdn",
|
||||
"private": false,
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"description": "CDN builds for page-agent - IIFE bundles for script tag usage",
|
||||
"author": "Simon<gaomeng1900>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alibaba/page-agent.git"
|
||||
},
|
||||
"homepage": "https://alibaba.github.io/page-agent/",
|
||||
"scripts": {
|
||||
"build": "vite build && vite build --mode demo"
|
||||
},
|
||||
"dependencies": {
|
||||
"page-agent": "0.0.22"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Demo CDN build for page-agent
|
||||
* Auto-initializes with built-in demo API for testing
|
||||
*
|
||||
* Usage:
|
||||
* <script src="https://unpkg.com/@page-agent/cdn/dist/page-agent.demo.js"></script>
|
||||
*/
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
// Clean up existing instances to prevent multiple injections from bookmarklet
|
||||
if (window.pageAgent) {
|
||||
window.pageAgent.dispose()
|
||||
}
|
||||
|
||||
// Mount to global window object
|
||||
window.PageAgent = PageAgent
|
||||
|
||||
// Export for IIFE
|
||||
export { PageAgent }
|
||||
|
||||
console.log('🚀 page-agent.js loaded!')
|
||||
|
||||
const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
const DEMO_BASE_URL = 'https://hwcxiuzfylggtcktqgij.supabase.co/functions/v1/llm-testing-proxy'
|
||||
const DEMO_API_KEY = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
// Demo warning
|
||||
console.log(
|
||||
'%c⚠️ DEMO MODE %c\n' +
|
||||
'This build uses a shared testing LLM with rate limits.\n' +
|
||||
'For production, use page-agent.js with your own API key:\n' +
|
||||
'https://alibaba.github.io/page-agent/#/docs/features/models',
|
||||
'background: #f59e0b; color: #000; font-size: 14px; font-weight: bold; padding: 4px 8px; border-radius: 4px;',
|
||||
'color: #f59e0b; font-size: 12px;'
|
||||
)
|
||||
|
||||
// in case document.x is not ready yet
|
||||
setTimeout(() => {
|
||||
const currentScript = document.currentScript
|
||||
|
||||
if (currentScript) {
|
||||
console.log('🚀 page-agent.js detected current script:', currentScript.src)
|
||||
const url = new URL(currentScript.src)
|
||||
const model = url.searchParams.get('model') || DEMO_MODEL
|
||||
const baseURL = url.searchParams.get('baseURL') || DEMO_BASE_URL
|
||||
const apiKey = url.searchParams.get('apiKey') || DEMO_API_KEY
|
||||
const language = url.searchParams.get('lang') || 'zh-CN'
|
||||
const config = { model, baseURL, apiKey, language }
|
||||
window.pageAgent = new PageAgent(config)
|
||||
} else {
|
||||
console.log('🚀 page-agent.js no current script detected, using default demo config')
|
||||
window.pageAgent = new PageAgent()
|
||||
}
|
||||
|
||||
console.log('🚀 page-agent.js initialized with config:', window.pageAgent.config)
|
||||
|
||||
window.pageAgent.panel.show()
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Full CDN build for page-agent
|
||||
* Exposes PageAgent class for manual instantiation and configuration
|
||||
*
|
||||
* Usage:
|
||||
* <script src="https://unpkg.com/@page-agent/cdn/dist/page-agent.js"></script>
|
||||
* <script>
|
||||
* const agent = new PageAgent({ model: 'gpt-4o', apiKey: 'your-key' })
|
||||
* agent.panel.show()
|
||||
* </script>
|
||||
*/
|
||||
import { PageAgent } from 'page-agent'
|
||||
|
||||
export { PageAgent }
|
||||
@@ -0,0 +1,42 @@
|
||||
// @ts-nocheck
|
||||
import { dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { defineConfig } from 'vite'
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
/**
|
||||
* CDN IIFE builds for page-agent
|
||||
*
|
||||
* Build targets (via --mode):
|
||||
* - demo: page-agent.demo.js with auto-init and built-in API
|
||||
* - full (default): page-agent.js exposes PageAgent class only
|
||||
*/
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isDemo = mode === 'demo'
|
||||
const entry = isDemo ? resolve(__dirname, 'src/demo.js') : resolve(__dirname, 'src/full.js')
|
||||
const fileName = isDemo ? 'page-agent.demo' : 'page-agent'
|
||||
|
||||
return {
|
||||
plugins: [cssInjectedByJsPlugin({ relativeCSSInjection: true })],
|
||||
publicDir: false,
|
||||
esbuild: {
|
||||
keepNames: true,
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry,
|
||||
name: 'PageAgent',
|
||||
fileName: () => `${fileName}.js`,
|
||||
formats: ['iife'],
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist'),
|
||||
emptyOutDir: !isDemo, // only empty on first build (full)
|
||||
minify: false,
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/llms",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-llms.js",
|
||||
"module": "./dist/lib/page-agent-llms.js",
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
/**
|
||||
* OpenAI Client implementation
|
||||
* @note This client is only for demonstrating how to implement a LLM client.
|
||||
* @note Use OpenAILenientClient instead.
|
||||
*/
|
||||
import { InvokeError, InvokeErrorType } from './errors'
|
||||
import type { InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||
import { modelPatch, zodToOpenAITool } from './utils'
|
||||
|
||||
/**
|
||||
* @deprecated Use OpenAILenientClient instead.
|
||||
* Client for OpenAI compatible APIs
|
||||
*/
|
||||
export class OpenAIClient implements LLMClient {
|
||||
config: LLMConfig
|
||||
config: Required<LLMConfig>
|
||||
private fetch: typeof globalThis.fetch
|
||||
|
||||
constructor(config: LLMConfig) {
|
||||
constructor(config: Required<LLMConfig>) {
|
||||
this.config = config
|
||||
this.fetch = config.customFetch
|
||||
}
|
||||
|
||||
async invoke(
|
||||
messages: Message[],
|
||||
tools: Record<string, Tool>,
|
||||
abortSignal?: AbortSignal
|
||||
abortSignal?: AbortSignal,
|
||||
options?: InvokeOptions
|
||||
): Promise<InvokeResult> {
|
||||
// 1. Convert tools to OpenAI format
|
||||
const openaiTools = Object.entries(tools).map(([name, tool]) => zodToOpenAITool(name, tool))
|
||||
const openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))
|
||||
|
||||
// Build request body
|
||||
const requestBody: Record<string, unknown> = {
|
||||
model: this.config.model,
|
||||
temperature: this.config.temperature,
|
||||
messages,
|
||||
tools: openaiTools,
|
||||
parallel_tool_calls: false,
|
||||
// Require tool call: specific tool if provided, otherwise any tool
|
||||
tool_choice: options?.toolChoiceName
|
||||
? { type: 'function', function: { name: options.toolChoiceName } }
|
||||
: 'required',
|
||||
}
|
||||
|
||||
// 2. Call API
|
||||
let response: Response
|
||||
try {
|
||||
response = await fetch(`${this.config.baseURL}/chat/completions`, {
|
||||
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.config.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(
|
||||
modelPatch({
|
||||
model: this.config.model,
|
||||
temperature: this.config.temperature,
|
||||
messages,
|
||||
|
||||
tools: openaiTools,
|
||||
// tool_choice: 'required',
|
||||
tool_choice: { type: 'function', function: { name: 'AgentOutput' } },
|
||||
|
||||
// model specific params
|
||||
|
||||
// reasoning_effort: 'minimal',
|
||||
// verbosity: 'low',
|
||||
parallel_tool_calls: false,
|
||||
})
|
||||
),
|
||||
body: JSON.stringify(modelPatch(requestBody)),
|
||||
signal: abortSignal,
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
// Network error
|
||||
console.error(error)
|
||||
throw new InvokeError(InvokeErrorType.NETWORK_ERROR, 'Network request failed', error)
|
||||
}
|
||||
|
||||
@@ -92,77 +90,94 @@ export class OpenAIClient implements LLMClient {
|
||||
)
|
||||
}
|
||||
|
||||
// 4. Parse and validate response
|
||||
const data = await response.json()
|
||||
|
||||
// 4. Check finish_reason
|
||||
const choice = data.choices?.[0]
|
||||
if (!choice) {
|
||||
throw new InvokeError(InvokeErrorType.UNKNOWN, 'No choices in response', data)
|
||||
}
|
||||
|
||||
// Check finish_reason
|
||||
switch (choice.finish_reason) {
|
||||
case 'tool_calls':
|
||||
// ✅ Normal
|
||||
case 'function_call': // gemini
|
||||
case 'stop': // some models use this even with tool calls
|
||||
break
|
||||
case 'length':
|
||||
// ⚠️ Token limit reached
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.CONTEXT_LENGTH,
|
||||
'Response truncated: max tokens reached',
|
||||
data
|
||||
'Response truncated: max tokens reached'
|
||||
)
|
||||
case 'content_filter':
|
||||
// ❌ Content filtered
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.CONTENT_FILTER,
|
||||
'Content filtered by safety system',
|
||||
data
|
||||
)
|
||||
case 'stop':
|
||||
// ❌ Did not call tool (we require tool call)
|
||||
throw new InvokeError(InvokeErrorType.NO_TOOL_CALL, 'Model did not call any tool', data)
|
||||
throw new InvokeError(InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system')
|
||||
default:
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.UNKNOWN,
|
||||
`Unexpected finish_reason: ${choice.finish_reason}`,
|
||||
data
|
||||
`Unexpected finish_reason: ${choice.finish_reason}`
|
||||
)
|
||||
}
|
||||
|
||||
// 5. Parse tool call
|
||||
const toolCall = choice.message?.tool_calls?.[0]
|
||||
if (!toolCall) {
|
||||
throw new InvokeError(InvokeErrorType.NO_TOOL_CALL, 'No tool call found in response', data)
|
||||
}
|
||||
// Apply normalizeResponse if provided (for fixing format issues automatically)
|
||||
const normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data
|
||||
const normalizedChoice = (normalizedData as any).choices?.[0]
|
||||
|
||||
const toolName = toolCall.function.name
|
||||
const tool = tools[toolName]
|
||||
if (!tool) {
|
||||
throw new InvokeError(InvokeErrorType.UNKNOWN, `Tool ${toolName} not found`, data)
|
||||
}
|
||||
|
||||
// 6. Parse and validate arguments
|
||||
let toolArgs: unknown
|
||||
try {
|
||||
toolArgs = JSON.parse(toolCall.function.arguments)
|
||||
} catch (e) {
|
||||
throw new InvokeError(InvokeErrorType.INVALID_TOOL_ARGS, 'Invalid JSON in tool arguments', e)
|
||||
}
|
||||
|
||||
// Validate against zod schema
|
||||
const validation = tool.inputSchema.safeParse(toolArgs)
|
||||
if (!validation.success) {
|
||||
// Get tool name from response
|
||||
const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name
|
||||
if (!toolCallName) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
`Tool arguments validation failed: ${validation.error.message}`,
|
||||
validation.error
|
||||
InvokeErrorType.NO_TOOL_CALL,
|
||||
'No tool call found in response',
|
||||
normalizedData
|
||||
)
|
||||
}
|
||||
|
||||
// 7. Execute tool
|
||||
const tool = tools[toolCallName]
|
||||
if (!tool) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.UNKNOWN,
|
||||
`Tool "${toolCallName}" not found in tools`,
|
||||
normalizedData
|
||||
)
|
||||
}
|
||||
|
||||
// Extract and parse tool arguments
|
||||
const argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments
|
||||
if (!argString) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'No tool call arguments found',
|
||||
normalizedData
|
||||
)
|
||||
}
|
||||
|
||||
let parsedArgs: unknown
|
||||
try {
|
||||
parsedArgs = JSON.parse(argString)
|
||||
} catch (error) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'Failed to parse tool arguments as JSON',
|
||||
error
|
||||
)
|
||||
}
|
||||
|
||||
// Validate with schema
|
||||
const validation = tool.inputSchema.safeParse(parsedArgs)
|
||||
if (!validation.success) {
|
||||
console.error(validation.error)
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'Tool arguments validation failed',
|
||||
validation.error
|
||||
)
|
||||
}
|
||||
const toolInput = validation.data
|
||||
|
||||
// 5. Execute tool
|
||||
let toolResult: unknown
|
||||
try {
|
||||
toolResult = await tool.execute(validation.data)
|
||||
toolResult = await tool.execute(toolInput)
|
||||
} catch (e) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.TOOL_EXECUTION_ERROR,
|
||||
@@ -171,12 +186,11 @@ export class OpenAIClient implements LLMClient {
|
||||
)
|
||||
}
|
||||
|
||||
// 8. Return result (including cache tokens)
|
||||
// Return result
|
||||
return {
|
||||
toolCall: {
|
||||
// id: toolCall.id,
|
||||
name: toolName,
|
||||
args: validation.data as Record<string, unknown>,
|
||||
name: toolCallName,
|
||||
args: toolInput,
|
||||
},
|
||||
toolResult,
|
||||
usage: {
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* OpenAI Client implementation
|
||||
*/
|
||||
import { InvokeError, InvokeErrorType } from './errors'
|
||||
import type { InvokeResult, LLMClient, LLMConfig, MacroToolInput, Message, Tool } from './types'
|
||||
import { lenientParseMacroToolCall, modelPatch, zodToOpenAITool } from './utils'
|
||||
|
||||
export class OpenAIClient implements LLMClient {
|
||||
config: Required<LLMConfig>
|
||||
private fetch: typeof globalThis.fetch
|
||||
|
||||
constructor(config: Required<LLMConfig>) {
|
||||
this.config = config
|
||||
this.fetch = config.customFetch
|
||||
}
|
||||
|
||||
async invoke(
|
||||
messages: Message[],
|
||||
tools: { AgentOutput: Tool<MacroToolInput> },
|
||||
abortSignal?: AbortSignal
|
||||
): Promise<InvokeResult> {
|
||||
// 1. Convert tools to OpenAI format
|
||||
const openaiTools = Object.entries(tools).map(([name, tool]) => zodToOpenAITool(name, tool))
|
||||
|
||||
// 2. Call API
|
||||
let response: Response
|
||||
try {
|
||||
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.config.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(
|
||||
modelPatch({
|
||||
model: this.config.model,
|
||||
temperature: this.config.temperature,
|
||||
messages,
|
||||
|
||||
tools: openaiTools,
|
||||
// tool_choice: 'required',
|
||||
tool_choice: { type: 'function', function: { name: 'AgentOutput' } },
|
||||
parallel_tool_calls: false,
|
||||
})
|
||||
),
|
||||
signal: abortSignal,
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
// Network error
|
||||
console.error(error)
|
||||
throw new InvokeError(InvokeErrorType.NETWORK_ERROR, 'Network request failed', error)
|
||||
}
|
||||
|
||||
// 3. Handle HTTP errors
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch()
|
||||
const errorMessage =
|
||||
(errorData as { error?: { message?: string } }).error?.message || response.statusText
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.AUTH_ERROR,
|
||||
`Authentication failed: ${errorMessage}`,
|
||||
errorData
|
||||
)
|
||||
}
|
||||
if (response.status === 429) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.RATE_LIMIT,
|
||||
`Rate limit exceeded: ${errorMessage}`,
|
||||
errorData
|
||||
)
|
||||
}
|
||||
if (response.status >= 500) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.SERVER_ERROR,
|
||||
`Server error: ${errorMessage}`,
|
||||
errorData
|
||||
)
|
||||
}
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.UNKNOWN,
|
||||
`HTTP ${response.status}: ${errorMessage}`,
|
||||
errorData
|
||||
)
|
||||
}
|
||||
|
||||
// parse response
|
||||
|
||||
const data = await response.json()
|
||||
const tool = tools.AgentOutput
|
||||
const macroToolInput = lenientParseMacroToolCall(data, tool.inputSchema as any)
|
||||
|
||||
// Execute tool
|
||||
let toolResult: unknown
|
||||
try {
|
||||
toolResult = await tool.execute(macroToolInput)
|
||||
} catch (e) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.TOOL_EXECUTION_ERROR,
|
||||
`Tool execution failed: ${(e as Error).message}`,
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
// Return result (including cache tokens)
|
||||
return {
|
||||
toolCall: {
|
||||
// id: toolCall.id,
|
||||
name: 'AgentOutput',
|
||||
args: macroToolInput,
|
||||
},
|
||||
toolResult,
|
||||
usage: {
|
||||
promptTokens: data.usage?.prompt_tokens ?? 0,
|
||||
completionTokens: data.usage?.completion_tokens ?? 0,
|
||||
totalTokens: data.usage?.total_tokens ?? 0,
|
||||
cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
|
||||
reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens,
|
||||
},
|
||||
rawResponse: data,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,4 @@
|
||||
/**
|
||||
* @topic LLM 与主流程的隔离
|
||||
* @reasoning
|
||||
* 将 llm 的调用和主流程分开是复杂的,
|
||||
* 因为 agent 的 tool call 通常集成在 llm 模块中,而而先得到 llm 返回,然后处理工具调用
|
||||
* tools 和 llm 调用的逻辑不可避免地耦合在一起,tool 的执行又和主流程耦合在一起
|
||||
* 而 history 的维护和更新逻辑,又必须嵌入多轮 tool call 中
|
||||
* @reasoning
|
||||
* - 放弃框架提供的自动的多轮调用,每轮调用都由主流程发起
|
||||
* - 理想情况下,llm 调用应该获得 structured output,然后由额外的模块触发 tool call,目前模型和框架都无法实现
|
||||
* - 当前只能将 llm api 和 本地 tool call 耦合在一起,不关心其中的衔接方式
|
||||
* @conclusion
|
||||
* - @llm responsibility boundary:
|
||||
* - call llm api with given messages and tools
|
||||
* - invoke tool call and get the result of the tool
|
||||
* - return the result to main loop
|
||||
* - @main_loop responsibility boundary:
|
||||
* - maintain all behaviors of an **agent**
|
||||
* @conclusion
|
||||
* - 这里的 llm 模块不是 agent,只负责一轮 llm 调用和工具调用,无状态
|
||||
*/
|
||||
/**
|
||||
* @topic 结构化输出
|
||||
* @facts
|
||||
* - 几乎所有模型都支持 tool call schema
|
||||
* - 几乎所有模型都支持返回 json
|
||||
* - 只有 openAI/grok/gemini 支持 schema 并保证格式
|
||||
* - 主流模型都支持 tool_choice: required
|
||||
* - 除了 qwen 必须指定一个函数名 (9月上新后支持)
|
||||
* @conclusion
|
||||
* - 永远使用 tool call 来返回结构化数据,禁止模型直接返回(视为出错)
|
||||
* - 不能假设 tool 参数合法,必须有修复机制,而且修复也应该使用 tool call 返回
|
||||
*/
|
||||
import { OpenAIClient } from './OpenAILenientClient'
|
||||
import { OpenAIClient } from './OpenAIClient'
|
||||
import {
|
||||
DEFAULT_API_KEY,
|
||||
DEFAULT_BASE_URL,
|
||||
@@ -40,27 +7,9 @@ import {
|
||||
LLM_MAX_RETRIES,
|
||||
} from './constants'
|
||||
import { InvokeError } from './errors'
|
||||
import type {
|
||||
AgentBrain,
|
||||
InvokeResult,
|
||||
LLMClient,
|
||||
LLMConfig,
|
||||
MacroToolInput,
|
||||
MacroToolResult,
|
||||
Message,
|
||||
Tool,
|
||||
} from './types'
|
||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||
|
||||
export type {
|
||||
AgentBrain,
|
||||
InvokeResult,
|
||||
LLMClient,
|
||||
LLMConfig,
|
||||
MacroToolInput,
|
||||
MacroToolResult,
|
||||
Message,
|
||||
Tool,
|
||||
}
|
||||
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
|
||||
|
||||
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
|
||||
return {
|
||||
@@ -93,11 +42,12 @@ export class LLM extends EventTarget {
|
||||
async invoke(
|
||||
messages: Message[],
|
||||
tools: Record<string, Tool>,
|
||||
abortSignal: AbortSignal
|
||||
abortSignal: AbortSignal,
|
||||
options?: InvokeOptions
|
||||
): Promise<InvokeResult> {
|
||||
return await withRetry(
|
||||
async () => {
|
||||
const result = await this.client.invoke(messages, tools, abortSignal)
|
||||
const result = await this.client.invoke(messages, tools, abortSignal, options)
|
||||
|
||||
return result
|
||||
},
|
||||
|
||||
+20
-34
@@ -32,6 +32,24 @@ export interface Tool<TParams = any, TResult = any> {
|
||||
execute: (args: TParams) => Promise<TResult>
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke options for LLM call
|
||||
*/
|
||||
export interface InvokeOptions {
|
||||
/**
|
||||
* Force LLM to call a specific tool by name.
|
||||
* If provided: tool_choice = { type: 'function', function: { name: toolChoiceName } }
|
||||
* If not provided: tool_choice = 'required' (must call some tool, but model chooses which)
|
||||
*/
|
||||
toolChoiceName?: string
|
||||
/**
|
||||
* Response normalization function.
|
||||
* Called before parsing the response.
|
||||
* Used to fix various response format errors from the model.
|
||||
*/
|
||||
normalizeResponse?: (response: any) => any
|
||||
}
|
||||
|
||||
/**
|
||||
* LLM Client interface
|
||||
* Note: Does not use generics because each tool in the tools array has different types
|
||||
@@ -40,7 +58,8 @@ export interface LLMClient {
|
||||
invoke(
|
||||
messages: Message[],
|
||||
tools: Record<string, Tool>,
|
||||
abortSignal?: AbortSignal
|
||||
abortSignal?: AbortSignal,
|
||||
options?: InvokeOptions
|
||||
): Promise<InvokeResult>
|
||||
}
|
||||
|
||||
@@ -82,36 +101,3 @@ export interface LLMConfig {
|
||||
*/
|
||||
customFetch?: typeof globalThis.fetch
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent brain state - the reflection-before-action model
|
||||
*
|
||||
* Every tool call must first reflect on:
|
||||
* - evaluation_previous_goal: How well did the previous action achieve its goal?
|
||||
* - memory: Key information to remember for future steps
|
||||
* - next_goal: What should be accomplished in the next action?
|
||||
*/
|
||||
export interface AgentBrain {
|
||||
// thinking?: string
|
||||
evaluation_previous_goal: string
|
||||
memory: string
|
||||
next_goal: string
|
||||
}
|
||||
|
||||
/**
|
||||
* MacroTool input structure
|
||||
*
|
||||
* This is the core abstraction that enforces the "reflection-before-action" mental model.
|
||||
* Before executing any action, the LLM must output its reasoning state.
|
||||
*/
|
||||
export interface MacroToolInput extends AgentBrain {
|
||||
action: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* MacroTool output structure
|
||||
*/
|
||||
export interface MacroToolResult {
|
||||
input: MacroToolInput
|
||||
output: string
|
||||
}
|
||||
|
||||
+18
-175
@@ -4,8 +4,7 @@
|
||||
import chalk from 'chalk'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { InvokeError, InvokeErrorType } from './errors'
|
||||
import type { MacroToolInput, Tool } from './types'
|
||||
import type { Tool } from './types'
|
||||
|
||||
function debug(message: string) {
|
||||
console.debug(chalk.gray('[LLM]'), message)
|
||||
@@ -26,176 +25,6 @@ export function zodToOpenAITool(name: string, tool: Tool) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Although some models cannot guarantee correct response. Common issues are fixable:
|
||||
* - Instead of returning a proper tool call. Return the tool call parameters in the message content.
|
||||
* - Returned tool calls or messages don't follow the nested MacroToolInput format.
|
||||
*/
|
||||
export function lenientParseMacroToolCall(
|
||||
responseData: any,
|
||||
inputSchema: z.ZodObject<MacroToolInput & Record<string, any>>
|
||||
): MacroToolInput {
|
||||
// check
|
||||
const choice = responseData.choices?.[0]
|
||||
if (!choice) {
|
||||
throw new InvokeError(InvokeErrorType.UNKNOWN, 'No choices in response', responseData)
|
||||
}
|
||||
|
||||
// check
|
||||
switch (choice.finish_reason) {
|
||||
case 'tool_calls':
|
||||
case 'function_call': // gemini
|
||||
case 'stop': // will try a robust parse
|
||||
// ✅ Normal
|
||||
break
|
||||
case 'length':
|
||||
// ⚠️ Token limit reached
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.CONTEXT_LENGTH,
|
||||
'Response truncated: max tokens reached'
|
||||
)
|
||||
case 'content_filter':
|
||||
// ❌ Content filtered
|
||||
throw new InvokeError(InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system')
|
||||
default:
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.UNKNOWN,
|
||||
`Unexpected finish_reason: ${choice.finish_reason}`
|
||||
)
|
||||
}
|
||||
|
||||
// Extract action schema from MacroToolInput schema
|
||||
const actionSchema = inputSchema.shape.action
|
||||
if (!actionSchema) {
|
||||
throw new Error('inputSchema must have an "action" field')
|
||||
}
|
||||
|
||||
// patch stopReason mis-format
|
||||
|
||||
let arg: string | null = null
|
||||
|
||||
// try to use tool call
|
||||
const toolCall = choice.message?.tool_calls?.[0]?.function
|
||||
arg = toolCall?.arguments ?? null
|
||||
|
||||
if (arg && toolCall.name !== 'AgentOutput') {
|
||||
// TODO: check if toolCall.name is a valid action name
|
||||
// case: instead of AgentOutput, the model returned a action name as tool call
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #1 fixing incorrect tool call'))
|
||||
let tmpArg
|
||||
try {
|
||||
tmpArg = JSON.parse(arg)
|
||||
} catch (error) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'Failed to parse tool arguments as JSON',
|
||||
error
|
||||
)
|
||||
}
|
||||
arg = JSON.stringify({ action: { [toolCall.name]: tmpArg } })
|
||||
}
|
||||
|
||||
if (!arg) {
|
||||
// try to use message content as JSON
|
||||
arg = choice.message?.content.trim() || null
|
||||
}
|
||||
|
||||
if (!arg) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.NO_TOOL_CALL,
|
||||
'No tool call or content found in response',
|
||||
responseData
|
||||
)
|
||||
}
|
||||
|
||||
// make sure is valid JSON
|
||||
|
||||
let parsedArgs: any
|
||||
try {
|
||||
parsedArgs = JSON.parse(arg)
|
||||
} catch (error) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'Failed to parse tool arguments as JSON',
|
||||
error
|
||||
)
|
||||
}
|
||||
|
||||
// patch incomplete formats
|
||||
|
||||
if (parsedArgs.action || parsedArgs.evaluation_previous_goal || parsedArgs.next_goal) {
|
||||
// case: nested MacroToolInput format (correct format)
|
||||
|
||||
// some models may give a empty action (they may think reasoning and action should be separate)
|
||||
if (!parsedArgs.action) {
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #2 fixing incorrect tool call'))
|
||||
parsedArgs.action = {
|
||||
wait: { seconds: 1 },
|
||||
}
|
||||
}
|
||||
} else if (parsedArgs.type && parsedArgs.function) {
|
||||
// case: upper level function call format provided. only keep its arguments
|
||||
// TODO: check if function name is a valid action name
|
||||
if (parsedArgs.function.name !== 'AgentOutput')
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
`Expected function name "AgentOutput", got "${parsedArgs.function.name}"`,
|
||||
null
|
||||
)
|
||||
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #3 fixing incorrect tool call'))
|
||||
parsedArgs = parsedArgs.function.arguments
|
||||
} else if (parsedArgs.name && parsedArgs.arguments) {
|
||||
// case: upper level function call format provided. only keep its arguments
|
||||
// TODO: check if function name is a valid action name
|
||||
if (parsedArgs.name !== 'AgentOutput')
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
`Expected function name "AgentOutput", got "${parsedArgs.name}"`,
|
||||
null
|
||||
)
|
||||
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #4 fixing incorrect tool call'))
|
||||
parsedArgs = parsedArgs.arguments
|
||||
} else {
|
||||
// case: only action parameters provided, wrap into MacroToolInput
|
||||
// TODO: check if action name is valid
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #5 fixing incorrect tool call'))
|
||||
parsedArgs = { action: parsedArgs } as MacroToolInput
|
||||
}
|
||||
|
||||
// make sure it's not wrapped as string
|
||||
if (typeof parsedArgs === 'string') {
|
||||
console.log(chalk.yellow('lenientParseMacroToolCall: #6 fixing incorrect tool call'))
|
||||
try {
|
||||
parsedArgs = JSON.parse(parsedArgs)
|
||||
} catch (error) {
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
'Failed to parse nested tool arguments as JSON',
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const validation = inputSchema.safeParse(parsedArgs)
|
||||
if (validation.success) {
|
||||
return validation.data as unknown as MacroToolInput
|
||||
} else {
|
||||
const action = parsedArgs.action ?? {}
|
||||
const actionName = Object.keys(action)[0] || 'unknown'
|
||||
const actionArgs = JSON.stringify(action[actionName] || 'unknown')
|
||||
|
||||
// TODO: check if action name is valid. give a readable error message
|
||||
|
||||
throw new InvokeError(
|
||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||
`Tool arguments validation failed: action "${actionName}" with args ${actionArgs}`,
|
||||
validation.error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch model specific parameters
|
||||
*/
|
||||
@@ -205,11 +34,25 @@ export function modelPatch(body: Record<string, any>) {
|
||||
|
||||
const modelName = normalizeModelName(model)
|
||||
|
||||
if (modelName.startsWith('qwen')) {
|
||||
debug('Applying Qwen patch: use higher temperature for auto fixing')
|
||||
body.temperature = Math.max(body.temperature || 0, 1.0)
|
||||
}
|
||||
|
||||
if (modelName.startsWith('claude')) {
|
||||
debug('Applying Claude patch: change tool_choice and disable thinking')
|
||||
body.tool_choice = { type: 'tool', name: 'AgentOutput' }
|
||||
debug('Applying Claude patch: disable thinking')
|
||||
body.thinking = { type: 'disabled' }
|
||||
// body.reasoning = { enabled: 'disabled' }
|
||||
|
||||
// Convert tool_choice to Claude format
|
||||
if (body.tool_choice === 'required') {
|
||||
// 'required' -> { type: 'any' } (must call some tool)
|
||||
debug('Applying Claude patch: convert tool_choice "required" to { type: "any" }')
|
||||
body.tool_choice = { type: 'any' }
|
||||
} else if (body.tool_choice?.function?.name) {
|
||||
// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }
|
||||
debug('Applying Claude patch: convert tool_choice format')
|
||||
body.tool_choice = { type: 'tool', name: body.tool_choice.function.name }
|
||||
}
|
||||
}
|
||||
|
||||
if (modelName.startsWith('grok')) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "page-agent",
|
||||
"private": false,
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent.js",
|
||||
"module": "./dist/esm/page-agent.js",
|
||||
@@ -46,8 +46,8 @@
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5",
|
||||
"@page-agent/llms": "0.0.20",
|
||||
"@page-agent/page-controller": "0.0.20",
|
||||
"@page-agent/ui": "0.0.20"
|
||||
"@page-agent/llms": "0.0.22",
|
||||
"@page-agent/page-controller": "0.0.22",
|
||||
"@page-agent/ui": "0.0.22"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,9 @@
|
||||
* Copyright (C) 2025 Alibaba Group Holding Limited
|
||||
* All rights reserved.
|
||||
*/
|
||||
import {
|
||||
type AgentBrain,
|
||||
LLM,
|
||||
type MacroToolInput,
|
||||
type MacroToolResult,
|
||||
type Tool,
|
||||
} from '@page-agent/llms'
|
||||
import { LLM, type Tool } from '@page-agent/llms'
|
||||
import { PageController } from '@page-agent/page-controller'
|
||||
import { Panel, SimulatorMask } from '@page-agent/ui'
|
||||
import { Panel } from '@page-agent/ui'
|
||||
import chalk from 'chalk'
|
||||
import zod from 'zod'
|
||||
|
||||
@@ -18,15 +12,46 @@ import type { PageAgentConfig } from './config'
|
||||
import { MAX_STEPS } from './config/constants'
|
||||
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
||||
import { tools } from './tools'
|
||||
import { trimLines, uid, waitUntil } from './utils'
|
||||
import { normalizeResponse, trimLines, uid, waitUntil } from './utils'
|
||||
import { assert } from './utils/assert'
|
||||
|
||||
/**
|
||||
* Agent brain state - the reflection-before-action model
|
||||
*
|
||||
* Every tool call must first reflect on:
|
||||
* - evaluation_previous_goal: How well did the previous action achieve its goal?
|
||||
* - memory: Key information to remember for future steps
|
||||
* - next_goal: What should be accomplished in the next action?
|
||||
*/
|
||||
export interface AgentReflection {
|
||||
evaluation_previous_goal: string
|
||||
memory: string
|
||||
next_goal: string
|
||||
}
|
||||
|
||||
/**
|
||||
* MacroTool input structure
|
||||
*
|
||||
* This is the core abstraction that enforces the "reflection-before-action" mental model.
|
||||
* Before executing any action, the LLM must output its reasoning state.
|
||||
*/
|
||||
export interface MacroToolInput extends Partial<AgentReflection> {
|
||||
action: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* MacroTool output structure
|
||||
*/
|
||||
export interface MacroToolResult {
|
||||
input: MacroToolInput
|
||||
output: string
|
||||
}
|
||||
|
||||
export type { PageAgentConfig }
|
||||
export { tool, type PageAgentTool } from './tools'
|
||||
export type { AgentBrain, MacroToolInput, MacroToolResult }
|
||||
|
||||
export interface AgentHistory {
|
||||
brain: AgentBrain
|
||||
brain: Partial<AgentReflection>
|
||||
action: {
|
||||
name: string
|
||||
input: any
|
||||
@@ -67,8 +92,6 @@ export class PageAgent extends EventTarget {
|
||||
/** PageController for DOM operations */
|
||||
pageController: PageController
|
||||
|
||||
/** Fullscreen mask */
|
||||
mask = new SimulatorMask()
|
||||
/** History records */
|
||||
history: AgentHistory[] = []
|
||||
|
||||
@@ -89,8 +112,11 @@ export class PageAgent extends EventTarget {
|
||||
})
|
||||
this.tools = new Map(tools)
|
||||
|
||||
// Initialize PageController with config
|
||||
this.pageController = new PageController(this.config)
|
||||
// Initialize PageController with config (mask enabled by default)
|
||||
this.pageController = new PageController({
|
||||
...this.config,
|
||||
enableMask: this.config.enableMask ?? true,
|
||||
})
|
||||
|
||||
// Listen to LLM events
|
||||
this.#llmRetryListener = (e) => {
|
||||
@@ -124,9 +150,6 @@ export class PageAgent extends EventTarget {
|
||||
window.addEventListener('beforeunload', this.#beforeUnloadListener)
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo maybe return something?
|
||||
*/
|
||||
async execute(task: string): Promise<ExecutionResult> {
|
||||
if (!task) throw new Error('Task is required')
|
||||
this.task = task
|
||||
@@ -140,7 +163,7 @@ export class PageAgent extends EventTarget {
|
||||
await onBeforeTask.call(this)
|
||||
|
||||
// Show mask and panel
|
||||
this.mask.show()
|
||||
this.pageController.showMask()
|
||||
|
||||
this.panel.show()
|
||||
this.panel.reset()
|
||||
@@ -183,7 +206,11 @@ export class PageAgent extends EventTarget {
|
||||
},
|
||||
],
|
||||
{ AgentOutput: this.#packMacroTool() },
|
||||
this.#abortController.signal
|
||||
this.#abortController.signal,
|
||||
{
|
||||
toolChoiceName: 'AgentOutput',
|
||||
normalizeResponse,
|
||||
}
|
||||
)
|
||||
|
||||
const macroResult = result.toolResult as MacroToolResult
|
||||
@@ -279,6 +306,7 @@ export class PageAgent extends EventTarget {
|
||||
})
|
||||
|
||||
return {
|
||||
description: 'You MUST call this tool every step. Outputs your reflections and next action.',
|
||||
inputSchema: macroToolSchema as zod.ZodType<MacroToolInput>,
|
||||
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
||||
// abort
|
||||
@@ -458,7 +486,7 @@ export class PageAgent extends EventTarget {
|
||||
// Task completed
|
||||
this.panel.update({ type: 'completed' })
|
||||
|
||||
this.mask.hide()
|
||||
this.pageController.hideMask()
|
||||
|
||||
this.#abortController.abort()
|
||||
}
|
||||
@@ -469,9 +497,7 @@ export class PageAgent extends EventTarget {
|
||||
const pi = await this.pageController.getPageInfo()
|
||||
const viewportExpansion = await this.pageController.getViewportExpansion()
|
||||
|
||||
this.mask.wrapper.style.pointerEvents = 'none'
|
||||
await this.pageController.updateTree()
|
||||
this.mask.wrapper.style.pointerEvents = 'auto'
|
||||
|
||||
let simplifiedHTML = await this.pageController.getSimplifiedHTML()
|
||||
|
||||
@@ -518,7 +544,6 @@ export class PageAgent extends EventTarget {
|
||||
this.disposed = true
|
||||
this.pageController.dispose()
|
||||
this.panel.dispose()
|
||||
this.mask.dispose()
|
||||
this.history = []
|
||||
this.#abortController.abort(reason ?? 'PageAgent disposed')
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import chalk from 'chalk'
|
||||
|
||||
/**
|
||||
* Normalize LLM response and fix common format issues.
|
||||
*
|
||||
* Handles:
|
||||
* - No tool_calls but JSON in message.content (fallback)
|
||||
* - Model returns action name as tool call instead of AgentOutput
|
||||
* - Arguments wrapped as double JSON string
|
||||
* - Nested function call format
|
||||
* - Missing action field (fallback to wait)
|
||||
* - etc.
|
||||
*/
|
||||
export function normalizeResponse(response: any): any {
|
||||
let resolvedArguments = null as any
|
||||
|
||||
const choice = (response as { choices?: Choice[] }).choices?.[0]
|
||||
if (!choice) throw new Error('No choices in response')
|
||||
|
||||
const message = choice.message
|
||||
if (!message) throw new Error('No message in choice')
|
||||
|
||||
const toolCall = message.tool_calls?.[0]
|
||||
|
||||
// fix level and location of arguments
|
||||
|
||||
if (toolCall?.function?.arguments) {
|
||||
resolvedArguments = safeJsonParse(toolCall.function.arguments)
|
||||
|
||||
// case: sometimes the model only returns the action level
|
||||
if (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #1: fixing tool_call`))
|
||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||
}
|
||||
} else {
|
||||
// case: sometimes the model returns json in content instead of tool_calls
|
||||
if (message.content) {
|
||||
const content = message.content.trim()
|
||||
const jsonInContent = retrieveJsonFromString(content)
|
||||
if (jsonInContent) {
|
||||
resolvedArguments = safeJsonParse(jsonInContent)
|
||||
|
||||
// case: sometimes the content json includes upper level wrapper
|
||||
if (resolvedArguments?.name === 'AgentOutput') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #2: fixing tool_call`))
|
||||
resolvedArguments = safeJsonParse(resolvedArguments.arguments)
|
||||
}
|
||||
|
||||
// case: sometimes even 2-levels of wrapping
|
||||
if (resolvedArguments?.type === 'function') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #3: fixing tool_call`))
|
||||
resolvedArguments = safeJsonParse(resolvedArguments.function.arguments)
|
||||
}
|
||||
|
||||
// case: and sometimes action level only
|
||||
// todo: needs better detection logic
|
||||
if (
|
||||
!resolvedArguments?.action &&
|
||||
!resolvedArguments?.evaluation_previous_goal &&
|
||||
!resolvedArguments?.memory &&
|
||||
!resolvedArguments?.next_goal &&
|
||||
!resolvedArguments?.thinking
|
||||
) {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #4: fixing tool_call`))
|
||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||
}
|
||||
} else {
|
||||
throw new Error('No tool_call and the message content does not contain valid JSON')
|
||||
}
|
||||
} else {
|
||||
throw new Error('No tool_call nor message content is present')
|
||||
}
|
||||
}
|
||||
|
||||
// fix double stringified arguments
|
||||
resolvedArguments = safeJsonParse(resolvedArguments)
|
||||
if (resolvedArguments.action) {
|
||||
resolvedArguments.action = safeJsonParse(resolvedArguments.action)
|
||||
}
|
||||
|
||||
// fix incomplete formats
|
||||
if (!resolvedArguments.action) {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #5: fixing tool_call`))
|
||||
resolvedArguments.action = { name: 'wait', input: { seconds: 1 } }
|
||||
}
|
||||
|
||||
// pack back to standard format
|
||||
return {
|
||||
...response,
|
||||
choices: [
|
||||
{
|
||||
...choice,
|
||||
message: {
|
||||
...message,
|
||||
tool_calls: [
|
||||
{
|
||||
...(toolCall || {}),
|
||||
function: {
|
||||
...(toolCall?.function || {}),
|
||||
name: 'AgentOutput',
|
||||
arguments: JSON.stringify(resolvedArguments),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely parse JSON, return original input if not json.
|
||||
*/
|
||||
function safeJsonParse(input: any): any {
|
||||
if (typeof input === 'string') {
|
||||
try {
|
||||
return JSON.parse(input.trim())
|
||||
} catch {
|
||||
return input
|
||||
}
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and parse JSON from a string.
|
||||
* - Treat content between the first `{` and the last `}` as JSON.
|
||||
* - Try to parse that content as JSON and return the parsed value (object/array/primitive) if successful, otherwise return null.
|
||||
*/
|
||||
function retrieveJsonFromString(str: string): any {
|
||||
try {
|
||||
const json = /({[\s\S]*})/.exec(str) ?? []
|
||||
if (json.length === 0) {
|
||||
return null
|
||||
}
|
||||
return JSON.parse(json[0]!)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
interface Choice {
|
||||
message?: {
|
||||
role?: 'assistant'
|
||||
content?: string
|
||||
tool_calls?: {
|
||||
id?: string
|
||||
type?: 'function'
|
||||
function?: {
|
||||
name?: string
|
||||
arguments?: string
|
||||
}
|
||||
}[]
|
||||
}
|
||||
index?: 0
|
||||
finish_reason?: 'tool_calls'
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export { normalizeResponse } from './autoFixer'
|
||||
|
||||
/**
|
||||
* Wait until condition becomes true
|
||||
* @returns Returns when condition becomes true, throws otherwise
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-controller.js",
|
||||
"module": "./dist/lib/page-controller.js",
|
||||
@@ -34,5 +34,8 @@
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "node -e \"const fs=require('fs');['LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,18 @@ import { VIEWPORT_EXPANSION } from './constants'
|
||||
import * as dom from './dom'
|
||||
import type { FlatDomTree, InteractiveElementDomNode } from './dom/dom_tree/type'
|
||||
import { getPageInfo } from './dom/getPageInfo'
|
||||
import { SimulatorMask } from './mask/SimulatorMask'
|
||||
import { patchReact } from './patches/react'
|
||||
|
||||
export { SimulatorMask }
|
||||
|
||||
/**
|
||||
* Configuration for PageController
|
||||
*/
|
||||
export interface PageControllerConfig extends dom.DomConfig {
|
||||
viewportExpansion?: number
|
||||
/** Enable visual mask overlay during operations (default: false) */
|
||||
enableMask?: boolean
|
||||
}
|
||||
|
||||
interface ActionResult {
|
||||
@@ -64,12 +69,19 @@ export class PageController extends EventTarget {
|
||||
/** last time the tree was updated */
|
||||
private lastTimeUpdate = 0
|
||||
|
||||
/** Visual mask overlay for blocking user interaction during automation */
|
||||
private mask: SimulatorMask | null = null
|
||||
|
||||
constructor(config: PageControllerConfig = {}) {
|
||||
super()
|
||||
|
||||
this.config = config
|
||||
|
||||
patchReact(this)
|
||||
|
||||
if (config.enableMask) {
|
||||
this.mask = new SimulatorMask()
|
||||
}
|
||||
}
|
||||
|
||||
// ======= State Queries =======
|
||||
@@ -136,12 +148,18 @@ export class PageController extends EventTarget {
|
||||
/**
|
||||
* Update DOM tree, returns simplified HTML for LLM.
|
||||
* This is the main method to refresh the page state.
|
||||
* Automatically bypasses mask during DOM extraction if enabled.
|
||||
*/
|
||||
async updateTree(): Promise<string> {
|
||||
this.dispatchEvent(new Event('beforeUpdate'))
|
||||
|
||||
this.lastTimeUpdate = Date.now()
|
||||
|
||||
// Temporarily bypass mask to allow DOM extraction
|
||||
if (this.mask) {
|
||||
this.mask.wrapper.style.pointerEvents = 'none'
|
||||
}
|
||||
|
||||
dom.cleanUpHighlights()
|
||||
|
||||
const blacklist = [
|
||||
@@ -162,6 +180,11 @@ export class PageController extends EventTarget {
|
||||
this.elementTextMap.clear()
|
||||
this.elementTextMap = dom.getElementTextMap(this.simplifiedHTML)
|
||||
|
||||
// Restore mask blocking
|
||||
if (this.mask) {
|
||||
this.mask.wrapper.style.pointerEvents = 'auto'
|
||||
}
|
||||
|
||||
this.dispatchEvent(new Event('afterUpdate'))
|
||||
|
||||
return this.simplifiedHTML
|
||||
@@ -326,6 +349,24 @@ export class PageController extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
// ======= Mask Operations =======
|
||||
|
||||
/**
|
||||
* Show the visual mask overlay.
|
||||
* Only works if enableMask was set to true in config.
|
||||
*/
|
||||
showMask(): void {
|
||||
this.mask?.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the visual mask overlay.
|
||||
* Only works if enableMask was set to true in config.
|
||||
*/
|
||||
hideMask(): void {
|
||||
this.mask?.hide()
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose and clean up resources
|
||||
*/
|
||||
@@ -335,5 +376,7 @@ export class PageController extends EventTarget {
|
||||
this.selectorMap.clear()
|
||||
this.elementTextMap.clear()
|
||||
this.simplifiedHTML = '<EMPTY>'
|
||||
this.mask?.dispose()
|
||||
this.mask = null
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export default defineConfig({
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||
rollupOptions: {
|
||||
external: ['@page-agent/*'],
|
||||
external: ['@page-agent/*', 'ai-motion'],
|
||||
},
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/ui",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-ui.js",
|
||||
"module": "./dist/lib/page-agent-ui.js",
|
||||
@@ -15,7 +15,7 @@
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"description": "UI components for page-agent - Panel, SimulatorMask, and i18n",
|
||||
"description": "UI components for page-agent - Panel and i18n",
|
||||
"keywords": [
|
||||
"page-agent",
|
||||
"ui",
|
||||
@@ -34,8 +34,5 @@
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "node -e \"const fs=require('fs');['LICENSE'].forEach(f=>fs.copyFileSync('../../'+f,f))\"",
|
||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { Panel, type PanelConfig, type PanelUpdate } from './Panel'
|
||||
export { SimulatorMask } from './SimulatorMask'
|
||||
export { UIState, type Step, type AgentStatus } from './UIState'
|
||||
export { I18n, type SupportedLanguage, type TranslationKey } from './i18n'
|
||||
|
||||
@@ -29,7 +29,7 @@ export default defineConfig({
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||
rollupOptions: {
|
||||
external: ['ai-motion'],
|
||||
external: [],
|
||||
},
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/website",
|
||||
"private": true,
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -11,14 +11,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.1",
|
||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||
"i18next": "^25.7.3",
|
||||
"i18next": "^25.7.4",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-i18next": "^16.5.1",
|
||||
"react-i18next": "^16.5.2",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"wouter": "^3.9.0"
|
||||
@@ -32,10 +32,10 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.562.0",
|
||||
"motion": "^12.24.0",
|
||||
"motion": "^12.26.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"rough-notation": "^0.5.1",
|
||||
"simple-icons": "^16.4.0",
|
||||
"simple-icons": "^16.5.0",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.4.0"
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
|
||||
{
|
||||
title: t('nav.features'),
|
||||
items: [
|
||||
{ title: t('nav.models'), path: '/docs/features/model-integration' },
|
||||
{ title: t('nav.models'), path: '/docs/features/models' },
|
||||
{ title: t('nav.custom_tools'), path: '/docs/features/custom-tools' },
|
||||
{ title: t('nav.knowledge_injection'), path: '/docs/features/custom-instructions' },
|
||||
{ title: t('nav.data_masking'), path: '/docs/features/data-masking' },
|
||||
|
||||
+10
-4
@@ -4,13 +4,19 @@ import CodeEditor from '@/components/CodeEditor'
|
||||
|
||||
// Recommended models: lightweight with excellent tool call capabilities
|
||||
const MODELS = {
|
||||
recommended: ['gpt-4.1-mini', 'claude-haiku-4.5', 'gemini-3-flash', 'deepseek-3.2', 'gpt-5.2'],
|
||||
verified: [
|
||||
recommended: [
|
||||
'gpt-4.1-mini',
|
||||
'claude-haiku-4.5',
|
||||
'gemini-3-flash',
|
||||
'deepseek-3.2',
|
||||
'gpt-5.2',
|
||||
'qwen-3-max',
|
||||
],
|
||||
verified: [
|
||||
'qwen-3-plus',
|
||||
'gpt-4.1',
|
||||
'gpt-5',
|
||||
'gpt-5-mini',
|
||||
'gpt-5.1',
|
||||
'grok-4',
|
||||
'grok-code-fast',
|
||||
'claude-sonnet-3.5',
|
||||
@@ -21,7 +27,7 @@ const MODELS = {
|
||||
],
|
||||
}
|
||||
|
||||
export default function ModelIntegration() {
|
||||
export default function Models() {
|
||||
const { t } = useTranslation('docs')
|
||||
const allModels = [...MODELS.recommended, ...MODELS.verified]
|
||||
|
||||
@@ -121,6 +121,13 @@ export default function Configuration() {
|
||||
|
||||
/** Viewport expansion in pixels (-1 for full page) */
|
||||
viewportExpansion?: number
|
||||
|
||||
/**
|
||||
* Enable visual mask overlay during automation.
|
||||
* Blocks user interaction while agent is running.
|
||||
* Default: false for PageController, true for PageAgent.
|
||||
*/
|
||||
enableMask?: boolean
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
@@ -7,7 +7,7 @@ import DocsLayout from './pages/docs/Layout'
|
||||
import Instructions from './pages/docs/features/custom-instructions/page'
|
||||
import CustomTools from './pages/docs/features/custom-tools/page'
|
||||
import DataMasking from './pages/docs/features/data-masking/page'
|
||||
import ModelIntegration from './pages/docs/features/model-integration/page'
|
||||
import Models from './pages/docs/features/models/page'
|
||||
// Integration pages
|
||||
import BestPractices from './pages/docs/integration/best-practices/page'
|
||||
import CdnSetup from './pages/docs/integration/cdn-setup/page'
|
||||
@@ -80,11 +80,11 @@ export default function Router() {
|
||||
</div>
|
||||
</Route>
|
||||
|
||||
<Route path="/docs/features/model-integration">
|
||||
<Route path="/docs/features/models">
|
||||
<div className="min-h-screen bg-white dark:bg-gray-900">
|
||||
<Header />
|
||||
<DocsLayout>
|
||||
<ModelIntegration />
|
||||
<Models />
|
||||
</DocsLayout>
|
||||
</div>
|
||||
</Route>
|
||||
|
||||
Reference in New Issue
Block a user