@poolzin/pool-bot 2026.3.11 → 2026.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +121 -0
- package/dist/.buildstamp +1 -1
- package/dist/agents/checkpoint-manager.js +291 -0
- package/dist/agents/poolbot-tools.js +5 -0
- package/dist/agents/subagent-announce-reliability.js +160 -0
- package/dist/agents/tool-result-truncation.js +299 -0
- package/dist/agents/tools/nodes-file-tool.js +197 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/config-cli.js +60 -0
- package/dist/cron/cron-improvements.js +195 -0
- package/dist/discord/discord-improvements.js +167 -0
- package/dist/gateway/auth-rate-limit.js +19 -0
- package/dist/gateway/auth.js +41 -0
- package/dist/gateway/gateway-improvements.js +294 -0
- package/dist/gateway/node-command-policy.js +7 -2
- package/dist/infra/net/ssrf.js +15 -2
- package/dist/infra/shell-security.js +201 -0
- package/dist/memory/memory-improvements.js +239 -0
- package/dist/node-host/runner.js +146 -79
- package/dist/security/prototype-pollution.js +141 -0
- package/dist/security/webhook-security.js +253 -0
- package/dist/shared/net/ip.js +52 -1
- package/dist/slack/slack-improvements.js +225 -0
- package/dist/telegram/telegram-improvements.js +220 -0
- package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
- package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
- package/docs/competitive-analysis.md +421 -0
- package/docs/implementation-analysis.md +393 -0
- package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
- package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
- package/docs/refactor/plugin-development-guide.md +281 -0
- package/extensions/agency-agents/README.md +301 -0
- package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
- package/extensions/agency-agents/agents/README.md +602 -0
- package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
- package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
- package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
- package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
- package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
- package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
- package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
- package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
- package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
- package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
- package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
- package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
- package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
- package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
- package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
- package/extensions/agency-agents/agents/examples/README.md +48 -0
- package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
- package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
- package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
- package/extensions/agency-agents/agents/integrations/README.md +117 -0
- package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
- package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
- package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
- package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
- package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
- package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
- package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
- package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
- package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
- package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
- package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
- package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
- package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
- package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
- package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
- package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
- package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
- package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
- package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
- package/extensions/agency-agents/agents/scripts/install.sh +465 -0
- package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
- package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
- package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
- package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
- package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
- package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
- package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
- package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
- package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
- package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
- package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
- package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
- package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
- package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
- package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
- package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
- package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
- package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
- package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
- package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
- package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
- package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
- package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
- package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
- package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
- package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
- package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
- package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
- package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
- package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
- package/extensions/agency-agents/index.ts +733 -0
- package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
- package/extensions/agency-agents/node_modules/.bin/vite +21 -0
- package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
- package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
- package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/agency-agents/package.json +25 -0
- package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
- package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
- package/extensions/agency-agents/src/types.ts +147 -0
- package/extensions/agency-agents/vitest.config.ts +8 -0
- package/extensions/hexstrike-ai/README.md +98 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
- package/extensions/hexstrike-ai/package.json +29 -0
- package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
- package/extensions/hexstrike-ai/src/client.ts +91 -0
- package/extensions/hexstrike-ai/src/index.ts +170 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
- package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
- package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
- package/extensions/hexstrike-ai/tsconfig.json +20 -0
- package/extensions/hexstrike-bridge/package.json +1 -1
- package/extensions/hexstrike-bridge/poolbot.plugin.json +23 -0
- package/extensions/mcp-server/poolbot.plugin.json +10 -0
- package/extensions/page-agent/README.md +159 -0
- package/extensions/page-agent/index.ts +595 -0
- package/extensions/page-agent/node_modules/.bin/jiti +21 -0
- package/extensions/page-agent/node_modules/.bin/playwright +21 -0
- package/extensions/page-agent/node_modules/.bin/tsc +21 -0
- package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
- package/extensions/page-agent/node_modules/.bin/tsx +21 -0
- package/extensions/page-agent/node_modules/.bin/vitest +21 -0
- package/extensions/page-agent/node_modules/.bin/yaml +21 -0
- package/extensions/page-agent/package.json +43 -0
- package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
- package/extensions/page-agent/src/PageAgentService.ts +636 -0
- package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
- package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
- package/extensions/page-agent/src/index.ts +20 -0
- package/extensions/page-agent/src/tools.test.ts +231 -0
- package/extensions/page-agent/src/tools.ts +167 -0
- package/extensions/page-agent/src/types.ts +198 -0
- package/extensions/template/README.md +101 -0
- package/extensions/template/index.ts +38 -0
- package/extensions/template/package.json +15 -0
- package/extensions/template/poolbot.plugin.json +10 -0
- package/extensions/xyops/README.md +227 -0
- package/extensions/xyops/index.ts +342 -0
- package/extensions/xyops/node_modules/.bin/jiti +21 -0
- package/extensions/xyops/node_modules/.bin/tsc +21 -0
- package/extensions/xyops/node_modules/.bin/tsserver +21 -0
- package/extensions/xyops/node_modules/.bin/tsx +21 -0
- package/extensions/xyops/node_modules/.bin/vitest +21 -0
- package/extensions/xyops/node_modules/.bin/yaml +21 -0
- package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/xyops/package.json +39 -0
- package/extensions/xyops/src/client.test.ts +467 -0
- package/extensions/xyops/src/client.ts +157 -0
- package/extensions/xyops/src/types.ts +147 -0
- package/extensions/xyops/vitest.config.ts +8 -0
- package/package.json +1 -1
- package/extensions/mavalie/README.md +0 -97
- package/extensions/mavalie/package.json +0 -15
- package/extensions/mavalie/src/index.ts +0 -62
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import { PoolBotPageController } from './PoolBotPageController.js'
|
|
3
|
+
|
|
4
|
+
// Mock the browser client modules
|
|
5
|
+
vi.mock('../../../src/browser/client.js', () => ({
|
|
6
|
+
browserSnapshot: vi.fn(),
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
vi.mock('../../../src/browser/client-actions.js', () => ({
|
|
10
|
+
browserAct: vi.fn(),
|
|
11
|
+
browserNavigate: vi.fn(),
|
|
12
|
+
browserScreenshotAction: vi.fn(),
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
import { browserSnapshot } from '../../../src/browser/client.js'
|
|
16
|
+
import { browserAct, browserNavigate, browserScreenshotAction } from '../../../src/browser/client-actions.js'
|
|
17
|
+
|
|
18
|
+
describe('PoolBotPageController', () => {
|
|
19
|
+
let controller: PoolBotPageController
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.clearAllMocks()
|
|
23
|
+
controller = new PoolBotPageController({
|
|
24
|
+
browserBaseUrl: 'http://localhost:9222',
|
|
25
|
+
profile: 'test-profile',
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('constructor', () => {
|
|
30
|
+
it('should create controller with default config', () => {
|
|
31
|
+
const defaultController = new PoolBotPageController()
|
|
32
|
+
expect(defaultController).toBeDefined()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should create controller with custom config', () => {
|
|
36
|
+
expect(controller).toBeDefined()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should store targetId from config', () => {
|
|
40
|
+
const controllerWithTarget = new PoolBotPageController({
|
|
41
|
+
targetId: 'test-target-id',
|
|
42
|
+
})
|
|
43
|
+
expect(controllerWithTarget.getTargetId()).toBe('test-target-id')
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('getBrowserState', () => {
|
|
48
|
+
it('should return browser state in ai format', async () => {
|
|
49
|
+
vi.mocked(browserSnapshot).mockResolvedValue({
|
|
50
|
+
ok: true,
|
|
51
|
+
url: 'http://example.com',
|
|
52
|
+
snapshot: 'Test page content',
|
|
53
|
+
format: 'ai',
|
|
54
|
+
targetId: 'test-target',
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const state = await controller.getBrowserState()
|
|
58
|
+
|
|
59
|
+
expect(state.url).toBe('http://example.com')
|
|
60
|
+
expect(state.content).toBe('Test page content')
|
|
61
|
+
expect(state.snapshot).toBe('Test page content')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should return browser state in aria format', async () => {
|
|
65
|
+
vi.mocked(browserSnapshot).mockResolvedValue({
|
|
66
|
+
ok: true,
|
|
67
|
+
url: 'http://example.com',
|
|
68
|
+
nodes: [
|
|
69
|
+
{ ref: '1', role: 'heading', name: 'Title', depth: 0 },
|
|
70
|
+
{ ref: '2', role: 'button', name: 'Click me', depth: 1 },
|
|
71
|
+
],
|
|
72
|
+
format: 'aria',
|
|
73
|
+
targetId: 'test-target',
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const state = await controller.getBrowserState()
|
|
77
|
+
|
|
78
|
+
expect(state.url).toBe('http://example.com')
|
|
79
|
+
expect(state.content).toContain('[1] heading: Title')
|
|
80
|
+
expect(state.content).toContain('[2] button: Click me')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should update targetId from snapshot', async () => {
|
|
84
|
+
vi.mocked(browserSnapshot).mockResolvedValue({
|
|
85
|
+
ok: true,
|
|
86
|
+
url: 'http://example.com',
|
|
87
|
+
snapshot: 'Content',
|
|
88
|
+
format: 'ai',
|
|
89
|
+
targetId: 'new-target-id',
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
await controller.getBrowserState()
|
|
93
|
+
|
|
94
|
+
expect(controller.getTargetId()).toBe('new-target-id')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should throw error if snapshot fails', async () => {
|
|
98
|
+
vi.mocked(browserSnapshot).mockResolvedValue({
|
|
99
|
+
ok: false,
|
|
100
|
+
} as never)
|
|
101
|
+
|
|
102
|
+
await expect(controller.getBrowserState()).rejects.toThrow('Failed to get browser snapshot')
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('navigate', () => {
|
|
107
|
+
it('should navigate to URL successfully', async () => {
|
|
108
|
+
vi.mocked(browserNavigate).mockResolvedValue({
|
|
109
|
+
ok: true,
|
|
110
|
+
targetId: 'new-target',
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const result = await controller.navigate('http://example.com')
|
|
114
|
+
|
|
115
|
+
expect(result.success).toBe(true)
|
|
116
|
+
expect(result.message).toContain('Navigated to')
|
|
117
|
+
expect(controller.getTargetId()).toBe('new-target')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should handle navigation failure', async () => {
|
|
121
|
+
vi.mocked(browserNavigate).mockRejectedValue(new Error('Navigation failed'))
|
|
122
|
+
|
|
123
|
+
const result = await controller.navigate('http://example.com')
|
|
124
|
+
|
|
125
|
+
expect(result.success).toBe(false)
|
|
126
|
+
expect(result.message).toContain('Failed to navigate')
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('clickElement', () => {
|
|
131
|
+
it('should click element successfully', async () => {
|
|
132
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
133
|
+
ok: true,
|
|
134
|
+
targetId: 'test-target',
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const result = await controller.clickElement('element-ref-1')
|
|
138
|
+
|
|
139
|
+
expect(result.success).toBe(true)
|
|
140
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
141
|
+
'http://localhost:9222',
|
|
142
|
+
{
|
|
143
|
+
kind: 'click',
|
|
144
|
+
ref: 'element-ref-1',
|
|
145
|
+
targetId: undefined,
|
|
146
|
+
},
|
|
147
|
+
{ profile: 'test-profile' }
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should handle click failure', async () => {
|
|
152
|
+
vi.mocked(browserAct).mockRejectedValue(new Error('Click failed'))
|
|
153
|
+
|
|
154
|
+
const result = await controller.clickElement('element-ref-1')
|
|
155
|
+
|
|
156
|
+
expect(result.success).toBe(false)
|
|
157
|
+
expect(result.message).toContain('Action failed')
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe('inputText', () => {
|
|
162
|
+
it('should input text successfully', async () => {
|
|
163
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
164
|
+
ok: true,
|
|
165
|
+
targetId: 'test-target',
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const result = await controller.inputText('input-ref', 'test text')
|
|
169
|
+
|
|
170
|
+
expect(result.success).toBe(true)
|
|
171
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
172
|
+
'http://localhost:9222',
|
|
173
|
+
{
|
|
174
|
+
kind: 'type',
|
|
175
|
+
ref: 'input-ref',
|
|
176
|
+
text: 'test text',
|
|
177
|
+
targetId: undefined,
|
|
178
|
+
},
|
|
179
|
+
{ profile: 'test-profile' }
|
|
180
|
+
)
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('selectOption', () => {
|
|
185
|
+
it('should select option successfully', async () => {
|
|
186
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
187
|
+
ok: true,
|
|
188
|
+
targetId: 'test-target',
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const result = await controller.selectOption('select-ref', 'option-value')
|
|
192
|
+
|
|
193
|
+
expect(result.success).toBe(true)
|
|
194
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
195
|
+
'http://localhost:9222',
|
|
196
|
+
{
|
|
197
|
+
kind: 'select',
|
|
198
|
+
ref: 'select-ref',
|
|
199
|
+
values: ['option-value'],
|
|
200
|
+
targetId: undefined,
|
|
201
|
+
},
|
|
202
|
+
{ profile: 'test-profile' }
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
describe('scroll', () => {
|
|
208
|
+
it('should scroll down by default', async () => {
|
|
209
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
210
|
+
ok: true,
|
|
211
|
+
targetId: 'test-target',
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
const result = await controller.scroll({})
|
|
215
|
+
|
|
216
|
+
expect(result.success).toBe(true)
|
|
217
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
218
|
+
'http://localhost:9222',
|
|
219
|
+
expect.objectContaining({
|
|
220
|
+
kind: 'evaluate',
|
|
221
|
+
fn: expect.stringContaining('scrollBy'),
|
|
222
|
+
}),
|
|
223
|
+
{ profile: 'test-profile' }
|
|
224
|
+
)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('should scroll up when specified', async () => {
|
|
228
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
229
|
+
ok: true,
|
|
230
|
+
targetId: 'test-target',
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
const result = await controller.scroll({ down: false })
|
|
234
|
+
|
|
235
|
+
expect(result.success).toBe(true)
|
|
236
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
237
|
+
'http://localhost:9222',
|
|
238
|
+
expect.objectContaining({
|
|
239
|
+
kind: 'evaluate',
|
|
240
|
+
fn: expect.stringContaining('-500'),
|
|
241
|
+
}),
|
|
242
|
+
{ profile: 'test-profile' }
|
|
243
|
+
)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('should use custom amount', async () => {
|
|
247
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
248
|
+
ok: true,
|
|
249
|
+
targetId: 'test-target',
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
const result = await controller.scroll({ amount: 1000 })
|
|
253
|
+
|
|
254
|
+
expect(result.success).toBe(true)
|
|
255
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
256
|
+
'http://localhost:9222',
|
|
257
|
+
expect.objectContaining({
|
|
258
|
+
fn: expect.stringContaining('1000'),
|
|
259
|
+
}),
|
|
260
|
+
{ profile: 'test-profile' }
|
|
261
|
+
)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
describe('wait', () => {
|
|
266
|
+
it('should wait for specified seconds', async () => {
|
|
267
|
+
const start = Date.now()
|
|
268
|
+
const result = await controller.wait(0.1) // 100ms
|
|
269
|
+
const elapsed = Date.now() - start
|
|
270
|
+
|
|
271
|
+
expect(result.success).toBe(true)
|
|
272
|
+
expect(result.message).toContain('Waited for 0.1 seconds')
|
|
273
|
+
expect(elapsed).toBeGreaterThanOrEqual(90)
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
describe('executeJavaScript', () => {
|
|
278
|
+
it('should execute JavaScript successfully', async () => {
|
|
279
|
+
vi.mocked(browserAct).mockResolvedValue({
|
|
280
|
+
ok: true,
|
|
281
|
+
targetId: 'test-target',
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
const result = await controller.executeJavaScript('document.title')
|
|
285
|
+
|
|
286
|
+
expect(result.success).toBe(true)
|
|
287
|
+
expect(browserAct).toHaveBeenCalledWith(
|
|
288
|
+
'http://localhost:9222',
|
|
289
|
+
{
|
|
290
|
+
kind: 'evaluate',
|
|
291
|
+
fn: 'document.title',
|
|
292
|
+
targetId: undefined,
|
|
293
|
+
},
|
|
294
|
+
{ profile: 'test-profile' }
|
|
295
|
+
)
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
describe('screenshot', () => {
|
|
300
|
+
it('should take screenshot successfully', async () => {
|
|
301
|
+
vi.mocked(browserScreenshotAction).mockResolvedValue({
|
|
302
|
+
ok: true,
|
|
303
|
+
targetId: 'test-target',
|
|
304
|
+
path: 'base64-screenshot-data',
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
const result = await controller.screenshot()
|
|
308
|
+
|
|
309
|
+
expect(result.success).toBe(true)
|
|
310
|
+
expect(result.base64).toBe('base64-screenshot-data')
|
|
311
|
+
expect(result.message).toContain('Screenshot captured')
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('should handle screenshot failure', async () => {
|
|
315
|
+
vi.mocked(browserScreenshotAction).mockRejectedValue(new Error('Screenshot failed'))
|
|
316
|
+
|
|
317
|
+
const result = await controller.screenshot()
|
|
318
|
+
|
|
319
|
+
expect(result.success).toBe(false)
|
|
320
|
+
expect(result.message).toContain('Screenshot failed')
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should pass fullPage option', async () => {
|
|
324
|
+
vi.mocked(browserScreenshotAction).mockResolvedValue({
|
|
325
|
+
ok: true,
|
|
326
|
+
targetId: 'test-target',
|
|
327
|
+
path: 'base64-data',
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
await controller.screenshot({ fullPage: true })
|
|
331
|
+
|
|
332
|
+
expect(browserScreenshotAction).toHaveBeenCalledWith(
|
|
333
|
+
'http://localhost:9222',
|
|
334
|
+
expect.objectContaining({
|
|
335
|
+
fullPage: true,
|
|
336
|
+
type: 'png',
|
|
337
|
+
})
|
|
338
|
+
)
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
describe('getToolContext', () => {
|
|
343
|
+
it('should return tool context', () => {
|
|
344
|
+
const context = controller.getToolContext()
|
|
345
|
+
|
|
346
|
+
expect(context.browserBaseUrl).toBe('http://localhost:9222')
|
|
347
|
+
expect(context.profile).toBe('test-profile')
|
|
348
|
+
expect(context.targetId).toBeUndefined()
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
describe('setTargetId', () => {
|
|
353
|
+
it('should update targetId', () => {
|
|
354
|
+
controller.setTargetId('new-target')
|
|
355
|
+
expect(controller.getTargetId()).toBe('new-target')
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
})
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoolBot Page Controller
|
|
3
|
+
*
|
|
4
|
+
* Adapter that wraps PoolBot's native browser client to provide
|
|
5
|
+
* the PageController interface expected by Page Agent.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
browserSnapshot,
|
|
10
|
+
} from '../../../src/browser/client.js';
|
|
11
|
+
import {
|
|
12
|
+
browserAct,
|
|
13
|
+
browserNavigate,
|
|
14
|
+
browserScreenshotAction,
|
|
15
|
+
type BrowserActRequest,
|
|
16
|
+
} from '../../../src/browser/client-actions.js';
|
|
17
|
+
import type { BrowserState, ToolContext } from './types.js';
|
|
18
|
+
|
|
19
|
+
export interface PoolBotPageControllerConfig {
|
|
20
|
+
browserBaseUrl?: string;
|
|
21
|
+
profile?: string;
|
|
22
|
+
targetId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class PoolBotPageController {
|
|
26
|
+
private config: PoolBotPageControllerConfig;
|
|
27
|
+
private currentTargetId: string | undefined;
|
|
28
|
+
|
|
29
|
+
constructor(config: PoolBotPageControllerConfig = {}) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.currentTargetId = config.targetId;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get current browser state for LLM consumption
|
|
36
|
+
*/
|
|
37
|
+
async getBrowserState(): Promise<BrowserState> {
|
|
38
|
+
const snapshot = await browserSnapshot(this.config.browserBaseUrl, {
|
|
39
|
+
format: 'ai',
|
|
40
|
+
profile: this.config.profile,
|
|
41
|
+
targetId: this.currentTargetId,
|
|
42
|
+
interactive: true,
|
|
43
|
+
labels: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!snapshot.ok) {
|
|
47
|
+
throw new Error('Failed to get browser snapshot');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.currentTargetId = snapshot.targetId;
|
|
51
|
+
|
|
52
|
+
// Handle both ai and aria formats
|
|
53
|
+
if (snapshot.format === 'ai') {
|
|
54
|
+
return {
|
|
55
|
+
url: snapshot.url,
|
|
56
|
+
title: '', // Extract from snapshot if needed
|
|
57
|
+
content: snapshot.snapshot,
|
|
58
|
+
snapshot: snapshot.snapshot,
|
|
59
|
+
};
|
|
60
|
+
} else {
|
|
61
|
+
// aria format - convert to string representation
|
|
62
|
+
const content = snapshot.nodes.map(n => `[${n.ref}] ${n.role}: ${n.name}`).join('\n');
|
|
63
|
+
return {
|
|
64
|
+
url: snapshot.url,
|
|
65
|
+
title: '',
|
|
66
|
+
content,
|
|
67
|
+
snapshot: content,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Navigate to a URL
|
|
74
|
+
*/
|
|
75
|
+
async navigate(url: string): Promise<{ success: boolean; message: string }> {
|
|
76
|
+
try {
|
|
77
|
+
const result = await browserNavigate(this.config.browserBaseUrl, {
|
|
78
|
+
url,
|
|
79
|
+
profile: this.config.profile,
|
|
80
|
+
targetId: this.currentTargetId,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.currentTargetId = result.targetId;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
message: `Navigated to ${url}`,
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
message: `Failed to navigate: ${error}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Click element by ref
|
|
99
|
+
*/
|
|
100
|
+
async clickElement(ref: string): Promise<{ success: boolean; message: string }> {
|
|
101
|
+
return this.executeAction({
|
|
102
|
+
kind: 'click',
|
|
103
|
+
ref,
|
|
104
|
+
targetId: this.currentTargetId,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Type text into element
|
|
110
|
+
*/
|
|
111
|
+
async inputText(ref: string, text: string): Promise<{ success: boolean; message: string }> {
|
|
112
|
+
return this.executeAction({
|
|
113
|
+
kind: 'type',
|
|
114
|
+
ref,
|
|
115
|
+
text,
|
|
116
|
+
targetId: this.currentTargetId,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Select option from dropdown
|
|
122
|
+
*/
|
|
123
|
+
async selectOption(ref: string, value: string): Promise<{ success: boolean; message: string }> {
|
|
124
|
+
return this.executeAction({
|
|
125
|
+
kind: 'select',
|
|
126
|
+
ref,
|
|
127
|
+
values: [value],
|
|
128
|
+
targetId: this.currentTargetId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Scroll page
|
|
134
|
+
*/
|
|
135
|
+
async scroll(options: {
|
|
136
|
+
down?: boolean;
|
|
137
|
+
amount?: number;
|
|
138
|
+
}): Promise<{ success: boolean; message: string }> {
|
|
139
|
+
const direction = options.down !== false ? 1 : -1;
|
|
140
|
+
const amount = options.amount || 500;
|
|
141
|
+
|
|
142
|
+
return this.executeAction({
|
|
143
|
+
kind: 'evaluate',
|
|
144
|
+
fn: `window.scrollBy(0, ${amount * direction})`,
|
|
145
|
+
targetId: this.currentTargetId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Wait for a specified time
|
|
151
|
+
*/
|
|
152
|
+
async wait(seconds: number): Promise<{ success: boolean; message: string }> {
|
|
153
|
+
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
message: `Waited for ${seconds} seconds`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Execute JavaScript on the page
|
|
162
|
+
*/
|
|
163
|
+
async executeJavaScript(script: string): Promise<{ success: boolean; message: string }> {
|
|
164
|
+
return this.executeAction({
|
|
165
|
+
kind: 'evaluate',
|
|
166
|
+
fn: script,
|
|
167
|
+
targetId: this.currentTargetId,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get tool context for executing actions
|
|
173
|
+
*/
|
|
174
|
+
getToolContext(): ToolContext {
|
|
175
|
+
return {
|
|
176
|
+
browserBaseUrl: this.config.browserBaseUrl,
|
|
177
|
+
profile: this.config.profile,
|
|
178
|
+
targetId: this.currentTargetId,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Execute a browser action
|
|
184
|
+
*/
|
|
185
|
+
private async executeAction(action: BrowserActRequest): Promise<{ success: boolean; message: string }> {
|
|
186
|
+
try {
|
|
187
|
+
const result = await browserAct(this.config.browserBaseUrl, action, {
|
|
188
|
+
profile: this.config.profile,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (result.targetId) {
|
|
192
|
+
this.currentTargetId = result.targetId;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
message: `Action completed successfully`,
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return {
|
|
201
|
+
success: false,
|
|
202
|
+
message: `Action failed: ${error}`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Update target ID
|
|
209
|
+
*/
|
|
210
|
+
setTargetId(targetId: string): void {
|
|
211
|
+
this.currentTargetId = targetId;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get current target ID
|
|
216
|
+
*/
|
|
217
|
+
getTargetId(): string | undefined {
|
|
218
|
+
return this.currentTargetId;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Take a screenshot of the current page
|
|
223
|
+
*/
|
|
224
|
+
async screenshot(options?: { fullPage?: boolean }): Promise<{ success: boolean; base64?: string; message: string }> {
|
|
225
|
+
try {
|
|
226
|
+
const result = await browserScreenshotAction(this.config.browserBaseUrl, {
|
|
227
|
+
profile: this.config.profile,
|
|
228
|
+
targetId: this.currentTargetId,
|
|
229
|
+
fullPage: options?.fullPage,
|
|
230
|
+
type: 'png',
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
base64: result.path,
|
|
236
|
+
message: `Screenshot captured successfully`,
|
|
237
|
+
};
|
|
238
|
+
} catch (error) {
|
|
239
|
+
return {
|
|
240
|
+
success: false,
|
|
241
|
+
message: `Screenshot failed: ${error}`,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Agent Integration for PoolBot
|
|
3
|
+
*
|
|
4
|
+
* This module provides AI-powered browser automation by integrating
|
|
5
|
+
* the Page Agent core logic with PoolBot's native browser system.
|
|
6
|
+
*
|
|
7
|
+
* Instead of creating a separate browser instance, this integration
|
|
8
|
+
* leverages PoolBot's existing CDP/Playwright infrastructure for
|
|
9
|
+
* efficient resource usage and seamless operation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { PageAgentService } from './PageAgentService.js';
|
|
13
|
+
export { PoolBotPageController } from './PoolBotPageController.js';
|
|
14
|
+
export { createPageAgentTools } from './tools.js';
|
|
15
|
+
export type {
|
|
16
|
+
PageAgentConfig,
|
|
17
|
+
PageAgentExecutionResult,
|
|
18
|
+
PageAgentStep,
|
|
19
|
+
PageAgentStatus
|
|
20
|
+
} from './types.js';
|