micro-models-agent 0.6.4 → 0.7.1
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/README.md +286 -436
- package/dist/main.js +11706 -0
- package/dist/skills/builtin/git.md +36 -0
- package/dist/skills/builtin/typescript.md +35 -0
- package/package.json +41 -47
- package/dist/agent/context.d.ts +0 -73
- package/dist/agent/context.d.ts.map +0 -1
- package/dist/agent/context.js +0 -262
- package/dist/agent/context.js.map +0 -1
- package/dist/agent/error-escalation.d.ts +0 -19
- package/dist/agent/error-escalation.d.ts.map +0 -1
- package/dist/agent/error-escalation.js +0 -168
- package/dist/agent/error-escalation.js.map +0 -1
- package/dist/agent/loop.d.ts +0 -46
- package/dist/agent/loop.d.ts.map +0 -1
- package/dist/agent/loop.js +0 -1043
- package/dist/agent/loop.js.map +0 -1
- package/dist/agent/memory.d.ts +0 -40
- package/dist/agent/memory.d.ts.map +0 -1
- package/dist/agent/memory.js +0 -231
- package/dist/agent/memory.js.map +0 -1
- package/dist/agent/modes/applier.d.ts +0 -43
- package/dist/agent/modes/applier.d.ts.map +0 -1
- package/dist/agent/modes/applier.js +0 -30
- package/dist/agent/modes/applier.js.map +0 -1
- package/dist/agent/modes/registry.d.ts +0 -35
- package/dist/agent/modes/registry.d.ts.map +0 -1
- package/dist/agent/modes/registry.js +0 -169
- package/dist/agent/modes/registry.js.map +0 -1
- package/dist/agent/modes/types.d.ts +0 -61
- package/dist/agent/modes/types.d.ts.map +0 -1
- package/dist/agent/modes/types.js +0 -13
- package/dist/agent/modes/types.js.map +0 -1
- package/dist/agent/react-loop.d.ts +0 -65
- package/dist/agent/react-loop.d.ts.map +0 -1
- package/dist/agent/react-loop.js +0 -101
- package/dist/agent/react-loop.js.map +0 -1
- package/dist/agent/system-prompt.d.ts +0 -26
- package/dist/agent/system-prompt.d.ts.map +0 -1
- package/dist/agent/system-prompt.js +0 -478
- package/dist/agent/system-prompt.js.map +0 -1
- package/dist/agent/tool-executor.d.ts +0 -36
- package/dist/agent/tool-executor.d.ts.map +0 -1
- package/dist/agent/tool-executor.js +0 -346
- package/dist/agent/tool-executor.js.map +0 -1
- package/dist/agent/user-profile.d.ts +0 -26
- package/dist/agent/user-profile.d.ts.map +0 -1
- package/dist/agent/user-profile.js +0 -169
- package/dist/agent/user-profile.js.map +0 -1
- package/dist/agent/write-enforcer.d.ts +0 -7
- package/dist/agent/write-enforcer.d.ts.map +0 -1
- package/dist/agent/write-enforcer.js +0 -8
- package/dist/agent/write-enforcer.js.map +0 -1
- package/dist/cli/config.d.ts +0 -13
- package/dist/cli/config.d.ts.map +0 -1
- package/dist/cli/config.js +0 -82
- package/dist/cli/config.js.map +0 -1
- package/dist/cli/model.d.ts +0 -13
- package/dist/cli/model.d.ts.map +0 -1
- package/dist/cli/model.js +0 -170
- package/dist/cli/model.js.map +0 -1
- package/dist/cli/provider.d.ts +0 -12
- package/dist/cli/provider.d.ts.map +0 -1
- package/dist/cli/provider.js +0 -90
- package/dist/cli/provider.js.map +0 -1
- package/dist/cli/update.d.ts +0 -12
- package/dist/cli/update.d.ts.map +0 -1
- package/dist/cli/update.js +0 -69
- package/dist/cli/update.js.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -310
- package/dist/cli.js.map +0 -1
- package/dist/config/config.d.ts +0 -59
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/config.js +0 -232
- package/dist/config/config.js.map +0 -1
- package/dist/config/types.d.ts +0 -122
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js +0 -13
- package/dist/config/types.js.map +0 -1
- package/dist/file-loader.d.ts +0 -20
- package/dist/file-loader.d.ts.map +0 -1
- package/dist/file-loader.js +0 -121
- package/dist/file-loader.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/indexer/cache.d.ts +0 -34
- package/dist/indexer/cache.d.ts.map +0 -1
- package/dist/indexer/cache.js +0 -221
- package/dist/indexer/cache.js.map +0 -1
- package/dist/indexer/code-index.d.ts +0 -27
- package/dist/indexer/code-index.d.ts.map +0 -1
- package/dist/indexer/code-index.js +0 -219
- package/dist/indexer/code-index.js.map +0 -1
- package/dist/indexer/project-map.d.ts +0 -23
- package/dist/indexer/project-map.d.ts.map +0 -1
- package/dist/indexer/project-map.js +0 -264
- package/dist/indexer/project-map.js.map +0 -1
- package/dist/llm/lmstudio-native.d.ts +0 -10
- package/dist/llm/lmstudio-native.d.ts.map +0 -1
- package/dist/llm/lmstudio-native.js +0 -179
- package/dist/llm/lmstudio-native.js.map +0 -1
- package/dist/llm/openai-compat.d.ts +0 -11
- package/dist/llm/openai-compat.d.ts.map +0 -1
- package/dist/llm/openai-compat.js +0 -161
- package/dist/llm/openai-compat.js.map +0 -1
- package/dist/llm/provider.d.ts +0 -46
- package/dist/llm/provider.d.ts.map +0 -1
- package/dist/llm/provider.js +0 -13
- package/dist/llm/provider.js.map +0 -1
- package/dist/llm/token-counter.d.ts +0 -15
- package/dist/llm/token-counter.d.ts.map +0 -1
- package/dist/llm/token-counter.js +0 -51
- package/dist/llm/token-counter.js.map +0 -1
- package/dist/logger.d.ts +0 -24
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -156
- package/dist/logger.js.map +0 -1
- package/dist/plugins/adapters/hooks-adapter.d.ts +0 -12
- package/dist/plugins/adapters/hooks-adapter.d.ts.map +0 -1
- package/dist/plugins/adapters/hooks-adapter.js +0 -23
- package/dist/plugins/adapters/hooks-adapter.js.map +0 -1
- package/dist/plugins/builtin/lint-on-write/index.d.ts +0 -12
- package/dist/plugins/builtin/lint-on-write/index.d.ts.map +0 -1
- package/dist/plugins/builtin/lint-on-write/index.js +0 -98
- package/dist/plugins/builtin/lint-on-write/index.js.map +0 -1
- package/dist/plugins/builtin/lint-on-write/index.ts +0 -97
- package/dist/plugins/builtin/lint-on-write/manifest.json +0 -13
- package/dist/plugins/builtin/notify/index.d.ts +0 -11
- package/dist/plugins/builtin/notify/index.d.ts.map +0 -1
- package/dist/plugins/builtin/notify/index.js +0 -102
- package/dist/plugins/builtin/notify/index.js.map +0 -1
- package/dist/plugins/builtin/notify/index.ts +0 -105
- package/dist/plugins/builtin/notify/manifest.json +0 -16
- package/dist/plugins/loader.d.ts +0 -18
- package/dist/plugins/loader.d.ts.map +0 -1
- package/dist/plugins/loader.js +0 -182
- package/dist/plugins/loader.js.map +0 -1
- package/dist/plugins/manager.d.ts +0 -55
- package/dist/plugins/manager.d.ts.map +0 -1
- package/dist/plugins/manager.js +0 -190
- package/dist/plugins/manager.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -76
- package/dist/plugins/types.d.ts.map +0 -1
- package/dist/plugins/types.js +0 -8
- package/dist/plugins/types.js.map +0 -1
- package/dist/repl/commands.d.ts +0 -63
- package/dist/repl/commands.d.ts.map +0 -1
- package/dist/repl/commands.js +0 -1070
- package/dist/repl/commands.js.map +0 -1
- package/dist/repl/history.d.ts +0 -32
- package/dist/repl/history.d.ts.map +0 -1
- package/dist/repl/history.js +0 -40
- package/dist/repl/history.js.map +0 -1
- package/dist/repl/index.d.ts +0 -14
- package/dist/repl/index.d.ts.map +0 -1
- package/dist/repl/index.js +0 -235
- package/dist/repl/index.js.map +0 -1
- package/dist/session/store.d.ts +0 -46
- package/dist/session/store.d.ts.map +0 -1
- package/dist/session/store.js +0 -154
- package/dist/session/store.js.map +0 -1
- package/dist/setup-wizard.d.ts +0 -3
- package/dist/setup-wizard.d.ts.map +0 -1
- package/dist/setup-wizard.js +0 -410
- package/dist/setup-wizard.js.map +0 -1
- package/dist/skills/builtin/agent-test/SKILL.md +0 -68
- package/dist/skills/builtin/cli-expert/SKILL.md +0 -848
- package/dist/skills/builtin/code-review/SKILL.md +0 -424
- package/dist/skills/builtin/git-expert/SKILL.md +0 -522
- package/dist/skills/builtin/grill-me/SKILL.md +0 -46
- package/dist/skills/builtin/jest-testing-expert/SKILL.md +0 -957
- package/dist/skills/builtin/nodejs-expert/SKILL.md +0 -192
- package/dist/skills/builtin/systematic-debugging/SKILL.md +0 -296
- package/dist/skills/builtin/test-driven-development/SKILL.md +0 -371
- package/dist/skills/builtin/typescript-expert/SKILL.md +0 -429
- package/dist/skills/builtin/web-scraping/SKILL.md +0 -422
- package/dist/skills/loader.d.ts +0 -49
- package/dist/skills/loader.d.ts.map +0 -1
- package/dist/skills/loader.js +0 -401
- package/dist/skills/loader.js.map +0 -1
- package/dist/styling/engine.d.ts +0 -39
- package/dist/styling/engine.d.ts.map +0 -1
- package/dist/styling/engine.js +0 -122
- package/dist/styling/engine.js.map +0 -1
- package/dist/styling/index.d.ts +0 -15
- package/dist/styling/index.d.ts.map +0 -1
- package/dist/styling/index.js +0 -40
- package/dist/styling/index.js.map +0 -1
- package/dist/styling/loader.d.ts +0 -9
- package/dist/styling/loader.d.ts.map +0 -1
- package/dist/styling/loader.js +0 -169
- package/dist/styling/loader.js.map +0 -1
- package/dist/styling/types.d.ts +0 -64
- package/dist/styling/types.d.ts.map +0 -1
- package/dist/styling/types.js +0 -12
- package/dist/styling/types.js.map +0 -1
- package/dist/subagent/chat.d.ts +0 -19
- package/dist/subagent/chat.d.ts.map +0 -1
- package/dist/subagent/chat.js +0 -279
- package/dist/subagent/chat.js.map +0 -1
- package/dist/subagent/context.d.ts +0 -9
- package/dist/subagent/context.d.ts.map +0 -1
- package/dist/subagent/context.js +0 -75
- package/dist/subagent/context.js.map +0 -1
- package/dist/subagent/definition.d.ts +0 -7
- package/dist/subagent/definition.d.ts.map +0 -1
- package/dist/subagent/definition.js +0 -26
- package/dist/subagent/definition.js.map +0 -1
- package/dist/subagent/errors.d.ts +0 -24
- package/dist/subagent/errors.d.ts.map +0 -1
- package/dist/subagent/errors.js +0 -85
- package/dist/subagent/errors.js.map +0 -1
- package/dist/subagent/validation.d.ts +0 -17
- package/dist/subagent/validation.d.ts.map +0 -1
- package/dist/subagent/validation.js +0 -83
- package/dist/subagent/validation.js.map +0 -1
- package/dist/tools/adapters/bash-to-fs.d.ts +0 -20
- package/dist/tools/adapters/bash-to-fs.d.ts.map +0 -1
- package/dist/tools/adapters/bash-to-fs.js +0 -74
- package/dist/tools/adapters/bash-to-fs.js.map +0 -1
- package/dist/tools/adapters/content-sanitizer.d.ts +0 -13
- package/dist/tools/adapters/content-sanitizer.d.ts.map +0 -1
- package/dist/tools/adapters/content-sanitizer.js +0 -73
- package/dist/tools/adapters/content-sanitizer.js.map +0 -1
- package/dist/tools/adapters/grounding-fixer.d.ts +0 -15
- package/dist/tools/adapters/grounding-fixer.d.ts.map +0 -1
- package/dist/tools/adapters/grounding-fixer.js +0 -57
- package/dist/tools/adapters/grounding-fixer.js.map +0 -1
- package/dist/tools/adapters/index.d.ts +0 -6
- package/dist/tools/adapters/index.d.ts.map +0 -1
- package/dist/tools/adapters/index.js +0 -5
- package/dist/tools/adapters/index.js.map +0 -1
- package/dist/tools/adapters/pipeline.d.ts +0 -34
- package/dist/tools/adapters/pipeline.d.ts.map +0 -1
- package/dist/tools/adapters/pipeline.js +0 -58
- package/dist/tools/adapters/pipeline.js.map +0 -1
- package/dist/tools/adapters/types.d.ts +0 -46
- package/dist/tools/adapters/types.d.ts.map +0 -1
- package/dist/tools/adapters/types.js +0 -2
- package/dist/tools/adapters/types.js.map +0 -1
- package/dist/tools/approve.d.ts +0 -27
- package/dist/tools/approve.d.ts.map +0 -1
- package/dist/tools/approve.js +0 -45
- package/dist/tools/approve.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -25
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js +0 -292
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/fs/copy-file.d.ts +0 -13
- package/dist/tools/fs/copy-file.d.ts.map +0 -1
- package/dist/tools/fs/copy-file.js +0 -54
- package/dist/tools/fs/copy-file.js.map +0 -1
- package/dist/tools/fs/create-dir.d.ts +0 -13
- package/dist/tools/fs/create-dir.d.ts.map +0 -1
- package/dist/tools/fs/create-dir.js +0 -44
- package/dist/tools/fs/create-dir.js.map +0 -1
- package/dist/tools/fs/delete-file.d.ts +0 -13
- package/dist/tools/fs/delete-file.d.ts.map +0 -1
- package/dist/tools/fs/delete-file.js +0 -52
- package/dist/tools/fs/delete-file.js.map +0 -1
- package/dist/tools/fs/edit-file.d.ts +0 -15
- package/dist/tools/fs/edit-file.d.ts.map +0 -1
- package/dist/tools/fs/edit-file.js +0 -95
- package/dist/tools/fs/edit-file.js.map +0 -1
- package/dist/tools/fs/file-info.d.ts +0 -13
- package/dist/tools/fs/file-info.d.ts.map +0 -1
- package/dist/tools/fs/file-info.js +0 -50
- package/dist/tools/fs/file-info.js.map +0 -1
- package/dist/tools/fs/glob-search.d.ts +0 -13
- package/dist/tools/fs/glob-search.d.ts.map +0 -1
- package/dist/tools/fs/glob-search.js +0 -112
- package/dist/tools/fs/glob-search.js.map +0 -1
- package/dist/tools/fs/grep-search.d.ts +0 -14
- package/dist/tools/fs/grep-search.d.ts.map +0 -1
- package/dist/tools/fs/grep-search.js +0 -135
- package/dist/tools/fs/grep-search.js.map +0 -1
- package/dist/tools/fs/list-dir.d.ts +0 -13
- package/dist/tools/fs/list-dir.d.ts.map +0 -1
- package/dist/tools/fs/list-dir.js +0 -74
- package/dist/tools/fs/list-dir.js.map +0 -1
- package/dist/tools/fs/move-file.d.ts +0 -13
- package/dist/tools/fs/move-file.d.ts.map +0 -1
- package/dist/tools/fs/move-file.js +0 -54
- package/dist/tools/fs/move-file.js.map +0 -1
- package/dist/tools/fs/path-utils.d.ts +0 -51
- package/dist/tools/fs/path-utils.d.ts.map +0 -1
- package/dist/tools/fs/path-utils.js +0 -103
- package/dist/tools/fs/path-utils.js.map +0 -1
- package/dist/tools/fs/read-file.d.ts +0 -13
- package/dist/tools/fs/read-file.d.ts.map +0 -1
- package/dist/tools/fs/read-file.js +0 -64
- package/dist/tools/fs/read-file.js.map +0 -1
- package/dist/tools/fs/tree.d.ts +0 -13
- package/dist/tools/fs/tree.d.ts.map +0 -1
- package/dist/tools/fs/tree.js +0 -89
- package/dist/tools/fs/tree.js.map +0 -1
- package/dist/tools/fs/types.d.ts +0 -14
- package/dist/tools/fs/types.d.ts.map +0 -1
- package/dist/tools/fs/types.js +0 -11
- package/dist/tools/fs/types.js.map +0 -1
- package/dist/tools/fs/write-file.d.ts +0 -14
- package/dist/tools/fs/write-file.d.ts.map +0 -1
- package/dist/tools/fs/write-file.js +0 -83
- package/dist/tools/fs/write-file.js.map +0 -1
- package/dist/tools/hooks.d.ts +0 -44
- package/dist/tools/hooks.d.ts.map +0 -1
- package/dist/tools/hooks.js +0 -217
- package/dist/tools/hooks.js.map +0 -1
- package/dist/tools/id-gen.d.ts +0 -4
- package/dist/tools/id-gen.d.ts.map +0 -1
- package/dist/tools/id-gen.js +0 -12
- package/dist/tools/id-gen.js.map +0 -1
- package/dist/tools/mcp.d.ts +0 -36
- package/dist/tools/mcp.d.ts.map +0 -1
- package/dist/tools/mcp.js +0 -93
- package/dist/tools/mcp.js.map +0 -1
- package/dist/tools/native/approve.d.ts +0 -11
- package/dist/tools/native/approve.d.ts.map +0 -1
- package/dist/tools/native/approve.js +0 -39
- package/dist/tools/native/approve.js.map +0 -1
- package/dist/tools/native/plan.d.ts +0 -10
- package/dist/tools/native/plan.d.ts.map +0 -1
- package/dist/tools/native/plan.js +0 -115
- package/dist/tools/native/plan.js.map +0 -1
- package/dist/tools/native/project-map.d.ts +0 -15
- package/dist/tools/native/project-map.d.ts.map +0 -1
- package/dist/tools/native/project-map.js +0 -203
- package/dist/tools/native/project-map.js.map +0 -1
- package/dist/tools/native/question.d.ts +0 -11
- package/dist/tools/native/question.d.ts.map +0 -1
- package/dist/tools/native/question.js +0 -41
- package/dist/tools/native/question.js.map +0 -1
- package/dist/tools/native/scaffold.d.ts +0 -5
- package/dist/tools/native/scaffold.d.ts.map +0 -1
- package/dist/tools/native/scaffold.js +0 -246
- package/dist/tools/native/scaffold.js.map +0 -1
- package/dist/tools/native/theme.d.ts +0 -10
- package/dist/tools/native/theme.d.ts.map +0 -1
- package/dist/tools/native/theme.js +0 -101
- package/dist/tools/native/theme.js.map +0 -1
- package/dist/tools/native/todo.d.ts +0 -10
- package/dist/tools/native/todo.d.ts.map +0 -1
- package/dist/tools/native/todo.js +0 -58
- package/dist/tools/native/todo.js.map +0 -1
- package/dist/tools/native/user-profile.d.ts +0 -10
- package/dist/tools/native/user-profile.d.ts.map +0 -1
- package/dist/tools/native/user-profile.js +0 -58
- package/dist/tools/native/user-profile.js.map +0 -1
- package/dist/tools/parse-tool-call.d.ts +0 -11
- package/dist/tools/parse-tool-call.d.ts.map +0 -1
- package/dist/tools/parse-tool-call.js +0 -87
- package/dist/tools/parse-tool-call.js.map +0 -1
- package/dist/tools/plan.d.ts +0 -40
- package/dist/tools/plan.d.ts.map +0 -1
- package/dist/tools/plan.js +0 -143
- package/dist/tools/plan.js.map +0 -1
- package/dist/tools/question.d.ts +0 -33
- package/dist/tools/question.d.ts.map +0 -1
- package/dist/tools/question.js +0 -62
- package/dist/tools/question.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -37
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -97
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/sanitizer.d.ts +0 -30
- package/dist/tools/sanitizer.d.ts.map +0 -1
- package/dist/tools/sanitizer.js +0 -77
- package/dist/tools/sanitizer.js.map +0 -1
- package/dist/tools/subagent.d.ts +0 -39
- package/dist/tools/subagent.d.ts.map +0 -1
- package/dist/tools/subagent.js +0 -225
- package/dist/tools/subagent.js.map +0 -1
- package/dist/tools/syntax-check.d.ts +0 -32
- package/dist/tools/syntax-check.d.ts.map +0 -1
- package/dist/tools/syntax-check.js +0 -272
- package/dist/tools/syntax-check.js.map +0 -1
- package/dist/tools/todo.d.ts +0 -28
- package/dist/tools/todo.d.ts.map +0 -1
- package/dist/tools/todo.js +0 -125
- package/dist/tools/todo.js.map +0 -1
- package/dist/tools/types.d.ts +0 -51
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -2
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/web-browse.d.ts +0 -5
- package/dist/tools/web-browse.d.ts.map +0 -1
- package/dist/tools/web-browse.js +0 -757
- package/dist/tools/web-browse.js.map +0 -1
- package/dist/tools/web-fetch.d.ts +0 -19
- package/dist/tools/web-fetch.d.ts.map +0 -1
- package/dist/tools/web-fetch.js +0 -90
- package/dist/tools/web-fetch.js.map +0 -1
- package/dist/tools/web-search.d.ts +0 -19
- package/dist/tools/web-search.d.ts.map +0 -1
- package/dist/tools/web-search.js +0 -97
- package/dist/tools/web-search.js.map +0 -1
- package/dist/ui/components/status-bar.d.ts +0 -11
- package/dist/ui/components/status-bar.d.ts.map +0 -1
- package/dist/ui/components/status-bar.js +0 -20
- package/dist/ui/components/status-bar.js.map +0 -1
- package/dist/ui/index.d.ts +0 -8
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/ui/index.js +0 -9
- package/dist/ui/index.js.map +0 -1
- package/dist/ui/ink/components/App.d.ts +0 -98
- package/dist/ui/ink/components/App.d.ts.map +0 -1
- package/dist/ui/ink/components/App.js +0 -212
- package/dist/ui/ink/components/App.js.map +0 -1
- package/dist/ui/ink/components/Conversation.d.ts +0 -20
- package/dist/ui/ink/components/Conversation.d.ts.map +0 -1
- package/dist/ui/ink/components/Conversation.js +0 -15
- package/dist/ui/ink/components/Conversation.js.map +0 -1
- package/dist/ui/ink/components/MessageBlock.d.ts +0 -27
- package/dist/ui/ink/components/MessageBlock.d.ts.map +0 -1
- package/dist/ui/ink/components/MessageBlock.js +0 -41
- package/dist/ui/ink/components/MessageBlock.js.map +0 -1
- package/dist/ui/ink/components/PromptInput.d.ts +0 -25
- package/dist/ui/ink/components/PromptInput.d.ts.map +0 -1
- package/dist/ui/ink/components/PromptInput.js +0 -167
- package/dist/ui/ink/components/PromptInput.js.map +0 -1
- package/dist/ui/ink/components/SelectInput.d.ts +0 -9
- package/dist/ui/ink/components/SelectInput.d.ts.map +0 -1
- package/dist/ui/ink/components/SelectInput.js +0 -30
- package/dist/ui/ink/components/SelectInput.js.map +0 -1
- package/dist/ui/ink/components/StatusBar.d.ts +0 -14
- package/dist/ui/ink/components/StatusBar.d.ts.map +0 -1
- package/dist/ui/ink/components/StatusBar.js +0 -20
- package/dist/ui/ink/components/StatusBar.js.map +0 -1
- package/dist/ui/ink/components/StreamBlock.d.ts +0 -8
- package/dist/ui/ink/components/StreamBlock.d.ts.map +0 -1
- package/dist/ui/ink/components/StreamBlock.js +0 -15
- package/dist/ui/ink/components/StreamBlock.js.map +0 -1
- package/dist/ui/ink/components/Tags.d.ts +0 -26
- package/dist/ui/ink/components/Tags.d.ts.map +0 -1
- package/dist/ui/ink/components/Tags.js +0 -37
- package/dist/ui/ink/components/Tags.js.map +0 -1
- package/dist/ui/ink/components/ToolCallBlock.d.ts +0 -31
- package/dist/ui/ink/components/ToolCallBlock.d.ts.map +0 -1
- package/dist/ui/ink/components/ToolCallBlock.js +0 -17
- package/dist/ui/ink/components/ToolCallBlock.js.map +0 -1
- package/dist/ui/ink/renderer.d.ts +0 -69
- package/dist/ui/ink/renderer.d.ts.map +0 -1
- package/dist/ui/ink/renderer.js +0 -142
- package/dist/ui/ink/renderer.js.map +0 -1
- package/dist/ui/ink/theme.d.ts +0 -28
- package/dist/ui/ink/theme.d.ts.map +0 -1
- package/dist/ui/ink/theme.js +0 -45
- package/dist/ui/ink/theme.js.map +0 -1
- package/dist/ui/md-formatter.d.ts +0 -16
- package/dist/ui/md-formatter.d.ts.map +0 -1
- package/dist/ui/md-formatter.js +0 -448
- package/dist/ui/md-formatter.js.map +0 -1
- package/dist/ui/readline-renderer.d.ts +0 -45
- package/dist/ui/readline-renderer.d.ts.map +0 -1
- package/dist/ui/readline-renderer.js +0 -181
- package/dist/ui/readline-renderer.js.map +0 -1
- package/dist/ui/renderer.d.ts +0 -66
- package/dist/ui/renderer.d.ts.map +0 -1
- package/dist/ui/renderer.js +0 -16
- package/dist/ui/renderer.js.map +0 -1
- package/dist/ui/subagent-renderer.d.ts +0 -58
- package/dist/ui/subagent-renderer.d.ts.map +0 -1
- package/dist/ui/subagent-renderer.js +0 -100
- package/dist/ui/subagent-renderer.js.map +0 -1
- package/dist/updater.d.ts +0 -15
- package/dist/updater.d.ts.map +0 -1
- package/dist/updater.js +0 -166
- package/dist/updater.js.map +0 -1
package/dist/tools/web-browse.js
DELETED
|
@@ -1,757 +0,0 @@
|
|
|
1
|
-
import { logInfo, logError } from '../logger.js';
|
|
2
|
-
/**
|
|
3
|
-
* Интерактивный инструмент для работы с веб-страницами через headless-браузер.
|
|
4
|
-
*
|
|
5
|
-
* Поддерживает 9 actions: navigate, click, type, extract, screenshot,
|
|
6
|
-
* scroll, wait, get_links, evaluate.
|
|
7
|
-
*
|
|
8
|
-
* Сессия браузера stateful: navigate открывает страницу,
|
|
9
|
-
* последующие action'ы работают с ней.
|
|
10
|
-
*
|
|
11
|
-
* @module tools/web-browse
|
|
12
|
-
*/
|
|
13
|
-
// ─── Browser Session ─────────────────────────────────────
|
|
14
|
-
/** Тип для Playwright (ленивый импорт) */
|
|
15
|
-
let playwrightModule = null;
|
|
16
|
-
async function getPlaywright() {
|
|
17
|
-
if (!playwrightModule) {
|
|
18
|
-
playwrightModule = await import('playwright');
|
|
19
|
-
}
|
|
20
|
-
return playwrightModule;
|
|
21
|
-
}
|
|
22
|
-
const state = {
|
|
23
|
-
browser: null,
|
|
24
|
-
context: null,
|
|
25
|
-
page: null,
|
|
26
|
-
lastActivity: 0,
|
|
27
|
-
};
|
|
28
|
-
/** Таймаут неактивности (5 минут) */
|
|
29
|
-
const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
30
|
-
/** Таймаут навигации */
|
|
31
|
-
const NAV_TIMEOUT_MS = 30_000;
|
|
32
|
-
/** Инициализация браузера (лениво) */
|
|
33
|
-
async function ensureBrowser() {
|
|
34
|
-
// Проверяем есть ли активная сессия
|
|
35
|
-
if (state.page && !state.page.isClosed()) {
|
|
36
|
-
state.lastActivity = Date.now();
|
|
37
|
-
return state.page;
|
|
38
|
-
}
|
|
39
|
-
// Закрываем старые ресурсы
|
|
40
|
-
await closeBrowser();
|
|
41
|
-
const pw = await getPlaywright();
|
|
42
|
-
state.browser = await pw.chromium.launch({
|
|
43
|
-
headless: true,
|
|
44
|
-
args: ['--no-sandbox', '--disable-gpu'],
|
|
45
|
-
});
|
|
46
|
-
state.context = await state.browser.newContext({
|
|
47
|
-
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
48
|
-
viewport: { width: 1280, height: 720 },
|
|
49
|
-
});
|
|
50
|
-
state.page = await state.context.newPage();
|
|
51
|
-
state.lastActivity = Date.now();
|
|
52
|
-
logInfo('BROWSER', 'Headless browser launched');
|
|
53
|
-
return state.page;
|
|
54
|
-
}
|
|
55
|
-
/** Закрытие браузера */
|
|
56
|
-
async function closeBrowser() {
|
|
57
|
-
try {
|
|
58
|
-
if (state.page && !state.page.isClosed())
|
|
59
|
-
await state.page.close();
|
|
60
|
-
if (state.context)
|
|
61
|
-
await state.context.close();
|
|
62
|
-
if (state.browser)
|
|
63
|
-
await state.browser.close();
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
logError('BROWSER', `close error: ${e.message}`);
|
|
67
|
-
}
|
|
68
|
-
state.browser = null;
|
|
69
|
-
state.context = null;
|
|
70
|
-
state.page = null;
|
|
71
|
-
}
|
|
72
|
-
/** Проверка и закрытие при простое */
|
|
73
|
-
async function checkIdle() {
|
|
74
|
-
if (state.page && Date.now() - state.lastActivity > IDLE_TIMEOUT_MS) {
|
|
75
|
-
logInfo('BROWSER', 'Idle timeout, closing browser');
|
|
76
|
-
await closeBrowser();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// ─── Actions ─────────────────────────────────────────────
|
|
80
|
-
/**
|
|
81
|
-
* Универсальный scrape: navigate + auto-scroll + discover + извлечение данных.
|
|
82
|
-
* Одним вызовом代替ует 4-шаговый паттерн (navigate → discover → evaluate → write_file).
|
|
83
|
-
*
|
|
84
|
-
* Алгоритм:
|
|
85
|
-
* 1. Navigate (если страница не активна)
|
|
86
|
-
* 2. Auto-scroll для lazy-контента
|
|
87
|
-
* 3. Discover — найти элементы по паттерну классов
|
|
88
|
-
* 4. Извлечь данные из каждого элемента (текст, ссылки, дочерние элементы)
|
|
89
|
-
* 5. Вернуть структурированный JSON
|
|
90
|
-
*/
|
|
91
|
-
async function actionScrape(page, args) {
|
|
92
|
-
const url = String(args.url ?? '').trim();
|
|
93
|
-
const pattern = String(args.pattern ?? 'product|card|item|goods|bike|velosiped').trim();
|
|
94
|
-
const maxItems = Math.min(Number(args.maxItems) || 10, 50);
|
|
95
|
-
const extractFields = String(args.fields ?? '').trim(); // e.g. "title,price,href"
|
|
96
|
-
// Шаг 1: Navigate (если нет активной страницы или указан URL)
|
|
97
|
-
if (url) {
|
|
98
|
-
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
99
|
-
return 'Error: URL must start with http:// or https://';
|
|
100
|
-
}
|
|
101
|
-
const navResult = await actionNavigate(page, args);
|
|
102
|
-
if (navResult.text.startsWith('Error'))
|
|
103
|
-
return navResult.text;
|
|
104
|
-
}
|
|
105
|
-
if (!page || page.isClosed()) {
|
|
106
|
-
return 'Error: No active page. Provide a URL to navigate to.';
|
|
107
|
-
}
|
|
108
|
-
// Шаг 2: Auto-scroll (если не был сделан в navigate)
|
|
109
|
-
try {
|
|
110
|
-
const currentScroll = await page.evaluate(() => window.scrollY);
|
|
111
|
-
if (currentScroll === 0) {
|
|
112
|
-
// Navigate уже скроллил, но если мы здесь — скроллим ещё раз
|
|
113
|
-
let prevCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
114
|
-
for (let i = 0; i < 5; i++) {
|
|
115
|
-
await page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
116
|
-
await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => { });
|
|
117
|
-
await page.waitForTimeout(1500);
|
|
118
|
-
const newCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
119
|
-
if (newCount === prevCount)
|
|
120
|
-
break;
|
|
121
|
-
prevCount = newCount;
|
|
122
|
-
}
|
|
123
|
-
await page.evaluate(() => window.scrollTo(0, 0));
|
|
124
|
-
await page.waitForTimeout(500);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch { /* ignore */ }
|
|
128
|
-
// Шаг 3: Discover — найти элементы по паттерну
|
|
129
|
-
const patternRegex = new RegExp(pattern, 'i');
|
|
130
|
-
const scrapeResult = await page.evaluate(({ pat, max }) => {
|
|
131
|
-
const regex = new RegExp(pat, 'i');
|
|
132
|
-
// Собираем все элементы с классами
|
|
133
|
-
const allElements = Array.from(document.querySelectorAll('*'));
|
|
134
|
-
const candidates = [];
|
|
135
|
-
for (const el of allElements) {
|
|
136
|
-
if (!el.className || typeof el.className !== 'string')
|
|
137
|
-
continue;
|
|
138
|
-
const cls = el.className;
|
|
139
|
-
// Считаем совпадения паттерна в классах
|
|
140
|
-
const matches = cls.match(regex);
|
|
141
|
-
if (!matches)
|
|
142
|
-
continue;
|
|
143
|
-
// Оценка: количество совпадений + длина текста (продукты обычно имеют текст)
|
|
144
|
-
const text = (el.textContent ?? '').trim();
|
|
145
|
-
const score = matches.length * 10 + Math.min(text.length / 10, 20);
|
|
146
|
-
candidates.push({ el, score, className: cls });
|
|
147
|
-
}
|
|
148
|
-
// Сортируем по score, берём топ
|
|
149
|
-
candidates.sort((a, b) => b.score - a.score);
|
|
150
|
-
// Группируем похожие элементы (одинаковый тег + похожие классы)
|
|
151
|
-
const groups = [];
|
|
152
|
-
for (const c of candidates.slice(0, max * 3)) {
|
|
153
|
-
const tag = c.el.tagName;
|
|
154
|
-
const existing = groups.find(g => g.tag === tag && g.className === c.className);
|
|
155
|
-
if (existing) {
|
|
156
|
-
existing.count++;
|
|
157
|
-
existing.elements.push(c.el);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
groups.push({ tag, className: c.className, count: 1, elements: [c.el] });
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Берём группу с наибольшим количеством элементов (это карточки товаров)
|
|
164
|
-
groups.sort((a, b) => b.count - a.count);
|
|
165
|
-
const bestGroup = groups[0];
|
|
166
|
-
if (!bestGroup || bestGroup.count < 2) {
|
|
167
|
-
// Нет подходящих групп — возвращаем все кандидаты как есть
|
|
168
|
-
return {
|
|
169
|
-
found: false,
|
|
170
|
-
message: `No elements matching pattern "${pat}" found. Page classes: ${candidates.slice(0, 20).map(c => c.className).join(', ')}`,
|
|
171
|
-
items: [],
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
// Извлекаем данные из элементов лучшей группы
|
|
175
|
-
const items = bestGroup.elements.slice(0, max).map(el => {
|
|
176
|
-
const data = {};
|
|
177
|
-
// Текст элемента
|
|
178
|
-
const fullText = (el.textContent ?? '').trim().substring(0, 500);
|
|
179
|
-
data.text = fullText;
|
|
180
|
-
// Ссылки внутри элемента
|
|
181
|
-
const links = Array.from(el.querySelectorAll('a[href]'))
|
|
182
|
-
.filter((a) => a instanceof HTMLAnchorElement)
|
|
183
|
-
.map(a => ({ text: (a.textContent ?? '').trim().substring(0, 100), href: a.href }));
|
|
184
|
-
if (links.length > 0)
|
|
185
|
-
data.links = JSON.stringify(links);
|
|
186
|
-
// Изображения
|
|
187
|
-
const imgs = Array.from(el.querySelectorAll('img[src]'))
|
|
188
|
-
.map(img => img.src)
|
|
189
|
-
.slice(0, 3);
|
|
190
|
-
if (imgs.length > 0)
|
|
191
|
-
data.images = JSON.stringify(imgs);
|
|
192
|
-
// Дочерние элементы с有意义ными классами
|
|
193
|
-
const children = Array.from(el.querySelectorAll('[class]'))
|
|
194
|
-
.filter(c => c !== el && c.className && typeof c.className === 'string')
|
|
195
|
-
.slice(0, 10)
|
|
196
|
-
.map(c => ({
|
|
197
|
-
tag: c.tagName,
|
|
198
|
-
class: c.className.substring(0, 60),
|
|
199
|
-
text: (c.textContent ?? '').trim().substring(0, 100),
|
|
200
|
-
}));
|
|
201
|
-
if (children.length > 0)
|
|
202
|
-
data.children = JSON.stringify(children);
|
|
203
|
-
return data;
|
|
204
|
-
});
|
|
205
|
-
return {
|
|
206
|
-
found: true,
|
|
207
|
-
groupTag: bestGroup.tag,
|
|
208
|
-
groupClass: bestGroup.className,
|
|
209
|
-
totalInGroup: bestGroup.count,
|
|
210
|
-
items,
|
|
211
|
-
};
|
|
212
|
-
}, { pat: pattern, max: maxItems });
|
|
213
|
-
if (!scrapeResult.found) {
|
|
214
|
-
return `Scrape: ${scrapeResult.message}\n\nTry a different pattern or use discover to see page structure.`;
|
|
215
|
-
}
|
|
216
|
-
// Форматируем вывод
|
|
217
|
-
let output = `Scrape results: ${scrapeResult.totalInGroup} items found (showing ${scrapeResult.items.length})\n`;
|
|
218
|
-
output += `Selector: <${scrapeResult.groupTag} class="${scrapeResult.groupClass}">\n\n`;
|
|
219
|
-
for (let i = 0; i < scrapeResult.items.length; i++) {
|
|
220
|
-
const item = scrapeResult.items[i];
|
|
221
|
-
output += `--- Item ${i + 1} ---\n`;
|
|
222
|
-
if (item.text)
|
|
223
|
-
output += `Text: ${item.text}\n`;
|
|
224
|
-
if (item.links) {
|
|
225
|
-
const links = JSON.parse(item.links);
|
|
226
|
-
for (const link of links) {
|
|
227
|
-
output += `Link: ${link.text} → ${link.href}\n`;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (item.images) {
|
|
231
|
-
const imgs = JSON.parse(item.images);
|
|
232
|
-
for (const img of imgs) {
|
|
233
|
-
output += `Image: ${img}\n`;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (item.children) {
|
|
237
|
-
const children = JSON.parse(item.children);
|
|
238
|
-
for (const child of children) {
|
|
239
|
-
output += ` <${child.tag} class="${child.class}"> ${child.text}\n`;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
output += '\n';
|
|
243
|
-
}
|
|
244
|
-
return output;
|
|
245
|
-
}
|
|
246
|
-
async function actionNavigate(page, args) {
|
|
247
|
-
const url = String(args.url ?? '').trim();
|
|
248
|
-
if (!url)
|
|
249
|
-
return { text: 'Error: url is required for navigate', status: 0 };
|
|
250
|
-
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
251
|
-
return { text: 'Error: URL must start with http:// or https://', status: 0 };
|
|
252
|
-
}
|
|
253
|
-
const response = await page.goto(url, {
|
|
254
|
-
waitUntil: 'domcontentloaded',
|
|
255
|
-
timeout: NAV_TIMEOUT_MS,
|
|
256
|
-
});
|
|
257
|
-
// Ждём появления body
|
|
258
|
-
await page.waitForSelector('body', { timeout: 5000 }).catch(() => { });
|
|
259
|
-
// Ждём networkidle для AJAX-страниц (макс 8 сек)
|
|
260
|
-
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => { });
|
|
261
|
-
// Доп. ожидание для тяжёлых JS-страниц
|
|
262
|
-
await page.waitForTimeout(2000);
|
|
263
|
-
// Универсальный автоскролл: скроллим пока DOM стабилизируется
|
|
264
|
-
// (нет новых элементов после скролла) или до лимита (8 скроллов).
|
|
265
|
-
// Покрывает: lazy loading, infinite scroll, AJAX-каталоги.
|
|
266
|
-
try {
|
|
267
|
-
let prevCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
268
|
-
const maxScrolls = 8;
|
|
269
|
-
let scrollsDone = 0;
|
|
270
|
-
for (let i = 0; i < maxScrolls; i++) {
|
|
271
|
-
// Скроллим вниз на высоту вьюпорта
|
|
272
|
-
await page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
273
|
-
scrollsDone++;
|
|
274
|
-
// Ждём загрузки контента (networkidle + фиксированная пауза)
|
|
275
|
-
await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => { });
|
|
276
|
-
await page.waitForTimeout(1500);
|
|
277
|
-
// Проверяем стабилизацию DOM
|
|
278
|
-
const newCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
279
|
-
if (newCount === prevCount) {
|
|
280
|
-
// DOM стабилизировался — пробуем ещё раз для надёжности
|
|
281
|
-
await page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
282
|
-
await page.waitForLoadState('networkidle', { timeout: 3000 }).catch(() => { });
|
|
283
|
-
await page.waitForTimeout(1000);
|
|
284
|
-
const finalCount = await page.evaluate(() => document.querySelectorAll('*').length);
|
|
285
|
-
if (finalCount === prevCount)
|
|
286
|
-
break; // Стабильно — выходим
|
|
287
|
-
}
|
|
288
|
-
prevCount = newCount;
|
|
289
|
-
}
|
|
290
|
-
// Вернуться наверх
|
|
291
|
-
await page.evaluate(() => window.scrollTo(0, 0));
|
|
292
|
-
await page.waitForTimeout(500);
|
|
293
|
-
}
|
|
294
|
-
catch { /* ignore — scroll is best-effort */ }
|
|
295
|
-
const status = response?.status() ?? 0;
|
|
296
|
-
const title = await page.title();
|
|
297
|
-
const finalUrl = page.url();
|
|
298
|
-
// Сводка страницы: текст + ссылки + классы
|
|
299
|
-
let summary = '';
|
|
300
|
-
try {
|
|
301
|
-
const info = await page.evaluate(() => {
|
|
302
|
-
// Текст страницы (первые 1500 символов для preview)
|
|
303
|
-
const bodyText = (document.body?.innerText ?? '').trim().substring(0, 1500);
|
|
304
|
-
// Ссылки (до 10)
|
|
305
|
-
const allLinks = Array.from(document.querySelectorAll('a[href]'))
|
|
306
|
-
.filter((a) => a instanceof HTMLAnchorElement)
|
|
307
|
-
.filter(a => !a.href.startsWith('javascript:') && !a.href.startsWith('#'));
|
|
308
|
-
const totalLinks = allLinks.length;
|
|
309
|
-
const links = allLinks.slice(0, 10).map(a => ({
|
|
310
|
-
text: (a.textContent ?? '').trim().substring(0, 60),
|
|
311
|
-
href: a.href,
|
|
312
|
-
parentClass: a.closest('div[class], li[class], article[class], section[class]')?.className?.substring(0, 40) ?? '',
|
|
313
|
-
}));
|
|
314
|
-
// Ключевые классы
|
|
315
|
-
const classSet = new Set();
|
|
316
|
-
for (const el of document.querySelectorAll('*')) {
|
|
317
|
-
for (const cls of el.classList) {
|
|
318
|
-
if (cls.length > 2 && cls.length < 50)
|
|
319
|
-
classSet.add(cls);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const classes = Array.from(classSet).sort();
|
|
323
|
-
const totalElements = document.querySelectorAll('*').length;
|
|
324
|
-
return { bodyText, links, totalLinks, classes, totalElements };
|
|
325
|
-
});
|
|
326
|
-
summary = `\n\nPage preview (${info.totalElements} elements, ${info.totalLinks} links):`;
|
|
327
|
-
if (info.bodyText) {
|
|
328
|
-
summary += `\nContent:\n${info.bodyText}`;
|
|
329
|
-
if (info.bodyText.length >= 1500)
|
|
330
|
-
summary += `\n... (content truncated)`;
|
|
331
|
-
}
|
|
332
|
-
if (info.links.length > 0) {
|
|
333
|
-
summary += `\n\nLinks (${info.links.length} of ${info.totalLinks} shown):`;
|
|
334
|
-
for (const link of info.links) {
|
|
335
|
-
summary += `\n "${link.text || '(no text)'}" → ${link.href}`;
|
|
336
|
-
if (link.parentClass)
|
|
337
|
-
summary += ` [${link.parentClass}]`;
|
|
338
|
-
}
|
|
339
|
-
if (info.totalLinks > 10)
|
|
340
|
-
summary += `\n ... and ${info.totalLinks - 10} more. Use get_links to see all.`;
|
|
341
|
-
}
|
|
342
|
-
if (info.classes.length > 0) {
|
|
343
|
-
summary += `\nKey CSS classes: ${info.classes.slice(0, 15).join(', ')}${info.classes.length > 15 ? ` (+${info.classes.length - 15} more)` : ''}`;
|
|
344
|
-
}
|
|
345
|
-
summary += `\n\nSuggested next steps:`;
|
|
346
|
-
summary += `\n - web_browse(action:"get_links") — all links on page`;
|
|
347
|
-
summary += `\n - web_browse(action:"extract", selector:".content") — extract specific section`;
|
|
348
|
-
summary += `\n - web_browse(action:"discover") — full page structure with CSS selectors`;
|
|
349
|
-
summary += `\n - web_browse(action:"scrape", pattern:"...") — auto-scrape items from page`;
|
|
350
|
-
}
|
|
351
|
-
catch { /* ignore — summary is optional */ }
|
|
352
|
-
return { text: `Navigated to: ${finalUrl}\nTitle: ${title}\nStatus: ${status}${summary}`, status };
|
|
353
|
-
}
|
|
354
|
-
async function actionClick(page, args) {
|
|
355
|
-
const selector = String(args.selector ?? '').trim();
|
|
356
|
-
if (!selector)
|
|
357
|
-
return 'Error: selector is required for click';
|
|
358
|
-
// Пробуем CSS-селектор, затем текст
|
|
359
|
-
try {
|
|
360
|
-
await page.click(selector, { timeout: 10_000 });
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
// Если CSS-селектор не сработал, пробуем getByText
|
|
364
|
-
try {
|
|
365
|
-
await page.getByText(selector, { exact: false }).first().click({ timeout: 10_000 });
|
|
366
|
-
}
|
|
367
|
-
catch (e) {
|
|
368
|
-
return `Error: could not click "${selector}": ${e.message}`;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
// Ждём стабилизации после клика
|
|
372
|
-
await page.waitForLoadState('domcontentloaded').catch(() => { });
|
|
373
|
-
return `Clicked: ${selector}`;
|
|
374
|
-
}
|
|
375
|
-
async function actionType(page, args) {
|
|
376
|
-
const selector = String(args.selector ?? '').trim();
|
|
377
|
-
const value = String(args.value ?? '');
|
|
378
|
-
if (!selector)
|
|
379
|
-
return 'Error: selector is required for type';
|
|
380
|
-
try {
|
|
381
|
-
await page.fill(selector, value, { timeout: 10_000 });
|
|
382
|
-
}
|
|
383
|
-
catch {
|
|
384
|
-
// Fallback: кликаем и печатаем
|
|
385
|
-
try {
|
|
386
|
-
await page.click(selector, { timeout: 5_000 });
|
|
387
|
-
await page.keyboard.type(value, { delay: 30 });
|
|
388
|
-
}
|
|
389
|
-
catch (e) {
|
|
390
|
-
return `Error: could not type into "${selector}": ${e.message}`;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return `Typed "${value}" into ${selector}`;
|
|
394
|
-
}
|
|
395
|
-
async function actionExtract(page, args) {
|
|
396
|
-
const selector = String(args.selector ?? '').trim();
|
|
397
|
-
const format = String(args.format ?? 'text');
|
|
398
|
-
const maxChars = Number(args.maxChars) || 10_000;
|
|
399
|
-
let content;
|
|
400
|
-
if (selector) {
|
|
401
|
-
// Извлечение из конкретного элемента
|
|
402
|
-
try {
|
|
403
|
-
if (format === 'html') {
|
|
404
|
-
content = await page.innerHTML(selector, { timeout: 10_000 });
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
content = await page.textContent(selector, { timeout: 10_000 }) ?? '';
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
catch (e) {
|
|
411
|
-
return `Error: could not extract from "${selector}": ${e.message}. Hint: extract the full page first (no selector) to see the actual structure, then choose the right selector.`;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
// Извлечение всей страницы
|
|
416
|
-
if (format === 'html') {
|
|
417
|
-
content = await page.content();
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
content = await page.evaluate(() => document.body?.innerText ?? '');
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
// Очистка
|
|
424
|
-
content = content
|
|
425
|
-
.replace(/\t/g, ' ')
|
|
426
|
-
.replace(/[ ]{2,}/g, ' ')
|
|
427
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
428
|
-
.trim();
|
|
429
|
-
if (content.length > maxChars) {
|
|
430
|
-
content = content.slice(0, maxChars) + `\n... (truncated, ${content.length} total chars)`;
|
|
431
|
-
}
|
|
432
|
-
return content || '(empty)';
|
|
433
|
-
}
|
|
434
|
-
async function actionScreenshot(page, args) {
|
|
435
|
-
const selector = String(args.selector ?? '').trim();
|
|
436
|
-
let buffer;
|
|
437
|
-
if (selector) {
|
|
438
|
-
try {
|
|
439
|
-
const element = await page.$(selector);
|
|
440
|
-
if (!element)
|
|
441
|
-
return `Error: element "${selector}" not found`;
|
|
442
|
-
buffer = await element.screenshot({ type: 'png' });
|
|
443
|
-
}
|
|
444
|
-
catch (e) {
|
|
445
|
-
return `Error: screenshot of "${selector}" failed: ${e.message}`;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
buffer = await page.screenshot({ type: 'png', fullPage: false });
|
|
450
|
-
}
|
|
451
|
-
const base64 = buffer.toString('base64');
|
|
452
|
-
const url = page.url();
|
|
453
|
-
const title = await page.title();
|
|
454
|
-
return `Screenshot of: ${url}\nTitle: ${title}\nSize: ${buffer.length} bytes\nFormat: PNG\n\nbase64:${base64}`;
|
|
455
|
-
}
|
|
456
|
-
async function actionScroll(page, args) {
|
|
457
|
-
const direction = String(args.direction ?? 'down');
|
|
458
|
-
const scrolled = await page.evaluate((dir) => {
|
|
459
|
-
const amount = dir === 'up' ? -window.innerHeight : window.innerHeight;
|
|
460
|
-
window.scrollBy(0, amount);
|
|
461
|
-
return {
|
|
462
|
-
scrollTop: window.scrollY,
|
|
463
|
-
scrollHeight: document.documentElement.scrollHeight,
|
|
464
|
-
clientHeight: window.innerHeight,
|
|
465
|
-
};
|
|
466
|
-
}, direction);
|
|
467
|
-
const atTop = scrolled.scrollTop <= 0;
|
|
468
|
-
const atBottom = scrolled.scrollTop + scrolled.clientHeight >= scrolled.scrollHeight - 10;
|
|
469
|
-
let status = `Scrolled ${direction}. Position: ${scrolled.scrollTop}px`;
|
|
470
|
-
if (atTop && direction === 'up')
|
|
471
|
-
status += ' (at top)';
|
|
472
|
-
if (atBottom && direction === 'down')
|
|
473
|
-
status += ' (at bottom)';
|
|
474
|
-
return status;
|
|
475
|
-
}
|
|
476
|
-
async function actionWait(page, args) {
|
|
477
|
-
const selector = String(args.selector ?? '').trim();
|
|
478
|
-
const condition = String(args.condition ?? '').trim();
|
|
479
|
-
const timeout = Number(args.timeout) || 5000;
|
|
480
|
-
if (condition) {
|
|
481
|
-
try {
|
|
482
|
-
await page.waitForFunction(condition, { timeout });
|
|
483
|
-
return `Condition "${condition}" met`;
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
return `Timeout: condition "${condition}" not met within ${timeout}ms`;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
else if (selector) {
|
|
490
|
-
try {
|
|
491
|
-
await page.waitForSelector(selector, { state: 'visible', timeout });
|
|
492
|
-
return `Element "${selector}" appeared`;
|
|
493
|
-
}
|
|
494
|
-
catch {
|
|
495
|
-
return `Timeout: element "${selector}" not found within ${timeout}ms. Hint: extract the full page first (no selector) to see the actual structure.`;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
else {
|
|
499
|
-
await page.waitForTimeout(timeout);
|
|
500
|
-
return `Waited ${timeout}ms`;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
async function actionDiscover(page, _args) {
|
|
504
|
-
const result = await page.evaluate(() => {
|
|
505
|
-
// Все уникальные CSS-классы на странице
|
|
506
|
-
const allElements = document.querySelectorAll('*');
|
|
507
|
-
const classSet = new Set();
|
|
508
|
-
for (const el of allElements) {
|
|
509
|
-
for (const cls of el.classList) {
|
|
510
|
-
if (cls.length > 1 && cls.length < 60)
|
|
511
|
-
classSet.add(cls);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
const classes = Array.from(classSet).sort();
|
|
515
|
-
// Все ссылки (до 50)
|
|
516
|
-
const links = Array.from(document.querySelectorAll('a[href]'))
|
|
517
|
-
.filter((a) => a instanceof HTMLAnchorElement)
|
|
518
|
-
.filter(a => !a.href.startsWith('javascript:') && !a.href.startsWith('#'))
|
|
519
|
-
.slice(0, 50)
|
|
520
|
-
.map(a => ({
|
|
521
|
-
text: (a.textContent ?? '').trim().substring(0, 100),
|
|
522
|
-
href: a.href,
|
|
523
|
-
parentClass: a.closest('div[class], li[class], article[class], section[class]')?.className ?? '',
|
|
524
|
-
}));
|
|
525
|
-
return { classes, links };
|
|
526
|
-
});
|
|
527
|
-
let output = `=== DISCOVER: ${result.classes.length} unique classes, ${result.links.length} links ===\n\n`;
|
|
528
|
-
if (result.links.length > 0) {
|
|
529
|
-
output += '--- Links (first 20) ---\n';
|
|
530
|
-
for (const link of result.links.slice(0, 20)) {
|
|
531
|
-
output += ` "${link.text || '(no text)'}" → ${link.href}\n parent: ${link.parentClass}\n`;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
output += `\n--- All classes (${result.classes.length}) ---\n`;
|
|
535
|
-
output += result.classes.join(', ');
|
|
536
|
-
return output;
|
|
537
|
-
}
|
|
538
|
-
async function actionGetLinks(page, _args) {
|
|
539
|
-
const links = await page.$$eval('a[href]', (anchors) => anchors.map((a) => ({
|
|
540
|
-
text: (a.textContent ?? '').trim().slice(0, 100),
|
|
541
|
-
href: a.getAttribute('href') ?? '',
|
|
542
|
-
})).filter((l) => l.href && !l.href.startsWith('#') && !l.href.startsWith('javascript:')));
|
|
543
|
-
if (links.length === 0)
|
|
544
|
-
return 'No links found on page.';
|
|
545
|
-
const maxLinks = 50;
|
|
546
|
-
let output = `Found ${links.length} links:\n\n`;
|
|
547
|
-
for (let i = 0; i < Math.min(links.length, maxLinks); i++) {
|
|
548
|
-
const l = links[i];
|
|
549
|
-
output += `${i + 1}. ${l.text || '(no text)'}\n ${l.href}\n`;
|
|
550
|
-
}
|
|
551
|
-
if (links.length > maxLinks) {
|
|
552
|
-
output += `\n... and ${links.length - maxLinks} more`;
|
|
553
|
-
}
|
|
554
|
-
return output.trim();
|
|
555
|
-
}
|
|
556
|
-
async function actionEvaluate(page, args) {
|
|
557
|
-
const code = String(args.value ?? args.code ?? '').trim();
|
|
558
|
-
if (!code)
|
|
559
|
-
return 'Error: value (JS code) is required for evaluate';
|
|
560
|
-
try {
|
|
561
|
-
// eval() в контексте страницы с IIFE-fallback для return-стейтментов.
|
|
562
|
-
// Автоматически подменяет querySelectorAll чтобы возвращал Array (а не NodeList).
|
|
563
|
-
// Это решает проблему "slice is not a function" для маленьких моделей.
|
|
564
|
-
const result = await page.evaluate((script) => {
|
|
565
|
-
// eslint-disable-next-line no-eval
|
|
566
|
-
let raw;
|
|
567
|
-
try {
|
|
568
|
-
raw = eval(script);
|
|
569
|
-
}
|
|
570
|
-
catch (e) {
|
|
571
|
-
if (e instanceof SyntaxError && /\breturn\b/.test(script)) {
|
|
572
|
-
// Оборачиваем в IIFE чтобы return работал
|
|
573
|
-
raw = eval('(function(){ ' + script + ' })()');
|
|
574
|
-
}
|
|
575
|
-
else if (e instanceof TypeError && /slice is not a function/i.test(e.message)) {
|
|
576
|
-
// querySelectorAll вернул NodeList — подменяем querySelectorAll чтобы возвращал Array
|
|
577
|
-
const wrapped = `
|
|
578
|
-
(function(){
|
|
579
|
-
var _origQSA = document.querySelectorAll.bind(document);
|
|
580
|
-
document.querySelectorAll = function(s){ return Array.from(_origQSA(s)); };
|
|
581
|
-
var _origQE = document.querySelector.bind(document);
|
|
582
|
-
${script}
|
|
583
|
-
})()
|
|
584
|
-
`;
|
|
585
|
-
raw = eval(wrapped);
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
throw e;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
// Автоматически конвертируем NodeList/HTMLCollection в Array для JSON
|
|
592
|
-
if (raw instanceof NodeList || raw instanceof HTMLCollection) {
|
|
593
|
-
return Array.from(raw);
|
|
594
|
-
}
|
|
595
|
-
return raw;
|
|
596
|
-
}, code);
|
|
597
|
-
if (result === undefined)
|
|
598
|
-
return '(undefined)';
|
|
599
|
-
if (result === null)
|
|
600
|
-
return '(null)';
|
|
601
|
-
if (typeof result === 'object')
|
|
602
|
-
return JSON.stringify(result, null, 2);
|
|
603
|
-
return String(result);
|
|
604
|
-
}
|
|
605
|
-
catch (e) {
|
|
606
|
-
const msg = e.message ?? String(e);
|
|
607
|
-
// Подсказка при типичных ошибках
|
|
608
|
-
if (msg.includes('slice is not a function')) {
|
|
609
|
-
return `Error: ${msg}\nHint: querySelectorAll returns NodeList, not Array. Use Array.from():\n WRONG: document.querySelectorAll('.item').slice(0,5)\n RIGHT: Array.from(document.querySelectorAll('.item')).slice(0,5)\n RIGHT: [...document.querySelectorAll('.item')].slice(0,5)`;
|
|
610
|
-
}
|
|
611
|
-
if (msg.includes('SyntaxError') && msg.includes('return')) {
|
|
612
|
-
return `Error: ${msg}\nHint: "return" works but code must be valid JS inside a function body. Check for syntax errors in your code.\n Example: return document.title\n Example: return Array.from(document.querySelectorAll('a')).map(a => a.href)`;
|
|
613
|
-
}
|
|
614
|
-
if (msg.includes('SyntaxError')) {
|
|
615
|
-
return `Error: JS syntax error — ${msg}\nHint: check parentheses, quotes, and semicolons. evaluate runs JS in browser context.`;
|
|
616
|
-
}
|
|
617
|
-
// Ошибки "null/undefined" — скорее всего селектор не найден
|
|
618
|
-
if (msg.includes('Cannot read propert') || msg.includes('is null') || msg.includes('is undefined')
|
|
619
|
-
|| msg.includes('null') || msg.includes('undefined')) {
|
|
620
|
-
return `Error: ${msg}\nHint: A selector likely returned null. Use web_browse(action:"discover") to find real CSS selectors on this page, then retry with the correct selector.`;
|
|
621
|
-
}
|
|
622
|
-
return `Error: evaluate failed: ${msg}`;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
// ─── Tool Definition ─────────────────────────────────────
|
|
626
|
-
export const webBrowseDefinition = {
|
|
627
|
-
type: 'function',
|
|
628
|
-
function: {
|
|
629
|
-
name: 'web_browse',
|
|
630
|
-
description: 'Interact with web pages using a headless browser. Navigate, click, type, scroll, extract text/HTML, take screenshots, and execute JavaScript on pages.',
|
|
631
|
-
parameters: {
|
|
632
|
-
type: 'object',
|
|
633
|
-
properties: {
|
|
634
|
-
action: {
|
|
635
|
-
type: 'string',
|
|
636
|
-
enum: ['navigate', 'click', 'type', 'extract', 'screenshot', 'scroll', 'wait', 'get_links', 'evaluate', 'discover', 'scrape', 'close'],
|
|
637
|
-
description: 'Action to perform on the page',
|
|
638
|
-
},
|
|
639
|
-
url: { type: 'string', description: 'URL to navigate to (for navigate action)' },
|
|
640
|
-
selector: { type: 'string', description: 'CSS selector or text to target an element' },
|
|
641
|
-
value: { type: 'string', description: 'Value to type, or JS code to evaluate' },
|
|
642
|
-
direction: { type: 'string', enum: ['up', 'down'], description: 'Scroll direction (default: down)' },
|
|
643
|
-
format: { type: 'string', enum: ['text', 'html'], description: 'Extract format (default: text)' },
|
|
644
|
-
maxChars: { type: 'number', description: 'Max characters to extract (default: 10000)' },
|
|
645
|
-
timeout: { type: 'number', description: 'Timeout in ms for wait action (default: 5000)' },
|
|
646
|
-
condition: { type: 'string', description: 'JS expression to wait for (e.g. "document.querySelectorAll(\\\'.item\\\').length > 0") — for wait action only' },
|
|
647
|
-
pattern: { type: 'string', description: 'Regex pattern to match element classes (for scrape, default: "product|card|item|goods")' },
|
|
648
|
-
maxItems: { type: 'number', description: 'Max items to scrape (default: 10, max: 50)' },
|
|
649
|
-
fields: { type: 'string', description: 'Comma-separated fields to extract (for scrape, e.g. "title,price,href")' },
|
|
650
|
-
},
|
|
651
|
-
required: ['action'],
|
|
652
|
-
},
|
|
653
|
-
},
|
|
654
|
-
};
|
|
655
|
-
// ─── Handler ─────────────────────────────────────────────
|
|
656
|
-
export const webBrowseHandler = {
|
|
657
|
-
name: 'web_browse',
|
|
658
|
-
definition: webBrowseDefinition,
|
|
659
|
-
async execute(args) {
|
|
660
|
-
const action = String(args.action ?? '').trim();
|
|
661
|
-
if (!action) {
|
|
662
|
-
return { stdout: '', stderr: 'action is required', exitCode: 1, truncated: false };
|
|
663
|
-
}
|
|
664
|
-
try {
|
|
665
|
-
// Проверка простой сессии перед каждым action'ом
|
|
666
|
-
await checkIdle();
|
|
667
|
-
// Close action
|
|
668
|
-
if (action === 'close') {
|
|
669
|
-
await closeBrowser();
|
|
670
|
-
return { stdout: 'Browser closed.', stderr: '', exitCode: 0, truncated: false };
|
|
671
|
-
}
|
|
672
|
-
// Navigate не требует существующей страницы
|
|
673
|
-
if (action === 'navigate') {
|
|
674
|
-
// Валидация URL до запуска браузера
|
|
675
|
-
const url = String(args.url ?? '').trim();
|
|
676
|
-
if (!url) {
|
|
677
|
-
return { stdout: '', stderr: 'url is required for navigate', exitCode: 1, truncated: false };
|
|
678
|
-
}
|
|
679
|
-
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
680
|
-
return { stdout: '', stderr: 'URL must start with http:// or https://', exitCode: 1, truncated: false };
|
|
681
|
-
}
|
|
682
|
-
const page = await ensureBrowser();
|
|
683
|
-
const navResult = await actionNavigate(page, args);
|
|
684
|
-
const isHttpError = navResult.status >= 400;
|
|
685
|
-
return {
|
|
686
|
-
stdout: navResult.text,
|
|
687
|
-
stderr: isHttpError ? `HTTP ${navResult.status}` : '',
|
|
688
|
-
exitCode: isHttpError ? 1 : 0,
|
|
689
|
-
truncated: false,
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
// Scrape тоже может навигировать сам (если указан URL)
|
|
693
|
-
if (action === 'scrape') {
|
|
694
|
-
const url = String(args.url ?? '').trim();
|
|
695
|
-
if (url && !url.startsWith('http://') && !url.startsWith('https://')) {
|
|
696
|
-
return { stdout: '', stderr: 'URL must start with http:// or https://', exitCode: 1, truncated: false };
|
|
697
|
-
}
|
|
698
|
-
const page = await ensureBrowser();
|
|
699
|
-
const result = await actionScrape(page, args);
|
|
700
|
-
const exitCode = result.startsWith('Error') ? 1 : 0;
|
|
701
|
-
return { stdout: result, stderr: '', exitCode, truncated: false };
|
|
702
|
-
}
|
|
703
|
-
// Все остальные action'ы требуют активную страницу
|
|
704
|
-
if (!state.page || state.page.isClosed()) {
|
|
705
|
-
return {
|
|
706
|
-
stdout: '',
|
|
707
|
-
stderr: 'No active page. Use navigate action first.',
|
|
708
|
-
exitCode: 1,
|
|
709
|
-
truncated: false,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
let result;
|
|
713
|
-
switch (action) {
|
|
714
|
-
case 'click':
|
|
715
|
-
result = await actionClick(state.page, args);
|
|
716
|
-
break;
|
|
717
|
-
case 'type':
|
|
718
|
-
result = await actionType(state.page, args);
|
|
719
|
-
break;
|
|
720
|
-
case 'extract':
|
|
721
|
-
result = await actionExtract(state.page, args);
|
|
722
|
-
break;
|
|
723
|
-
case 'screenshot':
|
|
724
|
-
result = await actionScreenshot(state.page, args);
|
|
725
|
-
break;
|
|
726
|
-
case 'scroll':
|
|
727
|
-
result = await actionScroll(state.page, args);
|
|
728
|
-
break;
|
|
729
|
-
case 'wait':
|
|
730
|
-
result = await actionWait(state.page, args);
|
|
731
|
-
break;
|
|
732
|
-
case 'get_links':
|
|
733
|
-
result = await actionGetLinks(state.page, args);
|
|
734
|
-
break;
|
|
735
|
-
case 'discover':
|
|
736
|
-
result = await actionDiscover(state.page, args);
|
|
737
|
-
break;
|
|
738
|
-
case 'evaluate':
|
|
739
|
-
result = await actionEvaluate(state.page, args);
|
|
740
|
-
break;
|
|
741
|
-
case 'scrape':
|
|
742
|
-
result = await actionScrape(state.page, args);
|
|
743
|
-
break;
|
|
744
|
-
default:
|
|
745
|
-
return { stdout: '', stderr: `Unknown action: ${action}`, exitCode: 1, truncated: false };
|
|
746
|
-
}
|
|
747
|
-
state.lastActivity = Date.now();
|
|
748
|
-
const exitCode = result.startsWith('Error') || result.startsWith('Timeout') ? 1 : 0;
|
|
749
|
-
return { stdout: result, stderr: '', exitCode, truncated: false };
|
|
750
|
-
}
|
|
751
|
-
catch (e) {
|
|
752
|
-
logError('BROWSER', `${action} error: ${e.message}`);
|
|
753
|
-
return { stdout: '', stderr: `Browser error: ${e.message}`, exitCode: 1, truncated: false };
|
|
754
|
-
}
|
|
755
|
-
},
|
|
756
|
-
};
|
|
757
|
-
//# sourceMappingURL=web-browse.js.map
|