@jcheesepkg/nanobot 0.9.1 → 0.9.2
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 +18 -18
- package/dist/agent/context.d.mts +4 -4
- package/dist/agent/context.d.mts.map +1 -1
- package/dist/agent/context.mjs +27 -28
- package/dist/agent/context.mjs.map +1 -1
- package/dist/agent/loop.d.mts +5 -3
- package/dist/agent/loop.d.mts.map +1 -1
- package/dist/agent/loop.mjs +64 -55
- package/dist/agent/loop.mjs.map +1 -1
- package/dist/agent/memory.d.mts.map +1 -1
- package/dist/agent/memory.mjs +3 -3
- package/dist/agent/memory.mjs.map +1 -1
- package/dist/agent/skills.d.mts.map +1 -1
- package/dist/agent/skills.mjs +4 -4
- package/dist/agent/skills.mjs.map +1 -1
- package/dist/agent/subagent.d.mts.map +1 -1
- package/dist/agent/subagent.mjs +22 -22
- package/dist/agent/subagent.mjs.map +1 -1
- package/dist/agent/tools/base.mjs +2 -2
- package/dist/agent/tools/base.mjs.map +1 -1
- package/dist/agent/tools/cron.d.mts +1 -1
- package/dist/agent/tools/cron.d.mts.map +1 -1
- package/dist/agent/tools/cron.mjs +11 -11
- package/dist/agent/tools/cron.mjs.map +1 -1
- package/dist/agent/tools/filesystem.d.mts +4 -4
- package/dist/agent/tools/filesystem.d.mts.map +1 -1
- package/dist/agent/tools/filesystem.mjs +20 -20
- package/dist/agent/tools/filesystem.mjs.map +1 -1
- package/dist/agent/tools/flex.d.mts +1 -1
- package/dist/agent/tools/flex.d.mts.map +1 -1
- package/dist/agent/tools/flex.mjs +112 -112
- package/dist/agent/tools/flex.mjs.map +1 -1
- package/dist/agent/tools/flex.test.mjs +60 -59
- package/dist/agent/tools/flex.test.mjs.map +1 -1
- package/dist/agent/tools/message.d.mts +1 -1
- package/dist/agent/tools/message.d.mts.map +1 -1
- package/dist/agent/tools/message.mjs +4 -4
- package/dist/agent/tools/message.mjs.map +1 -1
- package/dist/agent/tools/registry.d.mts.map +1 -1
- package/dist/agent/tools/registry.mjs +4 -4
- package/dist/agent/tools/registry.mjs.map +1 -1
- package/dist/agent/tools/shell.d.mts +1 -1
- package/dist/agent/tools/shell.mjs +4 -4
- package/dist/agent/tools/shell.mjs.map +1 -1
- package/dist/agent/tools/spawn.d.mts +1 -1
- package/dist/agent/tools/spawn.d.mts.map +1 -1
- package/dist/agent/tools/spawn.mjs +4 -4
- package/dist/agent/tools/spawn.mjs.map +1 -1
- package/dist/agent/tools/web.d.mts +2 -2
- package/dist/agent/tools/web.d.mts.map +1 -1
- package/dist/agent/tools/web.mjs +36 -36
- package/dist/agent/tools/web.mjs.map +1 -1
- package/dist/bus/events.mjs +1 -1
- package/dist/bus/events.mjs.map +1 -1
- package/dist/bus/queue.d.mts.map +1 -1
- package/dist/bus/queue.mjs.map +1 -1
- package/dist/channels/base.d.mts.map +1 -1
- package/dist/channels/base.mjs +2 -2
- package/dist/channels/base.mjs.map +1 -1
- package/dist/channels/line.d.mts +1 -0
- package/dist/channels/line.d.mts.map +1 -1
- package/dist/channels/line.mjs +65 -65
- package/dist/channels/line.mjs.map +1 -1
- package/dist/channels/line.test.mjs +26 -27
- package/dist/channels/line.test.mjs.map +1 -1
- package/dist/channels/manager.d.mts.map +1 -1
- package/dist/channels/manager.mjs +9 -9
- package/dist/channels/manager.mjs.map +1 -1
- package/dist/channels/telegram.mjs +34 -34
- package/dist/channels/telegram.mjs.map +1 -1
- package/dist/cli/index.mjs +36 -36
- package/dist/cli/index.mjs.map +1 -1
- package/dist/config/loader.d.mts.map +1 -1
- package/dist/config/loader.mjs +1 -1
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/schema.d.mts +387 -387
- package/dist/config/schema.d.mts.map +1 -1
- package/dist/config/schema.mjs +42 -42
- package/dist/config/schema.mjs.map +1 -1
- package/dist/gateway/server.d.mts.map +1 -1
- package/dist/gateway/server.mjs +48 -54
- package/dist/gateway/server.mjs.map +1 -1
- package/dist/heartbeat/service.d.mts.map +1 -1
- package/dist/heartbeat/service.mjs +8 -8
- package/dist/heartbeat/service.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/node_modules/{@jridgewell → .bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell}/sourcemap-codec/dist/sourcemap-codec.mjs +1 -1
- package/dist/node_modules/.bun/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_expect@2.1.9/node_modules/@vitest}/expect/dist/index.mjs +8 -8
- package/dist/node_modules/.bun/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_pretty-format@2.1.9/node_modules/@vitest}/pretty-format/dist/index.mjs +2 -2
- package/dist/node_modules/.bun/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/chunk-tasks.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/index.mjs +6 -6
- package/dist/node_modules/.bun/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_snapshot@2.1.9/node_modules/@vitest}/snapshot/dist/index.mjs +5 -5
- package/dist/node_modules/.bun/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_spy@2.1.9/node_modules/@vitest}/spy/dist/index.mjs +2 -2
- package/dist/node_modules/.bun/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/chunk-_commonjsHelpers.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/diff.mjs +4 -4
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/error.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/helpers.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/index.mjs +3 -3
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.mjs.map +1 -0
- package/dist/node_modules/{@vitest → .bun/@vitest_utils@2.1.9/node_modules/@vitest}/utils/dist/source-map.mjs +1 -1
- package/dist/node_modules/.bun/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.mjs.map +1 -0
- package/dist/node_modules/{chai → .bun/chai@5.3.3/node_modules/chai}/index.mjs +1 -1
- package/dist/node_modules/.bun/chai@5.3.3/node_modules/chai/index.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/arguments.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/arguments.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/array.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/array.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/bigint.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/bigint.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/class.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/class.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/date.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/date.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/error.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/error.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/function.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/function.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/helpers.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/helpers.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/html.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/html.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/index.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/index.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/map.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/map.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/number.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/number.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/object.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/object.mjs.map +1 -0
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs +6 -0
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/promise.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/regexp.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/regexp.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/set.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/set.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/string.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/string.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/symbol.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/symbol.mjs.map +1 -0
- package/dist/node_modules/{loupe → .bun/loupe@3.2.1/node_modules/loupe}/lib/typedarray.mjs +1 -1
- package/dist/node_modules/.bun/loupe@3.2.1/node_modules/loupe/lib/typedarray.mjs.map +1 -0
- package/dist/node_modules/{magic-string → .bun/magic-string@0.30.21/node_modules/magic-string}/dist/magic-string.es.mjs +2 -2
- package/dist/node_modules/.bun/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs.map +1 -0
- package/dist/node_modules/{@vitest/snapshot → .bun/pathe@1.1.2}/node_modules/pathe/dist/shared/pathe.ff20891b.mjs +1 -1
- package/dist/node_modules/.bun/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +1 -0
- package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/chunk-BVHSVHOK.mjs +1 -1
- package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +1 -0
- package/dist/node_modules/{tinyrainbow → .bun/tinyrainbow@1.2.0/node_modules/tinyrainbow}/dist/node.mjs +1 -1
- package/dist/node_modules/.bun/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.mjs.map +1 -0
- package/dist/node_modules/{tinyspy → .bun/tinyspy@3.0.2/node_modules/tinyspy}/dist/index.mjs +1 -1
- package/dist/node_modules/.bun/tinyspy@3.0.2/node_modules/tinyspy/dist/index.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs +1 -1
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/date.W2xKR2qe.mjs +1 -1
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/utils.C8RiOc4B.mjs +2 -2
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +1 -0
- package/dist/node_modules/{vitest → .bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest}/dist/chunks/vi.DgezovHB.mjs +11 -11
- package/dist/node_modules/.bun/vitest@2.1.9_7700f9e9ace41f23/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +1 -0
- package/dist/providers/base.d.mts +2 -2
- package/dist/providers/base.d.mts.map +1 -1
- package/dist/providers/openai-provider.d.mts.map +1 -1
- package/dist/providers/openai-provider.mjs +10 -9
- package/dist/providers/openai-provider.mjs.map +1 -1
- package/dist/providers/registry.d.mts +1 -1
- package/dist/providers/registry.d.mts.map +1 -1
- package/dist/providers/registry.mjs +99 -99
- package/dist/providers/registry.mjs.map +1 -1
- package/dist/session/manager.d.mts +2 -2
- package/dist/session/manager.d.mts.map +1 -1
- package/dist/session/manager.mjs +18 -19
- package/dist/session/manager.mjs.map +1 -1
- package/dist/utils/helpers.d.mts.map +1 -1
- package/dist/utils/helpers.mjs.map +1 -1
- package/package.json +11 -11
- package/skills/cron/SKILL.md +12 -8
- package/skills/daily-summary/SKILL.md +4 -0
- package/skills/english/SKILL.md +21 -7
- package/skills/expense/SKILL.md +11 -7
- package/skills/fortune/SKILL.md +24 -20
- package/skills/habit/SKILL.md +2 -1
- package/skills/hydration/SKILL.md +3 -0
- package/skills/memory/SKILL.md +1 -0
- package/skills/mood/SKILL.md +10 -6
- package/skills/skill-creator/SKILL.md +3 -0
- package/skills/summarize/SKILL.md +1 -0
- package/skills/weather/SKILL.md +10 -8
- package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +0 -1
- package/dist/node_modules/@vitest/expect/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/pretty-format/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/runner/dist/chunk-tasks.mjs.map +0 -1
- package/dist/node_modules/@vitest/runner/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/snapshot/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/snapshot/node_modules/pathe/dist/shared/pathe.ff20891b.mjs.map +0 -1
- package/dist/node_modules/@vitest/spy/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/diff.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/error.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/helpers.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/index.mjs.map +0 -1
- package/dist/node_modules/@vitest/utils/dist/source-map.mjs.map +0 -1
- package/dist/node_modules/chai/index.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/arguments.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/array.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/bigint.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/class.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/date.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/error.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/function.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/helpers.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/html.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/index.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/map.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/number.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/object.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/promise.mjs +0 -6
- package/dist/node_modules/loupe/lib/promise.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/regexp.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/set.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/string.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/symbol.mjs.map +0 -1
- package/dist/node_modules/loupe/lib/typedarray.mjs.map +0 -1
- package/dist/node_modules/magic-string/dist/magic-string.es.mjs.map +0 -1
- package/dist/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.mjs.map +0 -1
- package/dist/node_modules/tinyrainbow/dist/node.mjs.map +0 -1
- package/dist/node_modules/tinyspy/dist/index.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/date.W2xKR2qe.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/utils.C8RiOc4B.mjs.map +0 -1
- package/dist/node_modules/vitest/dist/chunks/vi.DgezovHB.mjs.map +0 -1
- /package/dist/node_modules/{@vitest → .bun/@vitest_runner@2.1.9/node_modules/@vitest}/runner/dist/utils.mjs +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.mjs","names":[],"sources":["../../../src/agent/tools/shell.ts"],"sourcesContent":["import { exec, spawn } from \"node:child_process\";\nimport { Tool } from \"./base.js\";\n\n/** Env var patterns that should never leak to user-invoked commands. */\nconst SENSITIVE_ENV_PATTERNS = [\n /api[_-]?key/i,\n /api[_-]?token/i,\n /api[_-]?secret/i,\n /secret/i,\n /token/i,\n /password/i,\n /credential/i,\n /^LLM_/i,\n /^OPENROUTER_/i,\n /^OPENAI_/i,\n /^ANTHROPIC_/i,\n /^STRIPE_/i,\n /^LINE_CHANNEL/i,\n /^APIFY_/i,\n /^Z_AI_/i,\n /^BRAVE_/i,\n /^AWS_/i,\n /^GOOGLE_/i,\n /^AZURE_/i,\n /^NANOBOT_/i,\n];\n\n/**\n * Trusted commands that receive skill-specific env vars.\n * Only the first word (binary name) of the command is matched.\n * These commands still do NOT get the core LLM/channel secrets.\n */\nconst TRUSTED_COMMANDS = [\"summarize\"];\n\n/** Env vars injected for trusted commands only. */\nconst SKILL_ENV_KEYS = [\
|
|
1
|
+
{"version":3,"file":"shell.mjs","names":[],"sources":["../../../src/agent/tools/shell.ts"],"sourcesContent":["import { exec, spawn } from \"node:child_process\";\n\nimport { Tool } from \"./base.js\";\n\n/** Env var patterns that should never leak to user-invoked commands. */\nconst SENSITIVE_ENV_PATTERNS = [\n /api[_-]?key/i,\n /api[_-]?token/i,\n /api[_-]?secret/i,\n /secret/i,\n /token/i,\n /password/i,\n /credential/i,\n /^LLM_/i,\n /^OPENROUTER_/i,\n /^OPENAI_/i,\n /^ANTHROPIC_/i,\n /^STRIPE_/i,\n /^LINE_CHANNEL/i,\n /^APIFY_/i,\n /^Z_AI_/i,\n /^BRAVE_/i,\n /^AWS_/i,\n /^GOOGLE_/i,\n /^AZURE_/i,\n /^NANOBOT_/i,\n];\n\n/**\n * Trusted commands that receive skill-specific env vars.\n * Only the first word (binary name) of the command is matched.\n * These commands still do NOT get the core LLM/channel secrets.\n */\nconst TRUSTED_COMMANDS = new Set([\"summarize\"]);\n\n/** Env vars injected for trusted commands only. */\nconst SKILL_ENV_KEYS = [\"LLM_API_KEY\", \"LLM_API_BASE\", \"APIFY_API_TOKEN\"];\n\n/** Build a sanitized env for child processes — strips all secrets. */\nfunction buildSafeEnv(): Record<string, string> {\n const safe: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (!value) continue;\n if (SENSITIVE_ENV_PATTERNS.some((re) => re.test(key))) continue;\n safe[key] = value;\n }\n return safe;\n}\n\n/** Build env for trusted skill commands — safe env + skill-specific keys. */\nfunction buildSkillEnv(): Record<string, string> {\n const env = buildSafeEnv();\n for (const key of SKILL_ENV_KEYS) {\n const value = process.env[key];\n if (value) env[key] = value;\n }\n return env;\n}\n\n/** Check if a command starts with a trusted binary. */\nfunction isTrustedCommand(command: string): boolean {\n // Extract the first word, stripping common prefixes like timeout, env, etc.\n const stripped = command.replace(\n /^\\s*(timeout\\s+\\d+\\s+|env\\s+\\S+=\\S+\\s+)*/,\n \"\"\n );\n const firstWord = stripped.trim().split(/\\s+/)[0];\n return TRUSTED_COMMANDS.has(firstWord);\n}\n\nexport class ExecTool extends Tool {\n readonly name = \"exec\";\n readonly description =\n \"Execute a shell command and return its output. Use for running programs, scripts, git, etc.\";\n readonly parameters = {\n properties: {\n command: { type: \"string\", description: \"Shell command to execute\" },\n timeout: {\n type: \"integer\",\n description: \"Timeout in seconds (default: 60)\",\n },\n },\n required: [\"command\"],\n type: \"object\",\n };\n\n private workingDir: string;\n private defaultTimeout: number;\n private restrictToWorkspace: boolean;\n\n constructor(params?: {\n workingDir?: string;\n timeout?: number;\n restrictToWorkspace?: boolean;\n }) {\n super();\n this.workingDir = params?.workingDir ?? process.cwd();\n this.defaultTimeout = params?.timeout ?? 60;\n this.restrictToWorkspace = params?.restrictToWorkspace ?? false;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const command = String(args.command);\n const timeout = args.timeout ? Number(args.timeout) : this.defaultTimeout;\n\n if (!command.trim()) {\n return \"Error: Empty command\";\n }\n\n // Safety checks when restricted to workspace\n if (this.restrictToWorkspace) {\n const dangerous = [\"rm -rf /\", \"mkfs\", \"dd if=\", \"> /dev/\"];\n for (const pattern of dangerous) {\n if (command.includes(pattern)) {\n return `Error: Command blocked for safety: ${pattern}`;\n }\n }\n }\n\n // Build env: scrub secrets when restricted, pass through when unrestricted\n let env: Record<string, string>;\n if (!this.restrictToWorkspace) {\n env = { ...process.env, HOME: process.env.HOME ?? \"\" } as Record<\n string,\n string\n >;\n } else if (isTrustedCommand(command)) {\n env = buildSkillEnv();\n // DEBUG: echo the key so we can verify it reaches the child process\n } else {\n env = buildSafeEnv();\n }\n\n return new Promise<string>((resolve) => {\n exec(\n command,\n {\n cwd: this.workingDir,\n timeout: timeout * 1000,\n maxBuffer: 1024 * 1024, // 1MB\n env,\n },\n (error, stdout, stderr) => {\n const parts: string[] = [];\n\n if (stdout) {\n parts.push(stdout);\n }\n if (stderr) {\n parts.push(`[stderr]\\n${stderr}`);\n }\n\n if (error) {\n if (error.killed) {\n parts.push(`\\n[Timed out after ${timeout}s]`);\n } else if (error.code !== undefined) {\n parts.push(`\\n[Exit code: ${error.code}]`);\n }\n }\n\n const output = parts.join(\"\\n\").trim();\n // Truncate large output\n if (output.length > 50_000) {\n resolve(\n output.slice(0, 50_000) + \"\\n... (truncated, output too large)\"\n );\n } else {\n resolve(output || \"(no output)\");\n }\n }\n );\n });\n }\n}\n"],"mappings":";;;;;AAKA,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAM,mBAAmB,IAAI,IAAI,CAAC,YAAY,CAAC;;AAG/C,MAAM,iBAAiB;CAAC;CAAe;CAAgB;CAAkB;;AAGzE,SAAS,eAAuC;CAC9C,MAAM,OAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,IAAI,EAAE;AACtD,MAAI,CAAC,MAAO;AACZ,MAAI,uBAAuB,MAAM,OAAO,GAAG,KAAK,IAAI,CAAC,CAAE;AACvD,OAAK,OAAO;;AAEd,QAAO;;;AAIT,SAAS,gBAAwC;CAC/C,MAAM,MAAM,cAAc;AAC1B,MAAK,MAAM,OAAO,gBAAgB;EAChC,MAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,MAAO,KAAI,OAAO;;AAExB,QAAO;;;AAIT,SAAS,iBAAiB,SAA0B;CAMlD,MAAM,YAJW,QAAQ,QACvB,4CACA,GACD,CAC0B,MAAM,CAAC,MAAM,MAAM,CAAC;AAC/C,QAAO,iBAAiB,IAAI,UAAU;;AAGxC,IAAa,WAAb,cAA8B,KAAK;CACjC,AAAS,OAAO;CAChB,AAAS,cACP;CACF,AAAS,aAAa;EACpB,YAAY;GACV,SAAS;IAAE,MAAM;IAAU,aAAa;IAA4B;GACpE,SAAS;IACP,MAAM;IACN,aAAa;IACd;GACF;EACD,UAAU,CAAC,UAAU;EACrB,MAAM;EACP;CAED,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,QAIT;AACD,SAAO;AACP,OAAK,aAAa,QAAQ,cAAc,QAAQ,KAAK;AACrD,OAAK,iBAAiB,QAAQ,WAAW;AACzC,OAAK,sBAAsB,QAAQ,uBAAuB;;CAG5D,MAAM,QAAQ,MAAgD;EAC5D,MAAM,UAAU,OAAO,KAAK,QAAQ;EACpC,MAAM,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ,GAAG,KAAK;AAE3D,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO;AAIT,MAAI,KAAK,qBAEP;QAAK,MAAM,WADO;IAAC;IAAY;IAAQ;IAAU;IAAU,CAEzD,KAAI,QAAQ,SAAS,QAAQ,CAC3B,QAAO,sCAAsC;;EAMnD,IAAI;AACJ,MAAI,CAAC,KAAK,oBACR,OAAM;GAAE,GAAG,QAAQ;GAAK,MAAM,QAAQ,IAAI,QAAQ;GAAI;WAI7C,iBAAiB,QAAQ,CAClC,OAAM,eAAe;MAGrB,OAAM,cAAc;AAGtB,SAAO,IAAI,SAAiB,YAAY;AACtC,QACE,SACA;IACE,KAAK,KAAK;IACV,SAAS,UAAU;IACnB,WAAW,OAAO;IAClB;IACD,GACA,OAAO,QAAQ,WAAW;IACzB,MAAM,QAAkB,EAAE;AAE1B,QAAI,OACF,OAAM,KAAK,OAAO;AAEpB,QAAI,OACF,OAAM,KAAK,aAAa,SAAS;AAGnC,QAAI,OACF;SAAI,MAAM,OACR,OAAM,KAAK,sBAAsB,QAAQ,IAAI;cACpC,MAAM,SAAS,OACxB,OAAM,KAAK,iBAAiB,MAAM,KAAK,GAAG;;IAI9C,MAAM,SAAS,MAAM,KAAK,KAAK,CAAC,MAAM;AAEtC,QAAI,OAAO,SAAS,IAClB,SACE,OAAO,MAAM,GAAG,IAAO,GAAG,sCAC3B;QAED,SAAQ,UAAU,cAAc;KAGrC;IACD"}
|
|
@@ -9,7 +9,6 @@ declare class SpawnTool extends Tool {
|
|
|
9
9
|
readonly name = "spawn";
|
|
10
10
|
readonly description: string;
|
|
11
11
|
readonly parameters: {
|
|
12
|
-
type: string;
|
|
13
12
|
properties: {
|
|
14
13
|
task: {
|
|
15
14
|
type: string;
|
|
@@ -21,6 +20,7 @@ declare class SpawnTool extends Tool {
|
|
|
21
20
|
};
|
|
22
21
|
};
|
|
23
22
|
required: string[];
|
|
23
|
+
type: string;
|
|
24
24
|
};
|
|
25
25
|
private manager;
|
|
26
26
|
private originChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn.d.mts","names":[],"sources":["../../../src/agent/tools/spawn.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"spawn.d.mts","names":[],"sources":["../../../src/agent/tools/spawn.ts"],"mappings":";;;;;;AAOA;cAAa,SAAA,SAAkB,IAAA;EAAA,SACpB,IAAA;EAAA,SACA,WAAA;EAAA,SAIA,UAAA;;;;;;;;;;;;;;UAeD,OAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;cAEI,OAAA,EAAS,eAAA;EAJb;EAUR,UAAA,CAAW,OAAA,UAAiB,MAAA;EAKtB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA"}
|
|
@@ -8,7 +8,6 @@ var SpawnTool = class extends Tool {
|
|
|
8
8
|
name = "spawn";
|
|
9
9
|
description = "Spawn a subagent to handle a task in the background. Use this for complex or time-consuming tasks that can run independently. The subagent will complete the task and report back when done.";
|
|
10
10
|
parameters = {
|
|
11
|
-
type: "object",
|
|
12
11
|
properties: {
|
|
13
12
|
task: {
|
|
14
13
|
type: "string",
|
|
@@ -19,7 +18,8 @@ var SpawnTool = class extends Tool {
|
|
|
19
18
|
description: "Optional short label for the task (for display)"
|
|
20
19
|
}
|
|
21
20
|
},
|
|
22
|
-
required: ["task"]
|
|
21
|
+
required: ["task"],
|
|
22
|
+
type: "object"
|
|
23
23
|
};
|
|
24
24
|
manager;
|
|
25
25
|
originChannel = "cli";
|
|
@@ -37,10 +37,10 @@ var SpawnTool = class extends Tool {
|
|
|
37
37
|
const task = String(args.task);
|
|
38
38
|
const label = args.label ? String(args.label) : void 0;
|
|
39
39
|
return this.manager.spawn({
|
|
40
|
-
task,
|
|
41
40
|
label,
|
|
42
41
|
originChannel: this.originChannel,
|
|
43
|
-
originChatId: this.originChatId
|
|
42
|
+
originChatId: this.originChatId,
|
|
43
|
+
task
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn.mjs","names":[],"sources":["../../../src/agent/tools/spawn.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"spawn.mjs","names":[],"sources":["../../../src/agent/tools/spawn.ts"],"sourcesContent":["import type { SubagentManager } from \"../subagent.js\";\n\nimport { Tool } from \"./base.js\";\n\n/**\n * Tool to spawn a subagent for background task execution.\n */\nexport class SpawnTool extends Tool {\n readonly name = \"spawn\";\n readonly description =\n \"Spawn a subagent to handle a task in the background. \" +\n \"Use this for complex or time-consuming tasks that can run independently. \" +\n \"The subagent will complete the task and report back when done.\";\n readonly parameters = {\n properties: {\n task: {\n type: \"string\",\n description: \"The task for the subagent to complete\",\n },\n label: {\n type: \"string\",\n description: \"Optional short label for the task (for display)\",\n },\n },\n required: [\"task\"],\n type: \"object\",\n };\n\n private manager: SubagentManager;\n private originChannel = \"cli\";\n private originChatId = \"direct\";\n\n constructor(manager: SubagentManager) {\n super();\n this.manager = manager;\n }\n\n /** Set the origin context for subagent announcements. */\n setContext(channel: string, chatId: string): void {\n this.originChannel = channel;\n this.originChatId = chatId;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const task = String(args.task);\n const label = args.label ? String(args.label) : undefined;\n return this.manager.spawn({\n label,\n originChannel: this.originChannel,\n originChatId: this.originChatId,\n task,\n });\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,YAAb,cAA+B,KAAK;CAClC,AAAS,OAAO;CAChB,AAAS,cACP;CAGF,AAAS,aAAa;EACpB,YAAY;GACV,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,OAAO;IACL,MAAM;IACN,aAAa;IACd;GACF;EACD,UAAU,CAAC,OAAO;EAClB,MAAM;EACP;CAED,AAAQ;CACR,AAAQ,gBAAgB;CACxB,AAAQ,eAAe;CAEvB,YAAY,SAA0B;AACpC,SAAO;AACP,OAAK,UAAU;;;CAIjB,WAAW,SAAiB,QAAsB;AAChD,OAAK,gBAAgB;AACrB,OAAK,eAAe;;CAGtB,MAAM,QAAQ,MAAgD;EAC5D,MAAM,OAAO,OAAO,KAAK,KAAK;EAC9B,MAAM,QAAQ,KAAK,QAAQ,OAAO,KAAK,MAAM,GAAG;AAChD,SAAO,KAAK,QAAQ,MAAM;GACxB;GACA,eAAe,KAAK;GACpB,cAAc,KAAK;GACnB;GACD,CAAC"}
|
|
@@ -6,7 +6,6 @@ declare class WebSearchTool extends Tool {
|
|
|
6
6
|
readonly name = "web_search";
|
|
7
7
|
readonly description = "Search the web. Returns titles, URLs, and snippets.";
|
|
8
8
|
readonly parameters: {
|
|
9
|
-
type: string;
|
|
10
9
|
properties: {
|
|
11
10
|
query: {
|
|
12
11
|
type: string;
|
|
@@ -20,6 +19,7 @@ declare class WebSearchTool extends Tool {
|
|
|
20
19
|
};
|
|
21
20
|
};
|
|
22
21
|
required: string[];
|
|
22
|
+
type: string;
|
|
23
23
|
};
|
|
24
24
|
private apiKey;
|
|
25
25
|
private maxResults;
|
|
@@ -34,7 +34,6 @@ declare class WebFetchTool extends Tool {
|
|
|
34
34
|
readonly name = "web_fetch";
|
|
35
35
|
readonly description = "Fetch URL and extract readable content (HTML -> markdown/text).";
|
|
36
36
|
readonly parameters: {
|
|
37
|
-
type: string;
|
|
38
37
|
properties: {
|
|
39
38
|
url: {
|
|
40
39
|
type: string;
|
|
@@ -51,6 +50,7 @@ declare class WebFetchTool extends Tool {
|
|
|
51
50
|
};
|
|
52
51
|
};
|
|
53
52
|
required: string[];
|
|
53
|
+
type: string;
|
|
54
54
|
};
|
|
55
55
|
private defaultMaxChars;
|
|
56
56
|
constructor(params?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.mts","names":[],"sources":["../../../src/agent/tools/web.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"web.d.mts","names":[],"sources":["../../../src/agent/tools/web.ts"],"mappings":";;;;cA8Oa,aAAA,SAAsB,IAAA;EAAA,SACxB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;;;;;;;UAcD,MAAA;EAAA,QACA,UAAA;cAEI,MAAA;IAAW,MAAA;IAAiB,UAAA;EAAA;EAMlC,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cA4DnC,YAAA,SAAqB,IAAA;EAAA,SACvB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;UAcD,eAAA;cAEI,MAAA;IAAW,QAAA;EAAA;EAKjB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA"}
|
package/dist/agent/tools/web.mjs
CHANGED
|
@@ -78,55 +78,55 @@ async function resolvesToPrivateIP(hostname) {
|
|
|
78
78
|
/** Strip HTML tags and decode entities. */
|
|
79
79
|
function stripTags(text) {
|
|
80
80
|
let result = text;
|
|
81
|
-
result = result.
|
|
82
|
-
result = result.
|
|
83
|
-
result = result.
|
|
84
|
-
result = result.
|
|
81
|
+
result = result.replaceAll(/<script[\s\S]*?<\/script>/gi, "");
|
|
82
|
+
result = result.replaceAll(/<style[\s\S]*?<\/style>/gi, "");
|
|
83
|
+
result = result.replaceAll(/<[^>]+>/g, "");
|
|
84
|
+
result = result.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", "\"").replaceAll("'", "'").replaceAll(" ", " ");
|
|
85
85
|
return result.trim();
|
|
86
86
|
}
|
|
87
87
|
/** Normalize whitespace. */
|
|
88
88
|
function normalize(text) {
|
|
89
|
-
return text.
|
|
89
|
+
return text.replaceAll(/[ \t]+/g, " ").replaceAll(/\n{3,}/g, "\n\n").trim();
|
|
90
90
|
}
|
|
91
91
|
/** Validate URL (protocol, hostname, SSRF protection). */
|
|
92
92
|
async function validateUrl(url) {
|
|
93
93
|
try {
|
|
94
94
|
const parsed = new URL(url);
|
|
95
95
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return {
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
error: `Only http/https allowed, got '${parsed.protocol}'`,
|
|
97
|
+
valid: false
|
|
98
98
|
};
|
|
99
99
|
if (!parsed.hostname) return {
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
error: "Missing domain",
|
|
101
|
+
valid: false
|
|
102
102
|
};
|
|
103
103
|
if (parsed.username || parsed.password) return {
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
error: "URLs with credentials are not allowed",
|
|
105
|
+
valid: false
|
|
106
106
|
};
|
|
107
107
|
if (await resolvesToPrivateIP(parsed.hostname)) return {
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
error: `Access to private/internal addresses is blocked (${parsed.hostname})`,
|
|
109
|
+
valid: false
|
|
110
110
|
};
|
|
111
111
|
return {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
error: "",
|
|
113
|
+
valid: true
|
|
114
114
|
};
|
|
115
|
-
} catch (
|
|
115
|
+
} catch (error) {
|
|
116
116
|
return {
|
|
117
117
|
valid: false,
|
|
118
|
-
error: String(
|
|
118
|
+
error: String(error)
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
/** Convert HTML to basic markdown. */
|
|
123
123
|
function htmlToMarkdown(html) {
|
|
124
124
|
let text = html;
|
|
125
|
-
text = text.
|
|
126
|
-
text = text.
|
|
127
|
-
text = text.
|
|
128
|
-
text = text.
|
|
129
|
-
text = text.
|
|
125
|
+
text = text.replaceAll(/<a\s+[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_m, url, inner) => `[${stripTags(inner)}](${url})`);
|
|
126
|
+
text = text.replaceAll(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi, (_m, level, inner) => `\n${"#".repeat(Number(level))} ${stripTags(inner)}\n`);
|
|
127
|
+
text = text.replaceAll(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_m, inner) => `\n- ${stripTags(inner)}`);
|
|
128
|
+
text = text.replaceAll(/<\/(p|div|section|article)>/gi, "\n\n");
|
|
129
|
+
text = text.replaceAll(/<(br|hr)\s*\/?>/gi, "\n");
|
|
130
130
|
return normalize(stripTags(text));
|
|
131
131
|
}
|
|
132
132
|
/** Search the web using Brave Search API. */
|
|
@@ -134,7 +134,6 @@ var WebSearchTool = class extends Tool {
|
|
|
134
134
|
name = "web_search";
|
|
135
135
|
description = "Search the web. Returns titles, URLs, and snippets.";
|
|
136
136
|
parameters = {
|
|
137
|
-
type: "object",
|
|
138
137
|
properties: {
|
|
139
138
|
query: {
|
|
140
139
|
type: "string",
|
|
@@ -147,7 +146,8 @@ var WebSearchTool = class extends Tool {
|
|
|
147
146
|
maximum: 10
|
|
148
147
|
}
|
|
149
148
|
},
|
|
150
|
-
required: ["query"]
|
|
149
|
+
required: ["query"],
|
|
150
|
+
type: "object"
|
|
151
151
|
};
|
|
152
152
|
apiKey;
|
|
153
153
|
maxResults;
|
|
@@ -176,14 +176,14 @@ var WebSearchTool = class extends Tool {
|
|
|
176
176
|
const results = (await resp.json()).web?.results ?? [];
|
|
177
177
|
if (results.length === 0) return `No results for: ${query}`;
|
|
178
178
|
const lines = [`Results for: ${query}\n`];
|
|
179
|
-
for (let i = 0; i < Math.min(results.length, count); i
|
|
179
|
+
for (let i = 0; i < Math.min(results.length, count); i += 1) {
|
|
180
180
|
const item = results[i];
|
|
181
181
|
lines.push(`${i + 1}. ${item.title ?? ""}\n ${item.url ?? ""}`);
|
|
182
182
|
if (item.description) lines.push(` ${item.description}`);
|
|
183
183
|
}
|
|
184
184
|
return lines.join("\n");
|
|
185
|
-
} catch (
|
|
186
|
-
return `Error: ${
|
|
185
|
+
} catch (error) {
|
|
186
|
+
return `Error: ${error instanceof Error ? error.message : error}`;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
};
|
|
@@ -192,7 +192,6 @@ var WebFetchTool = class extends Tool {
|
|
|
192
192
|
name = "web_fetch";
|
|
193
193
|
description = "Fetch URL and extract readable content (HTML -> markdown/text).";
|
|
194
194
|
parameters = {
|
|
195
|
-
type: "object",
|
|
196
195
|
properties: {
|
|
197
196
|
url: {
|
|
198
197
|
type: "string",
|
|
@@ -208,7 +207,8 @@ var WebFetchTool = class extends Tool {
|
|
|
208
207
|
minimum: 100
|
|
209
208
|
}
|
|
210
209
|
},
|
|
211
|
-
required: ["url"]
|
|
210
|
+
required: ["url"],
|
|
211
|
+
type: "object"
|
|
212
212
|
};
|
|
213
213
|
defaultMaxChars;
|
|
214
214
|
constructor(params) {
|
|
@@ -245,7 +245,7 @@ var WebFetchTool = class extends Tool {
|
|
|
245
245
|
redirect: "manual",
|
|
246
246
|
signal: AbortSignal.timeout(3e4)
|
|
247
247
|
});
|
|
248
|
-
redirectCount
|
|
248
|
+
redirectCount += 1;
|
|
249
249
|
}
|
|
250
250
|
if (redirectCount >= maxRedirects) return JSON.stringify({
|
|
251
251
|
error: "Too many redirects",
|
|
@@ -276,17 +276,17 @@ var WebFetchTool = class extends Tool {
|
|
|
276
276
|
const truncated = text.length > maxChars;
|
|
277
277
|
if (truncated) text = text.slice(0, maxChars);
|
|
278
278
|
return JSON.stringify({
|
|
279
|
-
|
|
279
|
+
extractor,
|
|
280
280
|
finalUrl: finalResp.url,
|
|
281
|
+
length: text.length,
|
|
281
282
|
status: finalResp.status,
|
|
282
|
-
|
|
283
|
+
text,
|
|
283
284
|
truncated,
|
|
284
|
-
|
|
285
|
-
text
|
|
285
|
+
url
|
|
286
286
|
});
|
|
287
|
-
} catch (
|
|
287
|
+
} catch (error) {
|
|
288
288
|
return JSON.stringify({
|
|
289
|
-
error:
|
|
289
|
+
error: error instanceof Error ? error.message : String(error),
|
|
290
290
|
url
|
|
291
291
|
});
|
|
292
292
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.mjs","names":[],"sources":["../../../src/agent/tools/web.ts"],"sourcesContent":["import { Tool } from \"./base.js\";\nimport { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nconst USER_AGENT =\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_2) AppleWebKit/537.36\";\n\n/** Blocked hostnames that could resolve to private IPs or allow SSRF. */\nconst BLOCKED_HOSTNAMES = new Set([\n \"localhost\",\n \"localhost.localdomain\",\n \"ip6-localhost\",\n \"ip6-loopback\",\n \"loopback\",\n \"broadcasthost\",\n \"ip6-localnet\",\n \"ip6-mcastprefix\",\n \"0.0.0.0\",\n \"::1\",\n \"::\",\n \"metadata.google.internal\",\n \"metadata\",\n \"kubernetes.default\",\n \"kubernetes.default.svc\",\n \"169.254.169.254\", // AWS/GCP/Azure metadata (also covered by link-local check)\n]);\n\n/** Check if an IP address is private/internal (SSRF protection). */\nfunction isPrivateIP(ip: string): boolean {\n const ipVersion = isIP(ip);\n if (ipVersion === 0) return true;\n\n if (ipVersion === 4) {\n const parts = ip.split(\".\").map(Number);\n const [a, b] = parts;\n\n if (Number.isNaN(a) || Number.isNaN(b)) return true;\n\n if (a === 0) return true;\n if (a === 10) return true;\n if (a === 100 && b >= 64 && b <= 127) return true; // Carrier-grade NAT (RFC 6598)\n if (a === 127) return true;\n if (a === 169 && b === 254) return true;\n if (a === 172 && b >= 16 && b <= 31) return true;\n if (a === 192 && b === 0 && parts[2] === 0) return true;\n if (a === 192 && b === 0 && parts[2] === 2) return true;\n if (a === 192 && b === 88 && parts[2] === 99) return true;\n if (a === 192 && b === 168) return true;\n if (a === 198 && b >= 18 && b <= 19) return true;\n if (a === 198 && b === 51 && parts[2] === 100) return true;\n if (a === 203 && b === 0 && parts[2] === 113) return true;\n if (a >= 224 && a <= 239) return true;\n if (a >= 240) return true;\n if (a === 255 && b === 255 && parts[2] === 255 && parts[3] === 255) return true;\n\n return false;\n }\n\n if (ipVersion === 6) {\n const lower = ip.toLowerCase();\n if (lower === \"::1\") return true;\n if (lower.startsWith(\"::ffff:\")) return true;\n if (lower.startsWith(\"fe80::\")) return true;\n if (lower.startsWith(\"fc\") || lower.startsWith(\"fd\")) return true;\n if (lower.startsWith(\"ff\")) return true;\n if (lower === \"::\" || lower === \"0:0:0:0:0:0:0:0\") return true;\n return false;\n }\n\n return true;\n}\n\n/** Resolve hostname and check if it resolves to a private IP. */\nasync function resolvesToPrivateIP(hostname: string): Promise<boolean> {\n if (BLOCKED_HOSTNAMES.has(hostname.toLowerCase())) {\n return true;\n }\n\n if (isIP(hostname) !== 0) {\n return isPrivateIP(hostname);\n }\n\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".localhost\")) {\n return true;\n }\n\n if (hostname.endsWith(\".internal\") || hostname.endsWith(\".svc.cluster.local\")) {\n return true;\n }\n\n try {\n const result = await lookup(hostname, { all: true });\n for (const addr of result) {\n if (isPrivateIP(addr.address)) {\n return true;\n }\n }\n return false;\n } catch {\n return true;\n }\n}\n\n/** Strip HTML tags and decode entities. */\nfunction stripTags(text: string): string {\n let result = text;\n result = result.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\");\n result = result.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\");\n result = result.replace(/<[^>]+>/g, \"\");\n // Decode common HTML entities\n result = result\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, \" \");\n return result.trim();\n}\n\n/** Normalize whitespace. */\nfunction normalize(text: string): string {\n return text\n .replace(/[ \\t]+/g, \" \")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** Validate URL (protocol, hostname, SSRF protection). */\nasync function validateUrl(url: string): Promise<{ valid: boolean; error: string }> {\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return {\n valid: false,\n error: `Only http/https allowed, got '${parsed.protocol}'`,\n };\n }\n if (!parsed.hostname) {\n return { valid: false, error: \"Missing domain\" };\n }\n\n if (parsed.username || parsed.password) {\n return { valid: false, error: \"URLs with credentials are not allowed\" };\n }\n\n if (await resolvesToPrivateIP(parsed.hostname)) {\n return {\n valid: false,\n error: `Access to private/internal addresses is blocked (${parsed.hostname})`,\n };\n }\n\n return { valid: true, error: \"\" };\n } catch (err) {\n return { valid: false, error: String(err) };\n }\n}\n\n/** Convert HTML to basic markdown. */\nfunction htmlToMarkdown(html: string): string {\n let text = html;\n // Links\n text = text.replace(\n /<a\\s+[^>]*href=[\"']([^\"']+)[\"'][^>]*>([\\s\\S]*?)<\\/a>/gi,\n (_m, url, inner) => `[${stripTags(inner)}](${url})`,\n );\n // Headings\n text = text.replace(\n /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi,\n (_m, level, inner) => `\\n${\"#\".repeat(Number(level))} ${stripTags(inner)}\\n`,\n );\n // List items\n text = text.replace(\n /<li[^>]*>([\\s\\S]*?)<\\/li>/gi,\n (_m, inner) => `\\n- ${stripTags(inner)}`,\n );\n // Block elements\n text = text.replace(/<\\/(p|div|section|article)>/gi, \"\\n\\n\");\n text = text.replace(/<(br|hr)\\s*\\/?>/gi, \"\\n\");\n return normalize(stripTags(text));\n}\n\n/** Search the web using Brave Search API. */\nexport class WebSearchTool extends Tool {\n readonly name = \"web_search\";\n readonly description = \"Search the web. Returns titles, URLs, and snippets.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n count: {\n type: \"integer\",\n description: \"Results (1-10)\",\n minimum: 1,\n maximum: 10,\n },\n },\n required: [\"query\"],\n };\n\n private apiKey: string;\n private maxResults: number;\n\n constructor(params?: { apiKey?: string; maxResults?: number }) {\n super();\n this.apiKey = params?.apiKey ?? process.env.BRAVE_API_KEY ?? \"\";\n this.maxResults = params?.maxResults ?? 5;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const query = String(args.query);\n const count = Math.min(\n Math.max(args.count ? Number(args.count) : this.maxResults, 1),\n 10,\n );\n\n if (!this.apiKey) {\n return \"Error: BRAVE_API_KEY not configured\";\n }\n\n try {\n const url = new URL(\"https://api.search.brave.com/res/v1/web/search\");\n url.searchParams.set(\"q\", query);\n url.searchParams.set(\"count\", String(count));\n\n const resp = await fetch(url.toString(), {\n headers: {\n Accept: \"application/json\",\n \"Accept-Encoding\": \"gzip\",\n \"X-Subscription-Token\": this.apiKey,\n },\n signal: AbortSignal.timeout(10000),\n });\n\n if (!resp.ok) {\n return `Error: Search API returned ${resp.status}`;\n }\n\n const data = (await resp.json()) as {\n web?: { results?: Array<{ title?: string; url?: string; description?: string }> };\n };\n const results = data.web?.results ?? [];\n\n if (results.length === 0) {\n return `No results for: ${query}`;\n }\n\n const lines = [`Results for: ${query}\\n`];\n for (let i = 0; i < Math.min(results.length, count); i++) {\n const item = results[i];\n lines.push(`${i + 1}. ${item.title ?? \"\"}\\n ${item.url ?? \"\"}`);\n if (item.description) {\n lines.push(` ${item.description}`);\n }\n }\n return lines.join(\"\\n\");\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** Fetch and extract content from a URL. */\nexport class WebFetchTool extends Tool {\n readonly name = \"web_fetch\";\n readonly description =\n \"Fetch URL and extract readable content (HTML -> markdown/text).\";\n readonly parameters = {\n type: \"object\",\n properties: {\n url: { type: \"string\", description: \"URL to fetch\" },\n extractMode: {\n type: \"string\",\n enum: [\"markdown\", \"text\"],\n description: \"Extract mode\",\n },\n maxChars: { type: \"integer\", minimum: 100 },\n },\n required: [\"url\"],\n };\n\n private defaultMaxChars: number;\n\n constructor(params?: { maxChars?: number }) {\n super();\n this.defaultMaxChars = params?.maxChars ?? 50000;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const url = String(args.url);\n const extractMode = String(args.extractMode ?? \"markdown\");\n const maxChars = args.maxChars\n ? Number(args.maxChars)\n : this.defaultMaxChars;\n\n const { valid, error: validationError } = await validateUrl(url);\n if (!valid) {\n return JSON.stringify({\n error: `URL validation failed: ${validationError}`,\n url,\n });\n }\n\n try {\n let finalResp = await fetch(url, {\n headers: { \"User-Agent\": USER_AGENT },\n redirect: \"manual\",\n signal: AbortSignal.timeout(30000),\n });\n\n let redirectCount = 0;\n const maxRedirects = 5;\n\n while (finalResp.status >= 300 && finalResp.status < 400 && redirectCount < maxRedirects) {\n const location = finalResp.headers.get(\"location\");\n if (!location) break;\n\n const { valid: redirectValid } = await validateUrl(location);\n if (!redirectValid) {\n return JSON.stringify({\n error: `Redirect to blocked address: ${location}`,\n url,\n });\n }\n\n finalResp = await fetch(location, {\n headers: { \"User-Agent\": USER_AGENT },\n redirect: \"manual\",\n signal: AbortSignal.timeout(30000),\n });\n redirectCount++;\n }\n\n if (redirectCount >= maxRedirects) {\n return JSON.stringify({\n error: \"Too many redirects\",\n url,\n });\n }\n\n if (!finalResp.ok) {\n return JSON.stringify({\n error: `HTTP ${finalResp.status}`,\n url,\n });\n }\n\n const contentType = finalResp.headers.get(\"content-type\") ?? \"\";\n const body = await finalResp.text();\n let text: string;\n let extractor: string;\n\n if (contentType.includes(\"application/json\")) {\n try {\n text = JSON.stringify(JSON.parse(body), null, 2);\n } catch {\n text = body;\n }\n extractor = \"json\";\n } else if (\n contentType.includes(\"text/html\") ||\n body.slice(0, 256).toLowerCase().startsWith(\"<!doctype\") ||\n body.slice(0, 256).toLowerCase().startsWith(\"<html\")\n ) {\n // Extract readable content from HTML\n text =\n extractMode === \"markdown\"\n ? htmlToMarkdown(body)\n : stripTags(body);\n extractor = \"html\";\n } else {\n text = body;\n extractor = \"raw\";\n }\n\n const truncated = text.length > maxChars;\n if (truncated) {\n text = text.slice(0, maxChars);\n }\n\n return JSON.stringify({\n url,\n finalUrl: finalResp.url,\n status: finalResp.status,\n extractor,\n truncated,\n length: text.length,\n text,\n });\n } catch (err) {\n return JSON.stringify({\n error: err instanceof Error ? err.message : String(err),\n url,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"web.mjs","names":[],"sources":["../../../src/agent/tools/web.ts"],"sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nimport { Tool } from \"./base.js\";\n\nconst USER_AGENT =\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_2) AppleWebKit/537.36\";\n\n/** Blocked hostnames that could resolve to private IPs or allow SSRF. */\nconst BLOCKED_HOSTNAMES = new Set([\n \"localhost\",\n \"localhost.localdomain\",\n \"ip6-localhost\",\n \"ip6-loopback\",\n \"loopback\",\n \"broadcasthost\",\n \"ip6-localnet\",\n \"ip6-mcastprefix\",\n \"0.0.0.0\",\n \"::1\",\n \"::\",\n \"metadata.google.internal\",\n \"metadata\",\n \"kubernetes.default\",\n \"kubernetes.default.svc\",\n \"169.254.169.254\", // AWS/GCP/Azure metadata (also covered by link-local check)\n]);\n\n/** Check if an IP address is private/internal (SSRF protection). */\nfunction isPrivateIP(ip: string): boolean {\n const ipVersion = isIP(ip);\n if (ipVersion === 0) {\n return true;\n }\n\n if (ipVersion === 4) {\n const parts = ip.split(\".\").map(Number);\n const [a, b] = parts;\n\n if (Number.isNaN(a) || Number.isNaN(b)) {\n return true;\n }\n\n if (a === 0) {\n return true;\n }\n if (a === 10) {\n return true;\n }\n if (a === 100 && b >= 64 && b <= 127) {\n return true;\n } // Carrier-grade NAT (RFC 6598)\n if (a === 127) {\n return true;\n }\n if (a === 169 && b === 254) {\n return true;\n }\n if (a === 172 && b >= 16 && b <= 31) {\n return true;\n }\n if (a === 192 && b === 0 && parts[2] === 0) {\n return true;\n }\n if (a === 192 && b === 0 && parts[2] === 2) {\n return true;\n }\n if (a === 192 && b === 88 && parts[2] === 99) {\n return true;\n }\n if (a === 192 && b === 168) {\n return true;\n }\n if (a === 198 && b >= 18 && b <= 19) {\n return true;\n }\n if (a === 198 && b === 51 && parts[2] === 100) {\n return true;\n }\n if (a === 203 && b === 0 && parts[2] === 113) {\n return true;\n }\n if (a >= 224 && a <= 239) {\n return true;\n }\n if (a >= 240) {\n return true;\n }\n if (a === 255 && b === 255 && parts[2] === 255 && parts[3] === 255) {\n return true;\n }\n\n return false;\n }\n\n if (ipVersion === 6) {\n const lower = ip.toLowerCase();\n if (lower === \"::1\") {\n return true;\n }\n if (lower.startsWith(\"::ffff:\")) {\n return true;\n }\n if (lower.startsWith(\"fe80::\")) {\n return true;\n }\n if (lower.startsWith(\"fc\") || lower.startsWith(\"fd\")) {\n return true;\n }\n if (lower.startsWith(\"ff\")) {\n return true;\n }\n if (lower === \"::\" || lower === \"0:0:0:0:0:0:0:0\") {\n return true;\n }\n return false;\n }\n\n return true;\n}\n\n/** Resolve hostname and check if it resolves to a private IP. */\nasync function resolvesToPrivateIP(hostname: string): Promise<boolean> {\n if (BLOCKED_HOSTNAMES.has(hostname.toLowerCase())) {\n return true;\n }\n\n if (isIP(hostname) !== 0) {\n return isPrivateIP(hostname);\n }\n\n if (hostname.endsWith(\".local\") || hostname.endsWith(\".localhost\")) {\n return true;\n }\n\n if (\n hostname.endsWith(\".internal\") ||\n hostname.endsWith(\".svc.cluster.local\")\n ) {\n return true;\n }\n\n try {\n const result = await lookup(hostname, { all: true });\n for (const addr of result) {\n if (isPrivateIP(addr.address)) {\n return true;\n }\n }\n return false;\n } catch {\n return true;\n }\n}\n\n/** Strip HTML tags and decode entities. */\nfunction stripTags(text: string): string {\n let result = text;\n result = result.replaceAll(/<script[\\s\\S]*?<\\/script>/gi, \"\");\n result = result.replaceAll(/<style[\\s\\S]*?<\\/style>/gi, \"\");\n result = result.replaceAll(/<[^>]+>/g, \"\");\n // Decode common HTML entities\n result = result\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\""\", '\"')\n .replaceAll(\"'\", \"'\")\n .replaceAll(\" \", \" \");\n return result.trim();\n}\n\n/** Normalize whitespace. */\nfunction normalize(text: string): string {\n return text\n .replaceAll(/[ \\t]+/g, \" \")\n .replaceAll(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** Validate URL (protocol, hostname, SSRF protection). */\nasync function validateUrl(\n url: string\n): Promise<{ valid: boolean; error: string }> {\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return {\n error: `Only http/https allowed, got '${parsed.protocol}'`,\n valid: false,\n };\n }\n if (!parsed.hostname) {\n return { error: \"Missing domain\", valid: false };\n }\n\n if (parsed.username || parsed.password) {\n return { error: \"URLs with credentials are not allowed\", valid: false };\n }\n\n if (await resolvesToPrivateIP(parsed.hostname)) {\n return {\n error: `Access to private/internal addresses is blocked (${parsed.hostname})`,\n valid: false,\n };\n }\n\n return { error: \"\", valid: true };\n } catch (error) {\n return { valid: false, error: String(error) };\n }\n}\n\n/** Convert HTML to basic markdown. */\nfunction htmlToMarkdown(html: string): string {\n let text = html;\n // Links\n text = text.replaceAll(\n /<a\\s+[^>]*href=[\"']([^\"']+)[\"'][^>]*>([\\s\\S]*?)<\\/a>/gi,\n (_m, url, inner) => `[${stripTags(inner)}](${url})`\n );\n // Headings\n text = text.replaceAll(\n /<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi,\n (_m, level, inner) => `\\n${\"#\".repeat(Number(level))} ${stripTags(inner)}\\n`\n );\n // List items\n text = text.replaceAll(\n /<li[^>]*>([\\s\\S]*?)<\\/li>/gi,\n (_m, inner) => `\\n- ${stripTags(inner)}`\n );\n // Block elements\n text = text.replaceAll(/<\\/(p|div|section|article)>/gi, \"\\n\\n\");\n text = text.replaceAll(/<(br|hr)\\s*\\/?>/gi, \"\\n\");\n return normalize(stripTags(text));\n}\n\n/** Search the web using Brave Search API. */\nexport class WebSearchTool extends Tool {\n readonly name = \"web_search\";\n readonly description = \"Search the web. Returns titles, URLs, and snippets.\";\n readonly parameters = {\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n count: {\n type: \"integer\",\n description: \"Results (1-10)\",\n minimum: 1,\n maximum: 10,\n },\n },\n required: [\"query\"],\n type: \"object\",\n };\n\n private apiKey: string;\n private maxResults: number;\n\n constructor(params?: { apiKey?: string; maxResults?: number }) {\n super();\n this.apiKey = params?.apiKey ?? process.env.BRAVE_API_KEY ?? \"\";\n this.maxResults = params?.maxResults ?? 5;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const query = String(args.query);\n const count = Math.min(\n Math.max(args.count ? Number(args.count) : this.maxResults, 1),\n 10\n );\n\n if (!this.apiKey) {\n return \"Error: BRAVE_API_KEY not configured\";\n }\n\n try {\n const url = new URL(\"https://api.search.brave.com/res/v1/web/search\");\n url.searchParams.set(\"q\", query);\n url.searchParams.set(\"count\", String(count));\n\n const resp = await fetch(url.toString(), {\n headers: {\n Accept: \"application/json\",\n \"Accept-Encoding\": \"gzip\",\n \"X-Subscription-Token\": this.apiKey,\n },\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!resp.ok) {\n return `Error: Search API returned ${resp.status}`;\n }\n\n const data = (await resp.json()) as {\n web?: {\n results?: {\n title?: string;\n url?: string;\n description?: string;\n }[];\n };\n };\n const results = data.web?.results ?? [];\n\n if (results.length === 0) {\n return `No results for: ${query}`;\n }\n\n const lines = [`Results for: ${query}\\n`];\n for (let i = 0; i < Math.min(results.length, count); i += 1) {\n const item = results[i];\n lines.push(`${i + 1}. ${item.title ?? \"\"}\\n ${item.url ?? \"\"}`);\n if (item.description) {\n lines.push(` ${item.description}`);\n }\n }\n return lines.join(\"\\n\");\n } catch (error) {\n return `Error: ${error instanceof Error ? error.message : error}`;\n }\n }\n}\n\n/** Fetch and extract content from a URL. */\nexport class WebFetchTool extends Tool {\n readonly name = \"web_fetch\";\n readonly description =\n \"Fetch URL and extract readable content (HTML -> markdown/text).\";\n readonly parameters = {\n properties: {\n url: { type: \"string\", description: \"URL to fetch\" },\n extractMode: {\n type: \"string\",\n enum: [\"markdown\", \"text\"],\n description: \"Extract mode\",\n },\n maxChars: { type: \"integer\", minimum: 100 },\n },\n required: [\"url\"],\n type: \"object\",\n };\n\n private defaultMaxChars: number;\n\n constructor(params?: { maxChars?: number }) {\n super();\n this.defaultMaxChars = params?.maxChars ?? 50_000;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const url = String(args.url);\n const extractMode = String(args.extractMode ?? \"markdown\");\n const maxChars = args.maxChars\n ? Number(args.maxChars)\n : this.defaultMaxChars;\n\n const { valid, error: validationError } = await validateUrl(url);\n if (!valid) {\n return JSON.stringify({\n error: `URL validation failed: ${validationError}`,\n url,\n });\n }\n\n try {\n let finalResp = await fetch(url, {\n headers: { \"User-Agent\": USER_AGENT },\n redirect: \"manual\",\n signal: AbortSignal.timeout(30_000),\n });\n\n let redirectCount = 0;\n const maxRedirects = 5;\n\n while (\n finalResp.status >= 300 &&\n finalResp.status < 400 &&\n redirectCount < maxRedirects\n ) {\n const location = finalResp.headers.get(\"location\");\n if (!location) {\n break;\n }\n\n const { valid: redirectValid } = await validateUrl(location);\n if (!redirectValid) {\n return JSON.stringify({\n error: `Redirect to blocked address: ${location}`,\n url,\n });\n }\n\n finalResp = await fetch(location, {\n headers: { \"User-Agent\": USER_AGENT },\n redirect: \"manual\",\n signal: AbortSignal.timeout(30_000),\n });\n redirectCount += 1;\n }\n\n if (redirectCount >= maxRedirects) {\n return JSON.stringify({\n error: \"Too many redirects\",\n url,\n });\n }\n\n if (!finalResp.ok) {\n return JSON.stringify({\n error: `HTTP ${finalResp.status}`,\n url,\n });\n }\n\n const contentType = finalResp.headers.get(\"content-type\") ?? \"\";\n const body = await finalResp.text();\n let text: string;\n let extractor: string;\n\n if (contentType.includes(\"application/json\")) {\n try {\n text = JSON.stringify(JSON.parse(body), null, 2);\n } catch {\n text = body;\n }\n extractor = \"json\";\n } else if (\n contentType.includes(\"text/html\") ||\n body.slice(0, 256).toLowerCase().startsWith(\"<!doctype\") ||\n body.slice(0, 256).toLowerCase().startsWith(\"<html\")\n ) {\n // Extract readable content from HTML\n text =\n extractMode === \"markdown\" ? htmlToMarkdown(body) : stripTags(body);\n extractor = \"html\";\n } else {\n text = body;\n extractor = \"raw\";\n }\n\n const truncated = text.length > maxChars;\n if (truncated) {\n text = text.slice(0, maxChars);\n }\n\n return JSON.stringify({\n extractor,\n finalUrl: finalResp.url,\n length: text.length,\n status: finalResp.status,\n text,\n truncated,\n url,\n });\n } catch (error) {\n return JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n url,\n });\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/bus/events.mjs
CHANGED
package/dist/bus/events.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.mjs","names":[],"sources":["../../src/bus/events.ts"],"sourcesContent":["/** Message received from a chat channel. */\nexport interface InboundMessage {\n channel: string; // telegram, discord, etc.\n senderId: string; // User identifier\n chatId: string; // Chat/channel identifier\n content: string; // Message text\n timestamp: Date;\n media: string[]; // Media file paths\n metadata: Record<string, unknown>; // Channel-specific data\n}\n\n/** Message to send to a chat channel. */\nexport interface OutboundMessage {\n channel: string;\n chatId: string;\n content: string;\n replyTo?: string;\n media: string[];\n metadata: Record<string, unknown>;\n}\n\n/** Create an InboundMessage with defaults. */\nexport function createInboundMessage(\n partial: Pick<InboundMessage, \"channel\" | \"senderId\" | \"chatId\" | \"content\"> &\n Partial<InboundMessage
|
|
1
|
+
{"version":3,"file":"events.mjs","names":[],"sources":["../../src/bus/events.ts"],"sourcesContent":["/** Message received from a chat channel. */\nexport interface InboundMessage {\n channel: string; // telegram, discord, etc.\n senderId: string; // User identifier\n chatId: string; // Chat/channel identifier\n content: string; // Message text\n timestamp: Date;\n media: string[]; // Media file paths\n metadata: Record<string, unknown>; // Channel-specific data\n}\n\n/** Message to send to a chat channel. */\nexport interface OutboundMessage {\n channel: string;\n chatId: string;\n content: string;\n replyTo?: string;\n media: string[];\n metadata: Record<string, unknown>;\n}\n\n/** Create an InboundMessage with defaults. */\nexport function createInboundMessage(\n partial: Pick<InboundMessage, \"channel\" | \"senderId\" | \"chatId\" | \"content\"> &\n Partial<InboundMessage>\n): InboundMessage {\n return {\n media: [],\n metadata: {},\n timestamp: new Date(),\n ...partial,\n };\n}\n\n/** Create an OutboundMessage with defaults. */\nexport function createOutboundMessage(\n partial: Pick<OutboundMessage, \"channel\" | \"chatId\" | \"content\"> &\n Partial<OutboundMessage>\n): OutboundMessage {\n return {\n media: [],\n metadata: {},\n ...partial,\n };\n}\n\n/** Get session key from an inbound message. */\nexport function getSessionKey(msg: InboundMessage): string {\n return `${msg.channel}:${msg.chatId}`;\n}\n"],"mappings":";;AAsBA,SAAgB,qBACd,SAEgB;AAChB,QAAO;EACL,OAAO,EAAE;EACT,UAAU,EAAE;EACZ,2BAAW,IAAI,MAAM;EACrB,GAAG;EACJ;;;AAIH,SAAgB,sBACd,SAEiB;AACjB,QAAO;EACL,OAAO,EAAE;EACT,UAAU,EAAE;EACZ,GAAG;EACJ;;;AAIH,SAAgB,cAAc,KAA6B;AACzD,QAAO,GAAG,IAAI,QAAQ,GAAG,IAAI"}
|
package/dist/bus/queue.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.mts","names":[],"sources":["../../src/bus/queue.ts"],"mappings":";;;;;AAAmE;cAK7D,UAAA;EAAA,QACI,KAAA;EAAA,QACA,OAAA;EAEF,GAAA,CAAI,IAAA,EAAM,CAAA,GAAI,OAAA;EASd,GAAA,CAAA,GAAO,OAAA,CAAQ,CAAA;EAAR;;;;EAcb,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,CAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"queue.d.mts","names":[],"sources":["../../src/bus/queue.ts"],"mappings":";;;;;AAAmE;cAK7D,UAAA;EAAA,QACI,KAAA;EAAA,QACA,OAAA;EAEF,GAAA,CAAI,IAAA,EAAM,CAAA,GAAI,OAAA;EASd,GAAA,CAAA,GAAO,OAAA,CAAQ,CAAA;EAAR;;;;EAcb,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,CAAA;EAAA,IAqChC,IAAA,CAAA;AAAA;;;;;;;cAWO,UAAA;EAAA,SACF,OAAA,EAAO,UAAA,CAAA,cAAA;EAAA,SACP,QAAA,EAAQ,UAAA,CAAA,eAAA;EAEX,cAAA,CAAe,GAAA,EAAK,cAAA,GAAiB,OAAA;EAIrC,cAAA,CAAA,GAAkB,OAAA,CAAQ,cAAA;EAxDI;EA6DpC,qBAAA,CAAsB,EAAA,WAAa,OAAA,CAAQ,cAAA;EAIrC,eAAA,CAAgB,GAAA,EAAK,eAAA,GAAkB,OAAA;EAIvC,eAAA,CAAA,GAAmB,OAAA,CAAQ,eAAA;EArBtB;EA0BX,sBAAA,CAAuB,EAAA,WAAa,OAAA,CAAQ,eAAA;EAAA,IAIxC,WAAA,CAAA;EAAA,IAIA,YAAA,CAAA;AAAA"}
|
package/dist/bus/queue.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.mjs","names":[],"sources":["../../src/bus/queue.ts"],"sourcesContent":["import type { InboundMessage, OutboundMessage } from \"./events.js\";\n\n/**\n * Simple async queue. Mimics Python's asyncio.Queue.\n */\nclass AsyncQueue<T> {\n private queue: T[] = [];\n private waiters:
|
|
1
|
+
{"version":3,"file":"queue.mjs","names":[],"sources":["../../src/bus/queue.ts"],"sourcesContent":["import type { InboundMessage, OutboundMessage } from \"./events.js\";\n\n/**\n * Simple async queue. Mimics Python's asyncio.Queue.\n */\nclass AsyncQueue<T> {\n private queue: T[] = [];\n private waiters: ((value: T) => void)[] = [];\n\n async put(item: T): Promise<void> {\n const waiter = this.waiters.shift();\n if (waiter) {\n waiter(item);\n } else {\n this.queue.push(item);\n }\n }\n\n async get(): Promise<T> {\n const item = this.queue.shift();\n if (item !== undefined) {\n return item;\n }\n return new Promise<T>((resolve) => {\n this.waiters.push(resolve);\n });\n }\n\n /**\n * Wait for an item with a timeout. On timeout, the waiter is removed\n * from the queue so it does not consume a future item.\n */\n getWithTimeout(ms: number): Promise<T> {\n const item = this.queue.shift();\n if (item !== undefined) {\n return Promise.resolve(item);\n }\n\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n\n const waiter = (value: T) => {\n if (settled) {\n // Timeout already fired — put the item back so it isn't lost\n this.queue.unshift(value);\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve(value);\n };\n\n this.waiters.push(waiter);\n\n const timer = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n // Remove our waiter so it doesn't consume a future item\n const idx = this.waiters.indexOf(waiter);\n if (idx !== -1) {\n this.waiters.splice(idx, 1);\n }\n reject(new Error(\"timeout\"));\n }, ms);\n });\n }\n\n get size(): number {\n return this.queue.length;\n }\n}\n\n/**\n * Async message bus that decouples chat channels from the agent core.\n *\n * Channels push messages to the inbound queue, and the agent processes\n * them and pushes responses to the outbound queue.\n */\nexport class MessageBus {\n readonly inbound = new AsyncQueue<InboundMessage>();\n readonly outbound = new AsyncQueue<OutboundMessage>();\n\n async publishInbound(msg: InboundMessage): Promise<void> {\n await this.inbound.put(msg);\n }\n\n async consumeInbound(): Promise<InboundMessage> {\n return this.inbound.get();\n }\n\n /** Consume with timeout — safely removes the waiter on timeout so no messages are lost. */\n consumeInboundTimeout(ms: number): Promise<InboundMessage> {\n return this.inbound.getWithTimeout(ms);\n }\n\n async publishOutbound(msg: OutboundMessage): Promise<void> {\n await this.outbound.put(msg);\n }\n\n async consumeOutbound(): Promise<OutboundMessage> {\n return this.outbound.get();\n }\n\n /** Consume with timeout — safely removes the waiter on timeout so no messages are lost. */\n consumeOutboundTimeout(ms: number): Promise<OutboundMessage> {\n return this.outbound.getWithTimeout(ms);\n }\n\n get inboundSize(): number {\n return this.inbound.size;\n }\n\n get outboundSize(): number {\n return this.outbound.size;\n }\n}\n"],"mappings":";;;;AAKA,IAAM,aAAN,MAAoB;CAClB,AAAQ,QAAa,EAAE;CACvB,AAAQ,UAAkC,EAAE;CAE5C,MAAM,IAAI,MAAwB;EAChC,MAAM,SAAS,KAAK,QAAQ,OAAO;AACnC,MAAI,OACF,QAAO,KAAK;MAEZ,MAAK,MAAM,KAAK,KAAK;;CAIzB,MAAM,MAAkB;EACtB,MAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,MAAI,SAAS,OACX,QAAO;AAET,SAAO,IAAI,SAAY,YAAY;AACjC,QAAK,QAAQ,KAAK,QAAQ;IAC1B;;;;;;CAOJ,eAAe,IAAwB;EACrC,MAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,MAAI,SAAS,OACX,QAAO,QAAQ,QAAQ,KAAK;AAG9B,SAAO,IAAI,SAAY,SAAS,WAAW;GACzC,IAAI,UAAU;GAEd,MAAM,UAAU,UAAa;AAC3B,QAAI,SAAS;AAEX,UAAK,MAAM,QAAQ,MAAM;AACzB;;AAEF,cAAU;AACV,iBAAa,MAAM;AACnB,YAAQ,MAAM;;AAGhB,QAAK,QAAQ,KAAK,OAAO;GAEzB,MAAM,QAAQ,iBAAiB;AAC7B,QAAI,QACF;AAEF,cAAU;IAEV,MAAM,MAAM,KAAK,QAAQ,QAAQ,OAAO;AACxC,QAAI,QAAQ,GACV,MAAK,QAAQ,OAAO,KAAK,EAAE;AAE7B,2BAAO,IAAI,MAAM,UAAU,CAAC;MAC3B,GAAG;IACN;;CAGJ,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM;;;;;;;;;AAUtB,IAAa,aAAb,MAAwB;CACtB,AAAS,UAAU,IAAI,YAA4B;CACnD,AAAS,WAAW,IAAI,YAA6B;CAErD,MAAM,eAAe,KAAoC;AACvD,QAAM,KAAK,QAAQ,IAAI,IAAI;;CAG7B,MAAM,iBAA0C;AAC9C,SAAO,KAAK,QAAQ,KAAK;;;CAI3B,sBAAsB,IAAqC;AACzD,SAAO,KAAK,QAAQ,eAAe,GAAG;;CAGxC,MAAM,gBAAgB,KAAqC;AACzD,QAAM,KAAK,SAAS,IAAI,IAAI;;CAG9B,MAAM,kBAA4C;AAChD,SAAO,KAAK,SAAS,KAAK;;;CAI5B,uBAAuB,IAAsC;AAC3D,SAAO,KAAK,SAAS,eAAe,GAAG;;CAGzC,IAAI,cAAsB;AACxB,SAAO,KAAK,QAAQ;;CAGtB,IAAI,eAAuB;AACzB,SAAO,KAAK,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.mts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"base.d.mts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;AAQA;uBAAsB,WAAA;EAAA,kBACF,IAAA;EAAA,UACR,MAAA;EAAA,UACA,GAAA,EAAK,UAAA;EAAA,UACL,QAAA;cAEE,MAAA,WAAiB,GAAA,EAAK,UAAA;EAAA,SAKzB,KAAA,CAAA,GAAS,OAAA;EAAA,SACT,IAAA,CAAA,GAAQ,OAAA;EAAA,SACR,IAAA,CAAK,GAAA,EAAK,eAAA,GAAkB,OAAA;EAiCjC;EA9BJ,SAAA,CAAU,QAAA;EA8BC;EAAA,UANK,aAAA,CAAc,MAAA;IAC5B,QAAA;IACA,MAAA;IACA,OAAA;IACA,KAAA;IACA,QAAA,GAAW,MAAA;EAAA,IACT,OAAA;EAAA,IAiBA,SAAA,CAAA;AAAA"}
|
package/dist/channels/base.mjs
CHANGED
|
@@ -28,11 +28,11 @@ var BaseChannel = class {
|
|
|
28
28
|
if (!this.isAllowed(params.senderId)) return;
|
|
29
29
|
const msg = createInboundMessage({
|
|
30
30
|
channel: this.name,
|
|
31
|
-
senderId: String(params.senderId),
|
|
32
31
|
chatId: String(params.chatId),
|
|
33
32
|
content: params.content,
|
|
34
33
|
media: params.media ?? [],
|
|
35
|
-
metadata: params.metadata ?? {}
|
|
34
|
+
metadata: params.metadata ?? {},
|
|
35
|
+
senderId: String(params.senderId)
|
|
36
36
|
});
|
|
37
37
|
await this.bus.publishInbound(msg);
|
|
38
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.mjs","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import type { OutboundMessage, InboundMessage } from \"../bus/events.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"base.mjs","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import type { OutboundMessage, InboundMessage } from \"../bus/events.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\n\nimport { createInboundMessage } from \"../bus/events.js\";\n\n/**\n * Abstract base class for chat channel implementations.\n */\nexport abstract class BaseChannel {\n abstract readonly name: string;\n protected config: unknown;\n protected bus: MessageBus;\n protected _running = false;\n\n constructor(config: unknown, bus: MessageBus) {\n this.config = config;\n this.bus = bus;\n }\n\n abstract start(): Promise<void>;\n abstract stop(): Promise<void>;\n abstract send(msg: OutboundMessage): Promise<void>;\n\n /** Check if a sender is allowed to use this bot. */\n isAllowed(senderId: string): boolean {\n const allowList =\n (this.config as { allowFrom?: string[] })?.allowFrom ?? [];\n if (allowList.length === 0) {\n return true;\n }\n\n const senderStr = String(senderId);\n if (allowList.includes(senderStr)) {\n return true;\n }\n\n // Check pipe-separated IDs (e.g., \"123456|username\")\n if (senderStr.includes(\"|\")) {\n for (const part of senderStr.split(\"|\")) {\n if (part && allowList.includes(part)) {\n return true;\n }\n }\n }\n return false;\n }\n\n /** Handle an incoming message from the chat platform. */\n protected async handleMessage(params: {\n senderId: string;\n chatId: string;\n content: string;\n media?: string[];\n metadata?: Record<string, unknown>;\n }): Promise<void> {\n if (!this.isAllowed(params.senderId)) {\n return;\n }\n\n const msg = createInboundMessage({\n channel: this.name,\n chatId: String(params.chatId),\n content: params.content,\n media: params.media ?? [],\n metadata: params.metadata ?? {},\n senderId: String(params.senderId),\n });\n\n await this.bus.publishInbound(msg);\n }\n\n get isRunning(): boolean {\n return this._running;\n }\n}\n"],"mappings":";;;;;;AAQA,IAAsB,cAAtB,MAAkC;CAEhC,AAAU;CACV,AAAU;CACV,AAAU,WAAW;CAErB,YAAY,QAAiB,KAAiB;AAC5C,OAAK,SAAS;AACd,OAAK,MAAM;;;CAQb,UAAU,UAA2B;EACnC,MAAM,YACH,KAAK,QAAqC,aAAa,EAAE;AAC5D,MAAI,UAAU,WAAW,EACvB,QAAO;EAGT,MAAM,YAAY,OAAO,SAAS;AAClC,MAAI,UAAU,SAAS,UAAU,CAC/B,QAAO;AAIT,MAAI,UAAU,SAAS,IAAI,EACzB;QAAK,MAAM,QAAQ,UAAU,MAAM,IAAI,CACrC,KAAI,QAAQ,UAAU,SAAS,KAAK,CAClC,QAAO;;AAIb,SAAO;;;CAIT,MAAgB,cAAc,QAMZ;AAChB,MAAI,CAAC,KAAK,UAAU,OAAO,SAAS,CAClC;EAGF,MAAM,MAAM,qBAAqB;GAC/B,SAAS,KAAK;GACd,QAAQ,OAAO,OAAO,OAAO;GAC7B,SAAS,OAAO;GAChB,OAAO,OAAO,SAAS,EAAE;GACzB,UAAU,OAAO,YAAY,EAAE;GAC/B,UAAU,OAAO,OAAO,SAAS;GAClC,CAAC;AAEF,QAAM,KAAK,IAAI,eAAe,IAAI;;CAGpC,IAAI,YAAqB;AACvB,SAAO,KAAK"}
|
package/dist/channels/line.d.mts
CHANGED
|
@@ -135,6 +135,7 @@ declare class LineChannel extends BaseChannel {
|
|
|
135
135
|
private parseMessage;
|
|
136
136
|
/** Find the end index of a top-level JSON object in a string. Returns -1 if not found. */
|
|
137
137
|
private findJsonEnd;
|
|
138
|
+
private extractText;
|
|
138
139
|
/** Extract alt text from flex content. */
|
|
139
140
|
private extractAltText;
|
|
140
141
|
/** Format file size in human-readable form. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"line.d.mts","names":[],"sources":["../../src/channels/line.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"line.d.mts","names":[],"sources":["../../src/channels/line.ts"],"mappings":";;;;;;UAiBU,eAAA;EACR,WAAA;EACA,MAAA,EAAQ,SAAA;AAAA;AAAA,UAGA,UAAA;EACR,IAAA;EACA,MAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAGQ,SAAA;EACR,IAAA;EACA,SAAA;EACA,MAAA,EAAQ,UAAA;EACR,UAAA;EACA,IAAA;EACA,cAAA;EACA,eAAA;IAAmB,YAAA;EAAA;EACnB,OAAA,GAAU,WAAA;AAAA;AAAA,UAKF,SAAA;EACR,KAAA;EACA,MAAA;EACA,SAAA;EACA,OAAA;AAAA;AAAA,UAKQ,aAAA;EACR,KAAA;EACA,MAAA;EACA,IAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAA,UAGQ,WAAA;EACR,UAAA,EAAY,aAAA;AAAA;AAAA,UAKJ,mBAAA;EACR,IAAA;EACA,kBAAA;EACA,eAAA;AAAA;AAAA,UAKQ,YAAA;EACR,EAAA;EACA,KAAA;EACA,KAAA;AAAA;AAAA,UAKQ,WAAA;EACR,IAAA;EACA,EAAA;EACA,UAAA;EACA,eAAA;EAGA,IAAA;EACA,MAAA,GAAS,SAAA;EACT,OAAA,GAAU,WAAA;EAGV,eAAA,GAAkB,mBAAA;EAClB,QAAA,GAAW,YAAA;EAGX,QAAA;EAGA,QAAA;EACA,QAAA;EAGA,KAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EAGA,SAAA;EACA,SAAA;EACA,mBAAA;EACA,QAAA;AAAA;AAAA,iBAOc,mBAAA,CACd,aAAA,UACA,OAAA,UACA,SAAA;;;AAvDe;;;;;cA8EJ,WAAA,SAAoB,WAAA;EAAA,SACtB,IAAA;EAAA,QACD,UAAA;EAAA,QACA,SAAA;cAEI,MAAA,EAAQ,UAAA,EAAY,GAAA,EAAK,UAAA,EAAY,SAAA;EAO3C,KAAA,CAAA,GAAS,OAAA;EAKT,IAAA,CAAA,GAAQ,OAAA;EAMR,IAAA,CAAK,GAAA,EAAK,eAAA,GAAkB,OAAA;EA/ExB;EAuFJ,KAAA,CAAM,UAAA,UAAoB,IAAA,WAAe,OAAA;EAnFpC;EAwGL,WAAA,CAAY,EAAA,UAAY,IAAA,WAAe,OAAA;EAxGtB;;;;EAkIjB,aAAA,CAAc,IAAA,EAAM,eAAA,GAAkB,OAAA;EAAA,QAiC9B,cAAA;EAxKd;EAAA,QA8PQ,gBAAA;EA7PR;EAAA,QAgRc,kBAAA;EA7Qd;EAAA,QAsSQ,kBAAA;EArSR;EAAA,QA6SQ,kBAAA;EA1SR;EAAA,QAkTc,iBAAA;EA9Sd;EAAA,QA0TQ,qBAAA;EAtTR;EAAA,QAqUQ,oBAAA;EAnUR;;;;EAAA,QA+Vc,eAAA;EAzVN;;AAOV;;;EAPU,QAqYM,mBAAA;EA7Xd;;;;EAgbA,eAAA,CAAgB,OAAA,UAAiB,SAAA;EAAA,QAUzB,iBAAA;EAjae;EAAA,QA8af,YAAA;EAzaY;EAAA,QA+cZ,WAAA;EAAA,QA6CA,WAAA;EAhfM;EAAA,QAuhBN,cAAA;EAjhB0B;EAAA,QAshB1B,cAAA;AAAA"}
|