diff --git a/bun.lock b/bun.lock index 7c47a0073..d781fb516 100644 --- a/bun.lock +++ b/bun.lock @@ -8,12 +8,12 @@ "@sinclair/typebox": "^0.34.48", }, "devDependencies": { - "@biomejs/biome": "2.3.14", + "@biomejs/biome": "^2.3.14", "@bufbuild/protoc-gen-es": "^2.11.0", - "@types/bun": "^1.3.7", - "@types/node": "^25.0.10", - "@typescript/native-preview": "^7.0.0-dev.20260128.1", - "bun-types": "^1.3.7", + "@types/bun": "^1.3.8", + "@types/node": "^25.2.0", + "@typescript/native-preview": "^7.0.0-dev.20260203.1", + "bun-types": "^1.3.8", "concurrently": "^9.2.1", "lint-staged": "^16.2.7", "prettier": "^3.8.1", @@ -30,7 +30,7 @@ }, "devDependencies": { "@sinclair/typebox": "^0.34.48", - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", }, }, "packages/ai": { @@ -40,27 +40,25 @@ "pi-ai": "./src/cli.ts", }, "dependencies": { - "@anthropic-ai/sdk": "^0.71.2", - "@aws-sdk/client-bedrock-runtime": "^3.975.0", + "@anthropic-ai/sdk": "^0.72.1", + "@aws-sdk/client-bedrock-runtime": "^3.982.0", "@bufbuild/protobuf": "^2.11.0", "@connectrpc/connect": "^2.1.1", "@connectrpc/connect-node": "^2.1.1", - "@google/genai": "^1.38.0", + "@google/genai": "^1.39.0", "@mistralai/mistralai": "^1.13.0", "@oh-my-pi/pi-utils": "workspace:*", "@sinclair/typebox": "^0.34.48", - "@smithy/node-http-handler": "^4.4.8", + "@smithy/node-http-handler": "^4.4.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "chalk": "^5.6.2", - "openai": "^6.16.0", + "openai": "^6.17.0", "partial-json": "^0.1.7", - "proxy-agent": "^6.5.0", - "undici": "^7.19.2", "zod-to-json-schema": "^3.25.1", }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", }, }, "packages/coding-agent": { @@ -70,31 +68,35 @@ "omp": "src/cli.ts", }, "dependencies": { + "@mozilla/readability": "0.6.0", "@oh-my-pi/omp-stats": "workspace:*", "@oh-my-pi/pi-agent-core": "workspace:*", "@oh-my-pi/pi-ai": "workspace:*", "@oh-my-pi/pi-natives": "workspace:*", "@oh-my-pi/pi-tui": "workspace:*", "@oh-my-pi/pi-utils": "workspace:*", - "@openai/agents": "^0.4.4", + "@openai/agents": "^0.4.5", "@sinclair/typebox": "^0.34.48", "ajv": "^8.17.1", "chalk": "^5.6.2", "diff": "^8.0.3", "file-type": "^21.3.0", - "glob": "^13.0.0", + "glob": "^13.0.1", "handlebars": "^4.7.8", "ignore": "^7.0.5", + "jsdom": "28.0.0", "marked": "^17.0.1", "nanoid": "^5.1.6", "node-html-parser": "^7.0.2", + "puppeteer": "^24.36.1", "smol-toml": "^1.6.0", "zod": "^4.3.6", }, "devDependencies": { "@types/diff": "^8.0.0", + "@types/jsdom": "27.0.0", "@types/ms": "^2.1.0", - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", "ms": "^2.1.3", }, }, @@ -105,7 +107,7 @@ "@oh-my-pi/pi-utils": "workspace:*", }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", }, }, "packages/react-edit-benchmark": { @@ -123,7 +125,7 @@ }, "devDependencies": { "@types/diff": "^8.0.0", - "bun-types": "^1.3.7", + "bun-types": "^1.3.8", }, }, "packages/stats": { @@ -141,7 +143,7 @@ "recharts": "^3.7.0", }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", }, @@ -169,12 +171,20 @@ "winston-daily-rotate-file": "^5.0.0", }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", }, }, }, "packages": { - "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="], + "@acemir/cssom": ["@acemir/cssom@0.9.31", "", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.72.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-MiUnue7qN7DvLIoYHgkedN2z05mRf2CutBzjXXY2krzOhG2r/rIfISS2uVkNLikgToB5hYIzw+xp2jdOtRkqYQ=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@4.1.1", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.2.4" } }, "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.7.7", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.5" } }, "sha512-8CO/UQ4tzDd7ula+/CVimJIVWez99UJlbMyIgk8xOnhAVPKLnBZmUFYVgugS441v2ZqUq5EnSh6B0Ua0liSFAA=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], @@ -244,6 +254,10 @@ "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.3", "", {}, "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], "@biomejs/biome": ["@biomejs/biome@2.3.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.14", "@biomejs/cli-darwin-x64": "2.3.14", "@biomejs/cli-linux-arm64": "2.3.14", "@biomejs/cli-linux-arm64-musl": "2.3.14", "@biomejs/cli-linux-x64": "2.3.14", "@biomejs/cli-linux-x64-musl": "2.3.14", "@biomejs/cli-win32-arm64": "2.3.14", "@biomejs/cli-win32-x64": "2.3.14" }, "bin": { "biome": "bin/biome" } }, "sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA=="], @@ -278,8 +292,22 @@ "@connectrpc/connect-node": ["@connectrpc/connect-node@2.1.1", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.7.0", "@connectrpc/connect": "2.1.1" } }, "sha512-s3TfsI1XF+n+1z6MBS9rTnFsxxR4Rw5wmdEnkQINli81ESGxcsfaEet8duzq8LVuuCupmhUsgpRo0Nv9pZkufg=="], + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.0.26", "", {}, "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], + "@exodus/bytes": ["@exodus/bytes@1.11.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA=="], + "@google/genai": ["@google/genai@1.39.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-Vz7AQsOdBeiIcxmXIQNy/hzDvyAOE1lSpWA10itUQza7h3aQFF6QSGaQ7o1GYsjMD3XslK4Ee/Ol0eLhRXb7gA=="], "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="], @@ -294,6 +322,8 @@ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "@mozilla/readability": ["@mozilla/readability@0.6.0", "", {}, "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ=="], + "@oh-my-pi/omp-stats": ["@oh-my-pi/omp-stats@workspace:packages/stats"], "@oh-my-pi/pi-agent-core": ["@oh-my-pi/pi-agent-core@workspace:packages/agent"], @@ -340,6 +370,8 @@ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + "@puppeteer/browsers": ["@puppeteer/browsers@2.11.2", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.3", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-GBY0+2lI9fDrjgb5dFL9+enKXqyOPok9PXg/69NVkjW3bikbK9RQrNrI3qccQXmDNN7ln4j/yL89Qgvj/tfqrw=="], + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], "@sinclair/typebox": ["@sinclair/typebox@0.34.48", "", {}, "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA=="], @@ -470,6 +502,8 @@ "@types/diff": ["@types/diff@8.0.0", "", { "dependencies": { "diff": "*" } }, "sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw=="], + "@types/jsdom": ["@types/jsdom@27.0.0", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw=="], + "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="], "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], @@ -480,27 +514,31 @@ "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], + "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260203.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260203.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260203.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260203.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260203.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260203.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260203.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260203.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-8XwI9TS/idoFXAM8fVjBS4QmRzBr/AE3dQiMdhgwW+49tcGJeiU6MOwTHYyp8MhlFFx/1kiNwwdPoUnp3cS5vQ=="], + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260203.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0se1eibJ4rJ3JXD1WlNF3JL3GWjUeZ9fmDJyUkpP0974cFApblKhqsYubT+kDWrWExh4IXctzjRpkWfJi2srow=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260204.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260204.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260204.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260204.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260204.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260204.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260204.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260204.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-o1R4QGAjCWG0LOqpDOBuH9H+BgaM+7Ps8AuvHJkf4V1tmTFqeGMXGNbv02f3HQIZiI3KMqsRGoBGe/LvCc4SFg=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260203.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-mMO5QBKSeVFWKvCu5+G9mbh5EmYqnCZb0g7s9HAUGAtOdjeGBVrvJTXLnahMbVB2KLU3ApUQCZQOoumZQ5z0Qw=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260204.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wdUsNaGGxAPPR5ySjtT7UBVV9Elapnt5dCnDIzxtZCGgO25VBo6SFS+BvIyHyq2tZd1pj800QWyeRz5mYyackg=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260203.1", "", { "os": "linux", "cpu": "arm" }, "sha512-74Q4nr9zOWlWqNJ726BrujFpeqNVuNo09GUaIHtV3TatVf6qGcQtWJLD4xdsZXt1n1YwneTiBHqfXwU7h6Z26g=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260204.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-U7Y3pWqkw1lypy4fG2WyvU7X5k7z1DjyvWQui4kyM4uRgdHQSZvdUe5ZF1iiYyhI2wcGkkO4dHf7IZkL55Z+Gw=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260203.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vl3f5LKFAXrgb7TTag8sG7Np9imyt1QsiRN5O9yl/aoqZpopdmMSBTUFuOzQFLD9ive/0cTsRIKt32gWT4zvew=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260204.1", "", { "os": "linux", "cpu": "arm" }, "sha512-7fd0o+w5JBBzfzPyli6mr3NUbVGsKN9l8smAU2BQHLnMa6P950ixfP6mVc8p9RXDqerAjlgl/HQptYyYUueeZQ=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260203.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HGNXkZ10rs88It98rfGhvCwG9RKLm2TVjfYOnKzYq0xKrfvG9exvpojio5n5OxXaSvTumTjRbWbRYY+qwpSCag=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260204.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yUrJnzXGXB7IO/mwWsmAnHngpry3hsRlFstIbEx+HKrX2cUR6A6+GMQaP4wyvPQ9qhriA/+uKTPtbkh4B15DrA=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260203.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GkuRbW13iq2lRijkHnTWzrM/LThrr6KG0IXewdXd//E0DXcVvlg7ommgxPC29cf18X6T3vul1owjNmjVwp8siA=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260204.1", "", { "os": "linux", "cpu": "x64" }, "sha512-OySOCSzlBpDW5sB9ttpC7QU4igM0x/H8qvdtfTLkV0v5l/qMfU12b0KJFo5DdTSnKomTLvfmfvHlSQ+GHVF5pQ=="], - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260203.1", "", { "os": "win32", "cpu": "x64" }, "sha512-O+OsC8DOEnoKWWlWrPOnULh0kSvQ8DiWWfFMVIDmoWfCIi2cDLZKKE1WfCXSLi6L76H9f1zXJEvNqW58tEG/vQ=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260204.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-/BSlSR8OoG7abieDC2jgvAZJeOo9Dlh6j1lTqoZOIFHmmbKICPwAsQXb9VW2e8Gr3uqLem4ZDALA3cSW3zIz/g=="], + + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260204.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rPVJt2AsPTn4cp7C1HbAg3vs+L+JYCGxeKQN1sE6j5k9qGM536K8XvVUHKumhL8BxrhidV7FPGSsQO/zSpboug=="], "@typescript/vfs": ["@typescript/vfs@1.6.2", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g=="], @@ -522,16 +560,34 @@ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + "b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], + + "bare-fs": ["bare-fs@4.5.3", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ=="], + + "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], + + "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], + + "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], + + "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], "basic-ftp": ["basic-ftp@5.1.0", "", {}, "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw=="], + "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], @@ -544,6 +600,8 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], @@ -554,8 +612,12 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "chromium-bidi": ["chromium-bidi@13.0.1", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-c+RLxH0Vg2x2syS9wPw378oJgiJNXtYXUvnVAldUlt5uaHekn0CCU7gPksNgHjrH1qFhmjVXQj4esvuthuC7OQ=="], + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], @@ -590,12 +652,18 @@ "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], + "cssstyle": ["cssstyle@5.3.7", "", { "dependencies": { "@asamuzakjp/css-color": "^4.1.1", "@csstools/css-syntax-patches-for-csstree": "^1.0.21", "css-tree": "^3.1.0", "lru-cache": "^11.2.4" } }, "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ=="], + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], @@ -620,18 +688,24 @@ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], - "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + + "data-urls": ["data-urls@7.0.0", "", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0" } }, "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA=="], "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "devtools-protocol": ["devtools-protocol@0.0.1551306", "", {}, "sha512-CFx8QdSim8iIv+2ZcEOclBKTQY6BI1IEDa7Tm9YkwAXzEWFndTEzpTo5jAUhSnq24IC7xaDw0wvGcm96+Y3PEg=="], + "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -656,10 +730,16 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -684,6 +764,8 @@ "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], @@ -694,12 +776,18 @@ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "fast-xml-parser": ["fast-xml-parser@5.3.4", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA=="], + "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fecha": ["fecha@4.2.3", "", {}, "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="], @@ -738,6 +826,8 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="], "glob": ["glob@13.0.1", "", { "dependencies": { "minimatch": "^10.1.2", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w=="], @@ -762,6 +852,8 @@ "hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="], + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], @@ -776,6 +868,8 @@ "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], @@ -784,10 +878,14 @@ "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -798,8 +896,16 @@ "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsdom": ["jsdom@28.0.0", "", { "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.7.6", "@exodus/bytes": "^1.11.0", "cssstyle": "^5.3.7", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "undici": "^7.20.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA=="], + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -812,6 +918,8 @@ "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "lint-staged": ["lint-staged@16.2.7", "", { "dependencies": { "commander": "^14.0.2", "listr2": "^9.0.5", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow=="], "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], @@ -822,7 +930,7 @@ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], - "lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], "lucide-react": ["lucide-react@0.563.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], @@ -830,6 +938,8 @@ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], @@ -848,6 +958,8 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + "moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -892,6 +1004,12 @@ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], @@ -904,6 +1022,10 @@ "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], @@ -912,6 +1034,8 @@ "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], @@ -920,6 +1044,14 @@ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "puppeteer": ["puppeteer@24.36.1", "", { "dependencies": { "@puppeteer/browsers": "2.11.2", "chromium-bidi": "13.0.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1551306", "puppeteer-core": "24.36.1", "typed-query-selector": "^2.12.0" }, "bin": { "puppeteer": "lib/cjs/puppeteer/node/cli.js" } }, "sha512-uPiDUyf7gd7Il1KnqfNUtHqntL0w1LapEw5Zsuh8oCK8GsqdxySX1PzdIHKB2Dw273gWY4MW0zC5gy3Re9XlqQ=="], + + "puppeteer-core": ["puppeteer-core@24.36.1", "", { "dependencies": { "@puppeteer/browsers": "2.11.2", "chromium-bidi": "13.0.1", "debug": "^4.4.3", "devtools-protocol": "0.0.1551306", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.4.0", "ws": "^8.19.0" } }, "sha512-L7ykMWc3lQf3HS7ME3PSjp7wMIjJeW6+bKfH/RSTz5l6VUDGubnrC2BKj3UvM28Y5PMDFW0xniJOZHBZPpW1dQ=="], + "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -948,6 +1080,8 @@ "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], @@ -964,8 +1098,12 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], @@ -1000,10 +1138,14 @@ "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="], "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1022,18 +1164,34 @@ "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], + + "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], + + "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], + "text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="], "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tldts": ["tldts@7.0.22", "", { "dependencies": { "tldts-core": "^7.0.22" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-nqpKFC53CgopKPjT6Wfb6tpIcZXHcI6G37hesvikhx0EmUGPkZrujRyAjgnmp1SHNgpQfKVanZ+KfpANFt2Hxw=="], + + "tldts-core": ["tldts-core@7.0.22", "", {}, "sha512-KgbTDC5wzlL6j/x6np6wCnDSMUq4kucHNm00KXPbfNzmllCmtmvtykJHfmgdHntwIeupW04y8s1N/43S1PkQDw=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], + "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + + "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], "triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="], @@ -1046,6 +1204,8 @@ "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typed-query-selector": ["typed-query-selector@2.12.0", "", {}, "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg=="], + "typescript": ["typescript@5.4.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="], "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], @@ -1066,8 +1226,18 @@ "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "webdriver-bidi-protocol": ["webdriver-bidi-protocol@0.4.0", "", {}, "sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA=="], + + "webidl-conversions": ["webidl-conversions@8.0.1", "", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], + + "whatwg-url": ["whatwg-url@16.0.0", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "winston": ["winston@3.19.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA=="], @@ -1086,6 +1256,10 @@ "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], @@ -1094,6 +1268,8 @@ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], @@ -1110,6 +1286,8 @@ "@reduxjs/toolkit/immer": ["immer@11.1.3", "", {}, "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q=="], + "chromium-bidi/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "cli-truncate/string-width": ["string-width@8.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -1120,11 +1298,15 @@ "concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "get-uri/data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], + + "jsdom/parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], + "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "node-fetch/data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], - - "path-scurry/lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], + "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], "rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], diff --git a/package.json b/package.json index 7a8c8ee98..a56382412 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "generate-models": "bun --cwd=packages/ai scripts/generate-models.ts" }, "devDependencies": { - "@biomejs/biome": "2.3.14", + "@biomejs/biome": "^2.3.14", "@bufbuild/protoc-gen-es": "^2.11.0", - "@types/bun": "^1.3.7", - "@types/node": "^25.0.10", - "@typescript/native-preview": "^7.0.0-dev.20260128.1", - "bun-types": "^1.3.7", + "@types/bun": "^1.3.8", + "@types/node": "^25.2.0", + "@typescript/native-preview": "^7.0.0-dev.20260203.1", + "bun-types": "^1.3.8", "concurrently": "^9.2.1", "lint-staged": "^16.2.7", "prettier": "^3.8.1", diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 3c142f346..711757c0e 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -1,6 +1,19 @@ # Changelog ## [Unreleased] +### Changed + +- Updated @anthropic-ai/sdk to ^0.72.1 +- Updated @aws-sdk/client-bedrock-runtime to ^3.982.0 +- Updated @google/genai to ^1.39.0 +- Updated @smithy/node-http-handler to ^4.4.9 +- Updated openai to ^6.17.0 +- Updated @types/node to ^25.2.0 + +### Removed + +- Removed proxy-agent dependency +- Removed undici dependency ## [9.4.0] - 2026-01-31 ### Added diff --git a/packages/ai/package.json b/packages/ai/package.json index b77ca3f2b..bd5fcdcb9 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -56,23 +56,21 @@ "test": "bun test" }, "dependencies": { - "@anthropic-ai/sdk": "^0.71.2", - "@aws-sdk/client-bedrock-runtime": "^3.975.0", + "@anthropic-ai/sdk": "^0.72.1", + "@aws-sdk/client-bedrock-runtime": "^3.982.0", "@bufbuild/protobuf": "^2.11.0", "@connectrpc/connect": "^2.1.1", "@connectrpc/connect-node": "^2.1.1", - "@google/genai": "^1.38.0", + "@google/genai": "^1.39.0", "@mistralai/mistralai": "^1.13.0", "@oh-my-pi/pi-utils": "workspace:*", "@sinclair/typebox": "^0.34.48", - "@smithy/node-http-handler": "^4.4.8", + "@smithy/node-http-handler": "^4.4.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "chalk": "^5.6.2", - "openai": "^6.16.0", + "openai": "^6.17.0", "partial-json": "^0.1.7", - "proxy-agent": "^6.5.0", - "undici": "^7.19.2", "zod-to-json-schema": "^3.25.1" }, "keywords": [ @@ -95,6 +93,6 @@ "bun": ">=1.3.7" }, "devDependencies": { - "@types/node": "^25.0.10" + "@types/node": "^25.2.0" } } diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 432f6118c..ada53321f 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] + ### Breaking Changes - Changed `ask` tool to require `questions` array parameter; single-question mode with `question`, `options`, `multi`, and `recommended` parameters is no longer supported @@ -8,6 +9,9 @@ ### Added +- Added browser tool powered by Ulixee Hero with support for navigation, DOM interaction, screenshots, and readable content extraction +- Added `/browser` command to toggle browser headless vs visible mode in interactive sessions +- Added `browser.enabled` and `browser.headless` settings to control browser automation behavior - Added Python prelude caching to improve startup performance by storing compiled prelude helpers and module metadata - Added `OMP_DEBUG_STARTUP` environment variable for conditional startup performance debugging output - Added autonomous memory system with storage, memory tools, and context injection diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index 077b4211f..c2286f42a 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -79,31 +79,35 @@ "test": "bun test" }, "dependencies": { + "@mozilla/readability": "0.6.0", "@oh-my-pi/omp-stats": "workspace:*", "@oh-my-pi/pi-agent-core": "workspace:*", "@oh-my-pi/pi-ai": "workspace:*", "@oh-my-pi/pi-natives": "workspace:*", "@oh-my-pi/pi-tui": "workspace:*", "@oh-my-pi/pi-utils": "workspace:*", - "@openai/agents": "^0.4.4", + "@openai/agents": "^0.4.5", "@sinclair/typebox": "^0.34.48", "ajv": "^8.17.1", "chalk": "^5.6.2", "diff": "^8.0.3", "file-type": "^21.3.0", - "glob": "^13.0.0", + "glob": "^13.0.1", "handlebars": "^4.7.8", "ignore": "^7.0.5", + "jsdom": "28.0.0", "marked": "^17.0.1", "nanoid": "^5.1.6", "node-html-parser": "^7.0.2", + "puppeteer": "^24.36.1", "smol-toml": "^1.6.0", "zod": "^4.3.6" }, "devDependencies": { "@types/diff": "^8.0.0", + "@types/jsdom": "27.0.0", "@types/ms": "^2.1.0", - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", "ms": "^2.1.3" }, "keywords": [ diff --git a/packages/coding-agent/src/bun-imports.d.ts b/packages/coding-agent/src/bun-imports.d.ts index e7dc00dff..996a8136e 100644 --- a/packages/coding-agent/src/bun-imports.d.ts +++ b/packages/coding-agent/src/bun-imports.d.ts @@ -20,3 +20,9 @@ declare module "*.py" { const content: string; export default content; } + +// JavaScript files imported as text +declare module "*.js?raw" { + const content: string; + export default content; +} diff --git a/packages/coding-agent/src/config/settings-schema.ts b/packages/coding-agent/src/config/settings-schema.ts index 0a501b8b0..d56f247f9 100644 --- a/packages/coding-agent/src/config/settings-schema.ts +++ b/packages/coding-agent/src/config/settings-schema.ts @@ -358,6 +358,24 @@ export const SETTINGS_SCHEMA = { description: "Enable the calculator tool for basic calculations", }, }, + "browser.enabled": { + type: "boolean", + default: false, + ui: { + tab: "tools", + label: "Enable Browser", + description: "Enable the browser tool (Ulixee Hero)", + }, + }, + "browser.headless": { + type: "boolean", + default: true, + ui: { + tab: "tools", + label: "Browser headless", + description: "Launch browser in headless mode (disable to show browser UI)", + }, + }, // ───────────────────────────────────────────────────────────────────────── // Startup settings diff --git a/packages/coding-agent/src/extensibility/extensions/wrapper.ts b/packages/coding-agent/src/extensibility/extensions/wrapper.ts index e1fbcfa5f..64ce0ad72 100644 --- a/packages/coding-agent/src/extensibility/extensions/wrapper.ts +++ b/packages/coding-agent/src/extensibility/extensions/wrapper.ts @@ -84,7 +84,7 @@ export class ExtensionToolWrapper { const target = this.tool as { restartForModeChange?: () => Promise }; diff --git a/packages/coding-agent/src/index.ts b/packages/coding-agent/src/index.ts index 68b7b5e89..24ed7ccad 100644 --- a/packages/coding-agent/src/index.ts +++ b/packages/coding-agent/src/index.ts @@ -239,6 +239,7 @@ export { // Tools (detail types and utilities) export { type BashToolDetails, + type BrowserToolDetails, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type FindOperations, diff --git a/packages/coding-agent/src/modes/controllers/input-controller.ts b/packages/coding-agent/src/modes/controllers/input-controller.ts index 35ee5400a..2bedcbf5e 100644 --- a/packages/coding-agent/src/modes/controllers/input-controller.ts +++ b/packages/coding-agent/src/modes/controllers/input-controller.ts @@ -5,6 +5,7 @@ import * as path from "node:path"; import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import { readImageFromClipboard } from "@oh-my-pi/pi-natives"; import { nanoid } from "nanoid"; +import type { SettingPath, SettingValue } from "../../config/settings"; import { settings } from "../../config/settings"; import { theme } from "../../modes/theme/theme"; import type { InteractiveModeContext } from "../../modes/types"; @@ -215,6 +216,43 @@ export class InputController { this.ctx.editor.setText(""); return; } + if (text === "/browser" || text.startsWith("/browser ")) { + const arg = text.slice(8).trim().toLowerCase(); + const current = settings.get("browser.headless" as SettingPath) as boolean; + let next = current; + if (!(settings.get("browser.enabled" as SettingPath) as boolean)) { + this.ctx.showWarning("Browser tool is disabled (enable in settings)"); + this.ctx.editor.setText(""); + return; + } + if (!arg) { + next = !current; + } else if (["headless", "hidden"].includes(arg)) { + next = true; + } else if (["visible", "show", "headful"].includes(arg)) { + next = false; + } else { + this.ctx.showStatus("Usage: /browser [headless|visible]"); + this.ctx.editor.setText(""); + return; + } + settings.set("browser.headless" as SettingPath, next as SettingValue); + const tool = this.ctx.session.getToolByName("browser"); + if (tool && "restartForModeChange" in tool) { + try { + await (tool as { restartForModeChange: () => Promise }).restartForModeChange(); + } catch (error) { + this.ctx.showWarning( + `Failed to restart browser: ${error instanceof Error ? error.message : String(error)}`, + ); + this.ctx.editor.setText(""); + return; + } + } + this.ctx.showStatus(`Browser mode: ${next ? "headless" : "visible"}`); + this.ctx.editor.setText(""); + return; + } if (text === "/copy") { await this.ctx.handleCopyCommand(); this.ctx.editor.setText(""); diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index 34f282905..afb339093 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -208,6 +208,7 @@ export class InteractiveMode implements InteractiveModeContext { { name: "export", description: "Export session to HTML file" }, { name: "dump", description: "Copy session transcript to clipboard" }, { name: "share", description: "Share session as a secret GitHub gist" }, + { name: "browser", description: "Toggle browser headless vs visible mode" }, { name: "copy", description: "Copy last agent message to clipboard" }, { name: "session", description: "Show session info and stats" }, { name: "usage", description: "Show provider usage and limits" }, diff --git a/packages/coding-agent/src/prompts/tools/browser.md b/packages/coding-agent/src/prompts/tools/browser.md new file mode 100644 index 000000000..f8fc2ae54 --- /dev/null +++ b/packages/coding-agent/src/prompts/tools/browser.md @@ -0,0 +1,26 @@ +# Browser + +Use this tool to navigate, click, type, scroll, drag, query DOM content, and capture screenshots. + + +- Use `action: "open"` to start a new headless browser session (or implicitly launch on first action) +- Use `action: "goto"` with `url` to navigate +- Use `action: "observe"` to capture a numbered accessibility snapshot with URL/title/viewport/scroll info + - Prefer `click_id`, `type_id`, or `fill_id` actions using the returned `element_id` values + - Optional flags: `include_all` to include non-interactive nodes, `viewport_only` to limit to visible elements +- Use `action: "click"`, `"type"`, `"fill"`, `"press"`, `"scroll"`, or `"drag"` for selector-based interactions + - Prefer ARIA or text selectors (e.g. `p-aria/[name="Sign in"]`, `p-text/Continue`) over brittle CSS +- Use `action: "click_id"`, `"type_id"`, or `"fill_id"` to interact with observed elements without selectors +- Use `action: "wait_for_selector"` before interacting when the page is dynamic +- Use `action: "evaluate"` with `script` to run a JavaScript expression in the page context +- Use `action: "get_text"`, `"get_html"`, or `"get_attribute"` for DOM queries + - For batch queries, pass `args: [{ selector, attribute? }]` to get an array of results (attribute required for `get_attribute`) +- Use `action: "extract_readable"` to return reader-mode content (title/byline/excerpt/text or markdown) + - Set `format` to `"text"` (default) or `"markdown"` +- Use `action: "screenshot"` to capture images (optionally with `selector` to capture a single element) +- Use `action: "close"` to release the browser when done + + + +Returns text output for navigation and DOM queries, and image output for screenshots. Screenshots can optionally be saved to disk via the `path` parameter. + \ No newline at end of file diff --git a/packages/coding-agent/src/tools/browser.ts b/packages/coding-agent/src/tools/browser.ts new file mode 100644 index 000000000..266cc5041 --- /dev/null +++ b/packages/coding-agent/src/tools/browser.ts @@ -0,0 +1,1137 @@ +import * as path from "node:path"; +import { Readability } from "@mozilla/readability"; +import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; +import { StringEnum } from "@oh-my-pi/pi-ai"; +import { logger, untilAborted } from "@oh-my-pi/pi-utils"; +import { type Static, Type } from "@sinclair/typebox"; +import { JSDOM, VirtualConsole } from "jsdom"; +import type { Browser, CDPSession, ElementHandle, KeyInput, Page, SerializedAXNode } from "puppeteer"; +import puppeteer from "puppeteer"; +import { renderPromptTemplate } from "../config/prompt-templates"; +import browserDescription from "../prompts/tools/browser.md" with { type: "text" }; +import type { ToolSession } from "../sdk"; +import { htmlToBasicMarkdown } from "../web/scrapers/types"; +import type { OutputMeta } from "./output-meta"; +import { resolveToCwd } from "./path-utils"; +import stealthTamperingScript from "./puppeteer/00_stealth_tampering.js?raw" with { type: "text" }; +import stealthActivityScript from "./puppeteer/01_stealth_activity.js?raw" with { type: "text" }; +import stealthHairlineScript from "./puppeteer/02_stealth_hairline.js?raw" with { type: "text" }; +import stealthBotdScript from "./puppeteer/03_stealth_botd.js?raw" with { type: "text" }; +import stealthIframeScript from "./puppeteer/04_stealth_iframe.js?raw" with { type: "text" }; +import stealthWebglScript from "./puppeteer/05_stealth_webgl.js?raw" with { type: "text" }; +import stealthScreenScript from "./puppeteer/06_stealth_screen.js?raw" with { type: "text" }; +import stealthFontsScript from "./puppeteer/07_stealth_fonts.js?raw" with { type: "text" }; +import stealthAudioScript from "./puppeteer/08_stealth_audio.js?raw" with { type: "text" }; +import stealthLocaleScript from "./puppeteer/09_stealth_locale.js?raw" with { type: "text" }; +import stealthPluginsScript from "./puppeteer/10_stealth_plugins.js?raw" with { type: "text" }; +import stealthHardwareScript from "./puppeteer/11_stealth_hardware.js?raw" with { type: "text" }; +import stealthCodecsScript from "./puppeteer/12_stealth_codecs.js?raw" with { type: "text" }; +import stealthWorkerScript from "./puppeteer/13_stealth_worker.js?raw" with { type: "text" }; +import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors"; +import { toolResult } from "./tool-result"; + +const DEFAULT_TIMEOUT_SECONDS = 30; +const MAX_TIMEOUT_SECONDS = 120; +const DEFAULT_VIEWPORT = { width: 1365, height: 768 }; +const STEALTH_IGNORE_DEFAULT_ARGS = [ + "--disable-extensions", + "--disable-default-apps", + "--disable-component-extensions-with-background-pages", +]; +const STEALTH_ACCEPT_LANGUAGE = "en-US,en"; +const PUPPETEER_SOURCE_URL_SUFFIX = "//# sourceURL=__puppeteer_evaluation_script__"; +const INTERACTIVE_AX_ROLES = new Set([ + "button", + "link", + "textbox", + "combobox", + "listbox", + "option", + "checkbox", + "radio", + "switch", + "tab", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "slider", + "spinbutton", + "searchbox", + "treeitem", +]); + +/** + * Stealth init scripts for Puppeteer. + */ + +type PuppeteerCdpClient = { + send: (method: string, params?: Record) => Promise; +}; + +type UserAgentOverride = { + userAgent: string; + platform: string; + acceptLanguage: string; + userAgentMetadata: { + brands: Array<{ brand: string; version: string }>; + fullVersion: string; + platform: string; + platformVersion: string; + architecture: string; + model: string; + mobile: boolean; + }; +}; + +function resolvePageClient(page: Page): PuppeteerCdpClient | null { + const pageWithClient = page as Page & { + _client?: (() => PuppeteerCdpClient) | PuppeteerCdpClient; + }; + if (!pageWithClient._client) return null; + return typeof pageWithClient._client === "function" ? pageWithClient._client() : pageWithClient._client; +} + +const puppeteerGetArgsSchema = Type.Array( + Type.Object({ + selector: Type.String({ description: "CSS selector for the target element" }), + attribute: Type.Optional(Type.String({ description: "Attribute name (get_attribute)" })), + }), + { description: "Batch arguments for get_* actions", minItems: 1 }, +); + +const browserSchema = Type.Object({ + action: StringEnum( + [ + "open", + "goto", + "observe", + "click", + "click_id", + "type", + "type_id", + "fill", + "fill_id", + "press", + "scroll", + "drag", + "wait_for_selector", + "evaluate", + "get_text", + "get_html", + "get_attribute", + "extract_readable", + "screenshot", + "close", + ], + { description: "Action to perform" }, + ), + url: Type.Optional(Type.String({ description: "URL to navigate to (goto)" })), + selector: Type.Optional(Type.String({ description: "CSS selector for the target element" })), + element_id: Type.Optional(Type.Number({ description: "Element ID from observe" })), + include_all: Type.Optional(Type.Boolean({ description: "Include non-interactive nodes in observe" })), + viewport_only: Type.Optional(Type.Boolean({ description: "Limit observe output to elements in the viewport" })), + args: Type.Optional(puppeteerGetArgsSchema), + script: Type.Optional(Type.String({ description: "JavaScript to evaluate (evaluate)" })), + text: Type.Optional(Type.String({ description: "Text to type (type)" })), + value: Type.Optional(Type.String({ description: "Value to set (fill)" })), + attribute: Type.Optional(Type.String({ description: "Attribute name to read (get_attribute)" })), + key: Type.Optional(Type.String({ description: "Keyboard key to press (press)" })), + timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30)" })), + wait_until: Type.Optional( + StringEnum(["load", "domcontentloaded", "networkidle0", "networkidle2"], { + description: "Navigation wait condition (goto)", + }), + ), + full_page: Type.Optional(Type.Boolean({ description: "Capture full page screenshot (screenshot)" })), + format: Type.Optional( + StringEnum(["png", "jpeg", "text", "markdown"], { + description: "Output format (screenshot: png/jpeg, extract_readable: text/markdown)", + }), + ), + quality: Type.Optional(Type.Number({ description: "JPEG quality 0-100 (screenshot)" })), + path: Type.Optional(Type.String({ description: "Optional path to save screenshot (relative to cwd)" })), + viewport: Type.Optional( + Type.Object({ + width: Type.Number({ description: "Viewport width in pixels" }), + height: Type.Number({ description: "Viewport height in pixels" }), + deviceScaleFactor: Type.Optional(Type.Number({ description: "Device scale factor" })), + }), + ), + delta_x: Type.Optional(Type.Number({ description: "Scroll delta X (scroll)" })), + delta_y: Type.Optional(Type.Number({ description: "Scroll delta Y (scroll)" })), + from_selector: Type.Optional(Type.String({ description: "Drag start selector (drag)" })), + to_selector: Type.Optional(Type.String({ description: "Drag end selector (drag)" })), +}); + +/** Input schema for the Puppeteer tool. */ +export type BrowserParams = Static; + +/** Details describing a Puppeteer tool execution result. */ +export interface BrowserToolDetails { + action: BrowserParams["action"]; + url?: string; + selector?: string; + elementId?: number; + result?: string | string[]; + screenshotPath?: string; + mimeType?: string; + bytes?: number; + viewport?: { width: number; height: number; deviceScaleFactor?: number }; + observation?: Observation; + readable?: ReadableResult; + meta?: OutputMeta; +} + +export interface ObservationEntry { + id: number; + role: string; + name?: string; + value?: string | number; + description?: string; + keyshortcuts?: string; + states: string[]; +} + +export interface Observation { + url: string; + title?: string; + viewport: { width: number; height: number; deviceScaleFactor?: number }; + scroll: { + x: number; + y: number; + width: number; + height: number; + scrollWidth: number; + scrollHeight: number; + }; + elements: ObservationEntry[]; +} + +export interface ReadableResult { + url: string; + title?: string; + byline?: string; + excerpt?: string; + contentLength: number; + text?: string; + markdown?: string; +} + +function clampTimeout(timeoutSeconds?: number): number { + if (timeoutSeconds === undefined) return DEFAULT_TIMEOUT_SECONDS; + return Math.min(Math.max(timeoutSeconds, 1), MAX_TIMEOUT_SECONDS); +} + +function clampQuality(quality?: number): number | undefined { + if (quality === undefined) return undefined; + return Math.min(Math.max(quality, 0), 100); +} + +function ensureParam(value: T | undefined, name: string, action: string): T { + if (value === undefined || value === null || value === "") { + throw new ToolError(`Missing required parameter '${name}' for action '${action}'.`); + } + return value; +} + +function resolveArtifactsDir(session: ToolSession): string | null { + return session.getArtifactsDir?.() ?? session.getSessionFile()?.slice(0, -6) ?? null; +} + +function formatEvaluateResult(value: unknown): string { + if (typeof value === "string") return value; + if (value === undefined) return "undefined"; + try { + const serialized = JSON.stringify(value, null, 2); + return serialized ?? "undefined"; + } catch { + return String(value); + } +} + +/** + * Puppeteer tool for headless browser automation. + */ +export class BrowserTool implements AgentTool { + public readonly name = "puppeteer"; + public readonly label = "Puppeteer"; + public readonly description: string; + public readonly parameters = browserSchema; + private readonly session: ToolSession; + private browser: Browser | null = null; + private page: Page | null = null; + private currentHeadless: boolean | null = null; + private browserSession: CDPSession | null = null; + private userAgentOverride: UserAgentOverride | null = null; + private elementIdCounter = 0; + private readonly elementCache = new Map(); + private readonly patchedClients = new WeakSet(); + + constructor(session: ToolSession) { + this.session = session; + this.description = renderPromptTemplate(browserDescription, {}); + } + + private async closeBrowser(): Promise { + await this.clearElementCache(); + if (this.page && !this.page.isClosed()) { + await this.page.close(); + } + this.page = null; + if (this.browser?.connected) { + await this.browser.close(); + } + this.browser = null; + this.browserSession = null; + this.userAgentOverride = null; + } + + private async resetBrowser(params?: BrowserParams): Promise { + await this.closeBrowser(); + this.currentHeadless = this.session.settings.get("browser.headless"); + this.browser = await puppeteer.launch({ + headless: this.currentHeadless, + args: [ + "--no-sandbox", + "--disable-setuid-sandbox", + "--disable-blink-features=AutomationControlled", + `--window-size=${DEFAULT_VIEWPORT.width},${DEFAULT_VIEWPORT.height}`, + ], + ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS], + }); + this.page = await this.browser.newPage(); + await this.applyStealthPatches(this.page); + await this.applyViewport(this.page, params?.viewport); + return this.page; + } + + private async ensurePage(params?: BrowserParams): Promise { + const desiredHeadless = this.session.settings.get("browser.headless"); + if (this.currentHeadless !== null && this.currentHeadless !== desiredHeadless) { + return this.resetBrowser(params); + } + if (this.page && !this.page.isClosed()) { + return this.page; + } + if (!this.browser || !this.browser.isConnected()) { + return this.resetBrowser(params); + } + this.page = await this.browser.newPage(); + await this.applyStealthPatches(this.page); + await this.applyViewport(this.page, params?.viewport); + return this.page; + } + + private async applyViewport(page: Page, viewport?: BrowserParams["viewport"]): Promise { + const target = viewport ?? DEFAULT_VIEWPORT; + await page.setViewport(target); + } + + private async clearElementCache(): Promise { + if (this.elementCache.size === 0) { + this.elementIdCounter = 0; + return; + } + const handles = Array.from(this.elementCache.values()); + this.elementCache.clear(); + this.elementIdCounter = 0; + await Promise.all( + handles.map(async handle => { + try { + await handle.dispose(); + } catch { + return; + } + }), + ); + } + + private async resolveCachedHandle(id: number): Promise { + const handle = this.elementCache.get(id); + if (!handle) { + throw new ToolError(`Unknown element_id ${id}. Run observe to refresh the element list.`); + } + try { + const isConnected = (await handle.evaluate(el => el.isConnected)) as boolean; + if (!isConnected) { + await this.clearElementCache(); + throw new ToolError(`Element_id ${id} is stale. Run observe again.`); + } + } catch { + await this.clearElementCache(); + throw new ToolError(`Element_id ${id} is stale. Run observe again.`); + } + return handle; + } + + private isInteractiveNode(node: SerializedAXNode): boolean { + if (INTERACTIVE_AX_ROLES.has(node.role)) return true; + return ( + node.checked !== undefined || + node.pressed !== undefined || + node.selected !== undefined || + node.expanded !== undefined || + node.focused === true + ); + } + + private async collectObservationEntries( + node: SerializedAXNode, + entries: ObservationEntry[], + options: { viewportOnly: boolean; includeAll: boolean }, + ): Promise { + if (options.includeAll || this.isInteractiveNode(node)) { + const handle = await node.elementHandle(); + if (handle) { + let inViewport = true; + if (options.viewportOnly) { + try { + inViewport = await handle.isIntersectingViewport(); + } catch { + inViewport = false; + } + } + if (inViewport) { + const id = ++this.elementIdCounter; + const states: string[] = []; + if (node.disabled) states.push("disabled"); + if (node.checked !== undefined) states.push(`checked=${String(node.checked)}`); + if (node.pressed !== undefined) states.push(`pressed=${String(node.pressed)}`); + if (node.selected !== undefined) states.push(`selected=${String(node.selected)}`); + if (node.expanded !== undefined) states.push(`expanded=${String(node.expanded)}`); + if (node.required) states.push("required"); + if (node.readonly) states.push("readonly"); + if (node.multiselectable) states.push("multiselectable"); + if (node.multiline) states.push("multiline"); + if (node.modal) states.push("modal"); + if (node.focused) states.push("focused"); + this.elementCache.set(id, handle); + entries.push({ + id, + role: node.role, + name: node.name, + value: node.value, + description: node.description, + keyshortcuts: node.keyshortcuts, + states, + }); + } else { + await handle.dispose(); + } + } + } + for (const child of node.children ?? []) { + await this.collectObservationEntries(child, entries, options); + } + } + + private formatObservation(observation: Observation): string { + const viewport = `${observation.viewport.width}x${observation.viewport.height}`; + const scroll = `x=${observation.scroll.x} y=${observation.scroll.y} viewport=${observation.scroll.width}x${observation.scroll.height} doc=${observation.scroll.scrollWidth}x${observation.scroll.scrollHeight}`; + const lines = [ + `URL: ${observation.url}`, + observation.title ? `Title: ${observation.title}` : "Title:", + `Viewport: ${viewport}`, + `Scroll: ${scroll}`, + "Elements:", + ]; + for (const entry of observation.elements) { + const name = entry.name ? ` "${entry.name}"` : ""; + const value = entry.value !== undefined ? ` value=${JSON.stringify(entry.value)}` : ""; + const description = entry.description ? ` desc=${JSON.stringify(entry.description)}` : ""; + const shortcuts = entry.keyshortcuts ? ` shortcuts=${JSON.stringify(entry.keyshortcuts)}` : ""; + const state = entry.states.length ? ` (${entry.states.join(", ")})` : ""; + lines.push(`${entry.id}. ${entry.role}${name}${value}${description}${shortcuts}${state}`); + } + return lines.join("\n"); + } + + /** + * Restart the browser to apply changes like headless mode. + */ + public async restartForModeChange(): Promise { + await this.resetBrowser(); + } + + private async applyStealthPatches(page: Page): Promise { + this.patchSourceUrl(page); + await this.applyUserAgentOverride(page); + await this.injectStealthScripts(page); + } + + private async applyUserAgentOverride(page: Page): Promise { + const client = resolvePageClient(page); + if (!client) return; + const override = await this.resolveUserAgentOverride(page); + await this.sendUserAgentOverride(client, override); + await this.configureUserAgentTargets(override); + } + + private async resolveUserAgentOverride(page: Page): Promise { + if (this.userAgentOverride) return this.userAgentOverride; + const rawUserAgent = await page.browser().userAgent(); + let userAgent = rawUserAgent.replace("HeadlessChrome/", "Chrome/"); + if (userAgent.includes("Linux") && !userAgent.includes("Android")) { + userAgent = userAgent.replace(/\(([^)]+)\)/, "(Windows NT 10.0; Win64; x64)"); + } + + const uaVersionMatch = userAgent.match(/Chrome\/([\d|.]+)/); + const fallbackVersionMatch = uaVersionMatch ?? (await page.browser().version()).match(/\/([\d|.]+)/); + const uaVersion = fallbackVersionMatch?.[1] ?? "0"; + const majorVersion = Number.parseInt(uaVersion.split(".")[0] ?? "0", 10) || 0; + const isAndroid = userAgent.includes("Android"); + const platform = userAgent.includes("Mac OS X") + ? "MacIntel" + : isAndroid + ? "Android" + : userAgent.includes("Linux") + ? "Linux" + : "Win32"; + const platformFull = userAgent.includes("Mac OS X") + ? "Mac OS X" + : isAndroid + ? "Android" + : userAgent.includes("Linux") + ? "Linux" + : "Windows"; + const platformVersion = userAgent.includes("Mac OS X ") + ? (userAgent.match(/Mac OS X ([^)]+)/)?.[1] ?? "") + : userAgent.includes("Android ") + ? (userAgent.match(/Android ([^;]+)/)?.[1] ?? "") + : userAgent.includes("Windows ") + ? (userAgent.match(/Windows .*?([\d|.]+);?/)?.[1] ?? "") + : ""; + const architecture = isAndroid ? "" : "x86"; + const model = isAndroid ? (userAgent.match(/Android.*?;\s([^)]+)/)?.[1] ?? "") : ""; + + const brandOrders = [ + [0, 1, 2], + [0, 2, 1], + [1, 0, 2], + [1, 2, 0], + [2, 0, 1], + [2, 1, 0], + ]; + const order = brandOrders[majorVersion % brandOrders.length] ?? brandOrders[0]; + const escapedChars = [" ", " ", ";"]; + const greaseyBrand = `${escapedChars[order[0]]}Not${escapedChars[order[1]]}A${escapedChars[order[2]]}Brand`; + const brands: { brand: string; version: string }[] = []; + brands[order[0]] = { brand: greaseyBrand, version: "99" }; + brands[order[1]] = { brand: "Chromium", version: String(majorVersion) }; + brands[order[2]] = { brand: "Google Chrome", version: String(majorVersion) }; + + this.userAgentOverride = { + userAgent, + platform, + acceptLanguage: STEALTH_ACCEPT_LANGUAGE, + userAgentMetadata: { + brands, + fullVersion: uaVersion, + platform: platformFull, + platformVersion, + architecture, + model, + mobile: isAndroid, + }, + }; + return this.userAgentOverride; + } + + private async configureUserAgentTargets(override: UserAgentOverride): Promise { + if (!this.browser) return; + if (!this.browserSession) { + this.browserSession = await this.browser.target().createCDPSession(); + await this.browserSession.send("Target.setAutoAttach", { + autoAttach: true, + waitForDebuggerOnStart: false, + flatten: true, + }); + this.browserSession.on("Target.attachedToTarget", async (event: { sessionId: string }) => { + const connection = this.browserSession?.connection(); + const session = connection?.session(event.sessionId); + if (!session || !this.userAgentOverride) return; + await this.sendUserAgentOverride(this.wrapSession(session), this.userAgentOverride); + }); + } + + const targets = this.browser.targets(); + await Promise.all( + targets.map(async target => { + const session = await target.createCDPSession(); + await this.sendUserAgentOverride(this.wrapSession(session), override); + }), + ); + } + + private wrapSession(session: CDPSession): PuppeteerCdpClient { + return { + send: async (method, params) => session.send(method as never, params as never), + }; + } + + private async sendUserAgentOverride(client: PuppeteerCdpClient, override: UserAgentOverride): Promise { + try { + await client.send("Network.enable"); + } catch {} + try { + await client.send("Network.setUserAgentOverride", override); + } catch (error) { + logger.debug("Failed to apply Network user agent override", { + error: error instanceof Error ? error.message : String(error), + }); + } + try { + await client.send("Emulation.setUserAgentOverride", override); + } catch (error) { + logger.debug("Failed to apply Emulation user agent override", { + error: error instanceof Error ? error.message : String(error), + }); + } + } + + private patchSourceUrl(page: Page): void { + const client = resolvePageClient(page); + if (!client) return; + const clientKey = client as object; + if (this.patchedClients.has(clientKey)) return; + this.patchedClients.add(clientKey); + const originalSend = client.send.bind(client); + client.send = async (method: string, params?: Record) => { + const next = async (payload?: Record) => { + try { + return await originalSend(method, payload); + } catch (error) { + if ( + error instanceof Error && + error.message.includes( + "Protocol error (Network.getResponseBody): No resource with given identifier found", + ) + ) { + return undefined; + } + throw error; + } + }; + if (!method || !params) { + return next(params); + } + const key = + method === "Runtime.evaluate" + ? "expression" + : method === "Runtime.callFunctionOn" + ? "functionDeclaration" + : null; + if (!key) { + return next(params); + } + const value = params[key]; + if (typeof value !== "string" || !value.includes(PUPPETEER_SOURCE_URL_SUFFIX)) { + return next(params); + } + const patchedParams = { ...params, [key]: value.replace(PUPPETEER_SOURCE_URL_SUFFIX, "") }; + return next(patchedParams); + }; + } + + /** Injects stealth scripts that cover common puppeteer detection surfaces. */ + private async injectStealthScripts(page: Page): Promise { + const scripts = [ + stealthTamperingScript, + stealthActivityScript, + stealthHairlineScript, + stealthBotdScript, + stealthIframeScript, + stealthWebglScript, + stealthScreenScript, + stealthFontsScript, + stealthAudioScript, + stealthLocaleScript, + stealthPluginsScript, + stealthHardwareScript, + stealthCodecsScript, + stealthWorkerScript, + ]; + + const joint = scripts + .map( + script => ` + try { + ${script}; + } catch (e) {} + `, + ) + .join(";\n"); + + await page.evaluateOnNewDocument(`(() => { + // Native function cache - captured before any tampering + const iframe = document.createElement("iframe"); + iframe.style.display = "none"; + document.head.appendChild(iframe); + const nativeWindow = iframe.contentWindow; + if (!nativeWindow) return; + + // Cache pristine native functions + const Function_toString = nativeWindow.Function.prototype.toString; + const Object_getOwnPropertyDescriptor = nativeWindow.Object.getOwnPropertyDescriptor; + const Object_getOwnPropertyDescriptors = nativeWindow.Object.getOwnPropertyDescriptors; + const Object_getPrototypeOf = nativeWindow.Object.getPrototypeOf; + const Object_defineProperty = nativeWindow.Object.defineProperty; + const Object_getOwnPropertyDescriptorOriginal = nativeWindow.Object.getOwnPropertyDescriptor; + const Object_create = nativeWindow.Object.create; + const Object_keys = nativeWindow.Object.keys; + const Object_getOwnPropertyNames = nativeWindow.Object.getOwnPropertyNames; + const Object_entries = nativeWindow.Object.entries; + const Object_setPrototypeOf = nativeWindow.Object.setPrototypeOf; + const Object_assign = nativeWindow.Object.assign; + const Window_setTimeout = nativeWindow.setTimeout; + const Math_random = nativeWindow.Math.random; + const Math_floor = nativeWindow.Math.floor; + const Math_max = nativeWindow.Math.max; + const Math_min = nativeWindow.Math.min; + const Window_Event = nativeWindow.Event; + const Promise_resolve = nativeWindow.Promise.resolve.bind(nativeWindow.Promise); + const Window_Blob = nativeWindow.Blob; + const Window_Proxy = nativeWindow.Proxy; + const Intl_DateTimeFormat = nativeWindow.Intl.DateTimeFormat; + const Date_constructor = nativeWindow.Date; + + + ${joint} + + document.head.removeChild(iframe);})();`); + } + + public async execute( + _toolCallId: string, + params: BrowserParams, + signal?: AbortSignal, + _onUpdate?: AgentToolUpdateCallback, + _context?: AgentToolContext, + ): Promise> { + try { + throwIfAborted(signal); + const timeoutSeconds = clampTimeout(params.timeout); + const timeoutMs = timeoutSeconds * 1000; + const details: BrowserToolDetails = { action: params.action }; + + switch (params.action) { + case "open": { + const page = await untilAborted(signal, () => this.resetBrowser(params)); + const viewport = page.viewport(); + details.viewport = viewport ?? DEFAULT_VIEWPORT; + return toolResult(details).text("Opened headless browser session").done(); + } + case "close": { + await untilAborted(signal, () => this.closeBrowser()); + return toolResult(details).text("Closed headless browser session").done(); + } + case "goto": { + const url = ensureParam(params.url, "url", params.action); + details.url = url; + const page = await this.ensurePage(params); + const waitUntil = params.wait_until ?? "networkidle2"; + await this.clearElementCache(); + await untilAborted(signal, () => page.goto(url, { waitUntil, timeout: timeoutMs })); + const finalUrl = page.url(); + const title = (await untilAborted(signal, () => page.title())) as string; + details.url = finalUrl; + details.result = title; + return toolResult(details) + .text(`Navigated to ${finalUrl}${title ? `\nTitle: ${title}` : ""}`) + .done(); + } + case "observe": { + const page = await this.ensurePage(params); + const timeoutSignal = AbortSignal.timeout(timeoutMs); + const observeSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal; + await this.clearElementCache(); + const snapshot = (await untilAborted(observeSignal, () => + page.accessibility.snapshot({ interestingOnly: !(params.include_all ?? false) }), + )) as SerializedAXNode | null; + if (!snapshot) { + throw new ToolError("Accessibility snapshot unavailable"); + } + const entries: ObservationEntry[] = []; + await this.collectObservationEntries(snapshot, entries, { + viewportOnly: params.viewport_only ?? false, + includeAll: params.include_all ?? false, + }); + const scroll = (await untilAborted(observeSignal, () => + page.evaluate(() => { + const win = globalThis as unknown as { + scrollX: number; + scrollY: number; + innerWidth: number; + innerHeight: number; + document: { documentElement: { scrollWidth: number; scrollHeight: number } }; + }; + const doc = win.document.documentElement; + return { + x: win.scrollX, + y: win.scrollY, + width: win.innerWidth, + height: win.innerHeight, + scrollWidth: doc.scrollWidth, + scrollHeight: doc.scrollHeight, + }; + }), + )) as Observation["scroll"]; + const url = page.url(); + const title = (await untilAborted(observeSignal, () => page.title())) as string; + const viewport = page.viewport() ?? DEFAULT_VIEWPORT; + const observation: Observation = { + url, + title, + viewport, + scroll, + elements: entries, + }; + details.url = url; + details.viewport = viewport; + details.observation = observation; + details.result = `${entries.length} elements`; + return toolResult(details).text(this.formatObservation(observation)).done(); + } + case "click": { + const selector = ensureParam(params.selector, "selector", params.action); + details.selector = selector; + const page = await this.ensurePage(params); + const locator = page.locator(selector).setTimeout(timeoutMs); + await untilAborted(signal, () => locator.click()); + return toolResult(details).text(`Clicked ${selector}`).done(); + } + case "click_id": { + const elementId = ensureParam(params.element_id, "element_id", params.action); + details.elementId = elementId; + const handle = await this.resolveCachedHandle(elementId); + try { + await untilAborted(signal, () => handle.click()); + } catch { + await this.clearElementCache(); + throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`); + } + return toolResult(details).text(`Clicked element ${elementId}`).done(); + } + case "type": { + const selector = ensureParam(params.selector, "selector", params.action); + const text = ensureParam(params.text, "text", params.action); + details.selector = selector; + const page = await this.ensurePage(params); + const locator = page.locator(selector).setTimeout(timeoutMs); + const handle = (await untilAborted(signal, () => locator.waitHandle())) as ElementHandle; + await untilAborted(signal, () => handle.type(text, { delay: 0 })); + await handle.dispose(); + return toolResult(details).text(`Typed into ${selector}`).done(); + } + case "type_id": { + const elementId = ensureParam(params.element_id, "element_id", params.action); + const text = ensureParam(params.text, "text", params.action); + details.elementId = elementId; + const page = await this.ensurePage(params); + const handle = await this.resolveCachedHandle(elementId); + try { + await untilAborted(signal, () => handle.focus()); + await untilAborted(signal, () => page.keyboard.type(text, { delay: 0 })); + } catch { + await this.clearElementCache(); + throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`); + } + return toolResult(details).text(`Typed into element ${elementId}`).done(); + } + case "fill": { + const selector = ensureParam(params.selector, "selector", params.action); + const value = ensureParam(params.value, "value", params.action); + details.selector = selector; + const page = await this.ensurePage(params); + const locator = page.locator(selector).setTimeout(timeoutMs); + await untilAborted(signal, () => locator.fill(value)); + return toolResult(details).text(`Filled ${selector}`).done(); + } + case "fill_id": { + const elementId = ensureParam(params.element_id, "element_id", params.action); + const value = ensureParam(params.value, "value", params.action); + details.elementId = elementId; + const handle = await this.resolveCachedHandle(elementId); + try { + await untilAborted(signal, () => + handle.evaluate((el, inputValue) => { + const element = el as { value?: string; dispatchEvent: (event: Event) => boolean }; + if (!("value" in element)) { + throw new Error("Target element is not a form input"); + } + element.value = String(inputValue); + element.dispatchEvent(new Event("input", { bubbles: true })); + element.dispatchEvent(new Event("change", { bubbles: true })); + }, value), + ); + } catch { + await this.clearElementCache(); + throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`); + } + return toolResult(details).text(`Filled element ${elementId}`).done(); + } + case "press": { + const key = ensureParam(params.key, "key", params.action) as KeyInput; + const page = await this.ensurePage(params); + if (params.selector) { + await untilAborted(signal, () => page.focus(params.selector as string)); + } + await untilAborted(signal, () => page.keyboard.press(key)); + return toolResult(details).text(`Pressed ${key}`).done(); + } + case "scroll": { + const deltaY = ensureParam(params.delta_y, "delta_y", params.action); + const deltaX = params.delta_x ?? 0; + const page = await this.ensurePage(params); + await untilAborted(signal, () => page.mouse.wheel({ deltaX, deltaY })); + return toolResult(details).text(`Scrolled by ${deltaX}, ${deltaY}`).done(); + } + case "drag": { + const fromSelector = ensureParam(params.from_selector, "from_selector", params.action); + const toSelector = ensureParam(params.to_selector, "to_selector", params.action); + const page = await this.ensurePage(params); + const fromHandle = (await untilAborted(signal, () => page.$(fromSelector))) as ElementHandle | null; + const toHandle = (await untilAborted(signal, () => page.$(toSelector))) as ElementHandle | null; + if (!fromHandle || !toHandle) { + throw new ToolError("Drag selectors did not resolve to elements"); + } + const fromBox = (await untilAborted(signal, () => fromHandle.boundingBox())) as { + x: number; + y: number; + width: number; + height: number; + } | null; + const toBox = (await untilAborted(signal, () => toHandle.boundingBox())) as { + x: number; + y: number; + width: number; + height: number; + } | null; + await fromHandle.dispose(); + await toHandle.dispose(); + if (!fromBox || !toBox) { + throw new ToolError("Drag elements are not visible"); + } + const startX = fromBox.x + fromBox.width / 2; + const startY = fromBox.y + fromBox.height / 2; + const endX = toBox.x + toBox.width / 2; + const endY = toBox.y + toBox.height / 2; + await untilAborted(signal, () => page.mouse.move(startX, startY)); + await untilAborted(signal, () => page.mouse.down()); + await untilAborted(signal, () => page.mouse.move(endX, endY, { steps: 12 })); + await untilAborted(signal, () => page.mouse.up()); + return toolResult(details).text(`Dragged from ${fromSelector} to ${toSelector}`).done(); + } + case "wait_for_selector": { + const selector = ensureParam(params.selector, "selector", params.action); + details.selector = selector; + const page = await this.ensurePage(params); + const locator = page.locator(selector).setTimeout(timeoutMs); + await untilAborted(signal, () => locator.wait()); + return toolResult(details).text(`Selector ready: ${selector}`).done(); + } + case "evaluate": { + const script = ensureParam(params.script, "script", params.action); + const page = await this.ensurePage(params); + const value = (await untilAborted(signal, () => + page.evaluate((source: string) => { + const evaluator = new Function(`return (${source});`); + return evaluator(); + }, script), + )) as unknown; + const output = formatEvaluateResult(value); + details.result = output; + return toolResult(details).text(output).done(); + } + case "get_text": { + const page = await this.ensurePage(params); + if (params.args?.length) { + const values = (await Promise.all( + params.args.map((arg, index) => { + const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action); + return untilAborted(signal, () => + page.$eval(selector, (el: Element) => (el as HTMLElement).innerText), + ); + }), + )) as string[]; + details.result = values; + return toolResult(details) + .text(JSON.stringify(values, null, 2)) + .done(); + } + const selector = ensureParam(params.selector, "selector", params.action); + details.selector = selector; + const value = (await untilAborted(signal, () => + page.$eval(selector, (el: Element) => (el as HTMLElement).innerText), + )) as string; + details.result = value; + return toolResult(details).text(value).done(); + } + case "get_html": { + const page = await this.ensurePage(params); + if (params.args?.length) { + const values = (await Promise.all( + params.args.map((arg, index) => { + const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action); + return untilAborted(signal, () => + page.$eval(selector, (el: Element) => (el as HTMLElement).innerHTML), + ); + }), + )) as string[]; + details.result = values; + return toolResult(details) + .text(JSON.stringify(values, null, 2)) + .done(); + } + const selector = ensureParam(params.selector, "selector", params.action); + details.selector = selector; + const value = (await untilAborted(signal, () => + page.$eval(selector, (el: Element) => (el as HTMLElement).innerHTML), + )) as string; + details.result = value; + return toolResult(details).text(value).done(); + } + case "get_attribute": { + const page = await this.ensurePage(params); + if (params.args?.length) { + const values = (await Promise.all( + params.args.map((arg, index) => { + const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action); + const attribute = ensureParam(arg.attribute, `args[${index}].attribute`, params.action); + return untilAborted(signal, () => + page.$eval(selector, (el: Element) => (el as HTMLElement).getAttribute(String(attribute))), + ); + }), + )) as string[]; + details.result = values; + return toolResult(details) + .text(JSON.stringify(values, null, 2)) + .done(); + } + const selector = ensureParam(params.selector, "selector", params.action); + const attribute = ensureParam(params.attribute, "attribute", params.action); + details.selector = selector; + const value = (await untilAborted(signal, () => + page.$eval( + selector, + (el: { getAttribute: (name: string) => string | null }, attr: string) => + el.getAttribute(String(attr)), + attribute, + ), + )) as string | null; + const output = value ?? ""; + details.result = output; + return toolResult(details).text(output).done(); + } + case "extract_readable": { + const page = await this.ensurePage(params); + const format = params.format ?? "text"; + if (format !== "text" && format !== "markdown") { + throw new ToolError("extract_readable format must be text or markdown"); + } + const html = (await untilAborted(signal, () => page.content())) as string; + const url = page.url(); + const virtualConsole = new VirtualConsole(); + virtualConsole.on("jsdomError", error => { + if (error?.message?.includes("Could not parse CSS stylesheet")) return; + logger.debug("JSDOM error during readable extraction", { + error: error instanceof Error ? error.message : String(error), + }); + }); + const dom = new JSDOM(html, { url, virtualConsole }); + const reader = new Readability(dom.window.document); + const article = reader.parse(); + dom.window.close(); + if (!article) { + throw new ToolError("Readable content not found"); + } + const markdown = format === "markdown" ? htmlToBasicMarkdown(article.content ?? "") : undefined; + const text = format === "text" ? (article.textContent ?? "") : undefined; + const readable: ReadableResult = { + url, + title: article.title ?? undefined, + byline: article.byline ?? undefined, + excerpt: article.excerpt ?? undefined, + contentLength: article.length ?? article.textContent?.length ?? 0, + text, + markdown, + }; + details.url = url; + details.readable = readable; + details.result = format === "markdown" ? (markdown ?? "") : (text ?? ""); + return toolResult(details) + .text(JSON.stringify(readable, null, 2)) + .done(); + } + case "screenshot": { + const page = await this.ensurePage(params); + const format = params.format ?? "png"; + if (format !== "png" && format !== "jpeg") { + throw new ToolError("Screenshot format must be png or jpeg"); + } + const imageFormat = format; + const fullPage = params.selector ? false : (params.full_page ?? false); + const quality = imageFormat === "jpeg" ? clampQuality(params.quality ?? 80) : undefined; + let buffer: Buffer; + + if (params.selector) { + const handle = (await untilAborted(signal, () => + page.$(params.selector as string), + )) as ElementHandle | null; + if (!handle) { + throw new ToolError("Screenshot selector did not resolve to an element"); + } + buffer = (await untilAborted(signal, () => + handle.screenshot({ type: imageFormat, quality }), + )) as Buffer; + await handle.dispose(); + details.selector = params.selector; + } else { + buffer = (await untilAborted(signal, () => + page.screenshot({ type: imageFormat, quality, fullPage }), + )) as Buffer; + } + + const mimeType = imageFormat === "png" ? "image/png" : "image/jpeg"; + const base64 = buffer.toString("base64"); + let savedPath: string | undefined; + if (params.path) { + const resolved = resolveToCwd(params.path, this.session.cwd); + const ext = path.extname(resolved); + savedPath = ext ? resolved : `${resolved}.${imageFormat}`; + await Bun.write(savedPath, buffer); + } else { + const artifactsDir = resolveArtifactsDir(this.session); + if (artifactsDir) { + savedPath = path.join(artifactsDir, `puppeteer-${Date.now()}.${imageFormat}`); + await Bun.write(savedPath, buffer); + } + } + details.screenshotPath = savedPath; + details.mimeType = mimeType; + details.bytes = buffer.length; + + const lines = ["Screenshot captured", `Format: ${format}`, `Bytes: ${buffer.length}`]; + if (savedPath) { + lines.push(`Saved: ${savedPath}`); + } + + return toolResult(details) + .content([ + { type: "text", text: lines.join("\n") }, + { type: "image", data: base64, mimeType }, + ]) + .done(); + } + default: + throw new ToolError(`Unsupported action: ${params.action}`); + } + } catch (error) { + if (error instanceof ToolAbortError) throw error; + if (error instanceof Error && error.name === "AbortError") { + throw new ToolAbortError(); + } + throw error; + } + } +} diff --git a/packages/coding-agent/src/tools/index.ts b/packages/coding-agent/src/tools/index.ts index ebe6f89bc..110615510 100644 --- a/packages/coding-agent/src/tools/index.ts +++ b/packages/coding-agent/src/tools/index.ts @@ -17,6 +17,7 @@ import { time } from "../utils/timings"; import { WebSearchTool } from "../web/search"; import { AskTool } from "./ask"; import { BashTool } from "./bash"; +import { BrowserTool } from "./browser"; import { CalculatorTool } from "./calculator"; import { ExitPlanModeTool } from "./exit-plan-mode"; import { FetchTool } from "./fetch"; @@ -69,6 +70,7 @@ export { } from "../web/search"; export { AskTool, type AskToolDetails } from "./ask"; export { BashTool, type BashToolDetails, type BashToolOptions } from "./bash"; +export { BrowserTool, type BrowserToolDetails } from "./browser"; export { CalculatorTool, type CalculatorToolDetails } from "./calculator"; export { type ExitPlanModeDetails, ExitPlanModeTool } from "./exit-plan-mode"; export { FetchTool, type FetchToolDetails } from "./fetch"; @@ -171,6 +173,7 @@ export const BUILTIN_TOOLS: Record = { lsp: LspTool.createIf, notebook: s => new NotebookTool(s), read: s => new ReadTool(s), + browser: s => new BrowserTool(s), task: TaskTool.create, todo_write: s => new TodoWriteTool(s), fetch: s => new FetchTool(s), @@ -283,6 +286,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P if (name === "web_search") return session.settings.get("web_search.enabled"); if (name === "lsp") return session.settings.get("lsp.enabled"); if (name === "calc") return session.settings.get("calc.enabled"); + if (name === "browser") return session.settings.get("browser.enabled"); return true; }; if (includeSubmitResult && requestedTools && !requestedTools.includes("submit_result")) { diff --git a/packages/coding-agent/src/tools/puppeteer/00_stealth_tampering.js b/packages/coding-agent/src/tools/puppeteer/00_stealth_tampering.js new file mode 100644 index 000000000..bdc87d9c5 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/00_stealth_tampering.js @@ -0,0 +1,63 @@ +// Helper to generate native code string +const makeNativeString = (name) => + "function " + (name || "") + "() { [native code] }"; + +// Patch toString for common fingerprinted functions +const patchedFns = [ + [window.alert, "alert"], + [window.prompt, "prompt"], + [window.confirm, "confirm"], + [window.fetch, "fetch"], + [window.XMLHttpRequest, "XMLHttpRequest"], + [window.WebSocket, "WebSocket"], + [window.localStorage?.getItem, "getItem"], + [window.localStorage?.setItem, "setItem"], + [window.navigator.geolocation?.getCurrentPosition, "getCurrentPosition"], + [window.navigator.geolocation?.watchPosition, "watchPosition"], + [window.CanvasRenderingContext2D?.prototype.getImageData, "getImageData"], + [window.HTMLCanvasElement?.prototype.toDataURL, "toDataURL"], + [window.HTMLCanvasElement?.prototype.toBlob, "toBlob"], +]; + +for (const [fn, name] of patchedFns) { + if (typeof fn === "function") { + const nativeStr = makeNativeString(name); + Object_defineProperty(fn, "toString", { + value: function toString() { return nativeStr; }, + writable: false, + configurable: true, + enumerable: false, + }); + } +} + +// Patch Object.getOwnPropertyDescriptor to return native-looking descriptors +Object.getOwnPropertyDescriptor = function (obj, prop) { + const descriptor = Object_getOwnPropertyDescriptor.call(this, obj, prop); + if (!descriptor) return descriptor; + + // Make patched descriptors look native + if (descriptor.get && typeof descriptor.get === "function") { + const getStr = makeNativeString("get " + String(prop)); + Object_defineProperty(descriptor.get, "toString", { + value: function toString() { return getStr; }, + writable: false, + configurable: true, + enumerable: false, + }); + } + if (descriptor.set && typeof descriptor.set === "function") { + const setStr = makeNativeString("set " + String(prop)); + Object_defineProperty(descriptor.set, "toString", { + value: function toString() { return setStr; }, + writable: false, + configurable: true, + enumerable: false, + }); + } + + return descriptor; +}; + +// Cleanup +document.head.removeChild(iframe); diff --git a/packages/coding-agent/src/tools/puppeteer/01_stealth_activity.js b/packages/coding-agent/src/tools/puppeteer/01_stealth_activity.js new file mode 100644 index 000000000..8306db9b6 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/01_stealth_activity.js @@ -0,0 +1,20 @@ +const scheduleActivity = () => { + const delay = 3000 + Math_random() * 4000; + Window_setTimeout(() => { + Object_defineProperty(document, "hidden", { get: () => false }); + Object_defineProperty(document, "visibilityState", { + get: () => "visible", + }); + Object_defineProperty(document, "webkitVisibilityState", { + get: () => "visible", + }); + document.dispatchEvent(new Window_Event("visibilitychange")); + if (Math_random() < 0.4) window.dispatchEvent(new Window_Event("focus")); + document.hasFocus = () => true; + if (Math_random() < 0.3) window.dispatchEvent(new Window_Event("scroll")); + if (navigator.wakeLock) + navigator.wakeLock.request("screen").catch(() => {}); + scheduleActivity(); + }, delay); +}; +scheduleActivity(); diff --git a/packages/coding-agent/src/tools/puppeteer/02_stealth_hairline.js b/packages/coding-agent/src/tools/puppeteer/02_stealth_hairline.js new file mode 100644 index 000000000..258af2825 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/02_stealth_hairline.js @@ -0,0 +1,11 @@ +const elementDescriptor = Object_getOwnPropertyDescriptor( + HTMLElement.prototype, + "offsetHeight", +); +Object_defineProperty(HTMLDivElement.prototype, "offsetHeight", { + ...elementDescriptor, + get: function () { + if (this.id === "modernizr") return 1; + return elementDescriptor.get.apply(this); + }, +}); diff --git a/packages/coding-agent/src/tools/puppeteer/03_stealth_botd.js b/packages/coding-agent/src/tools/puppeteer/03_stealth_botd.js new file mode 100644 index 000000000..4a322ffda --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/03_stealth_botd.js @@ -0,0 +1,384 @@ +const makeNativeString = (name) => "function " + (name || "") + "() { [native code] }"; +const patchToString = (fn, name) => { + if (typeof fn !== "function") return; + Object_defineProperty(fn, "toString", { + value: function toString() { + return makeNativeString(name); + }, + writable: false, + configurable: true, + enumerable: false, + }); +}; + +// Ensure navigator.webdriver behaves like real Chrome +if (navigator.webdriver !== false && navigator.webdriver !== undefined) { + const proto = Object_getPrototypeOf(navigator); + if (proto && "webdriver" in proto) { + delete proto.webdriver; + } +} + +// Ensure window.chrome exists and is populated with standard Chrome APIs +if (!window.chrome) { + Object_defineProperty(window, "chrome", { + writable: true, + enumerable: true, + configurable: false, + value: {}, + }); +} + +if (window.chrome && !("app" in window.chrome)) { + const STATIC_DATA = { + isInstalled: false, + InstallState: { + DISABLED: "disabled", + INSTALLED: "installed", + NOT_INSTALLED: "not_installed", + }, + RunningState: { + CANNOT_RUN: "cannot_run", + READY_TO_RUN: "ready_to_run", + RUNNING: "running", + }, + }; + + const makeError = (fn) => new TypeError(`Error in invocation of app.${fn}()`); + + window.chrome.app = { + ...STATIC_DATA, + get isInstalled() { + return false; + }, + getDetails: function getDetails() { + if (arguments.length) throw makeError("getDetails"); + return null; + }, + getIsInstalled: function getIsInstalled() { + if (arguments.length) throw makeError("getIsInstalled"); + return false; + }, + runningState: function runningState() { + if (arguments.length) throw makeError("runningState"); + return "cannot_run"; + }, + }; + + patchToString(window.chrome.app.getDetails, "getDetails"); + patchToString(window.chrome.app.getIsInstalled, "getIsInstalled"); + patchToString(window.chrome.app.runningState, "runningState"); +} + +if (window.chrome && !("csi" in window.chrome) && window.performance?.timing) { + window.chrome.csi = function csi() { + const { timing } = window.performance; + return { + onloadT: timing.domContentLoadedEventEnd, + startE: timing.navigationStart, + pageT: Date.now() - timing.navigationStart, + tran: 15, + }; + }; + patchToString(window.chrome.csi, "csi"); +} + +if ( + window.chrome && + !("loadTimes" in window.chrome) && + window.performance?.timing && + window.PerformancePaintTiming +) { + const { performance } = window; + const ntEntryFallback = { + nextHopProtocol: "h2", + type: "other", + }; + + const protocolInfo = { + get connectionInfo() { + const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback; + return ntEntry.nextHopProtocol; + }, + get npnNegotiatedProtocol() { + const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback; + return ["h2", "hq"].includes(ntEntry.nextHopProtocol) ? ntEntry.nextHopProtocol : "unknown"; + }, + get navigationType() { + const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback; + return ntEntry.type; + }, + get wasAlternateProtocolAvailable() { + return false; + }, + get wasFetchedViaSpdy() { + const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback; + return ["h2", "hq"].includes(ntEntry.nextHopProtocol); + }, + get wasNpnNegotiated() { + const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback; + return ["h2", "hq"].includes(ntEntry.nextHopProtocol); + }, + }; + + const { timing } = window.performance; + + const toFixed = (num, fixed) => { + const re = new RegExp("^-?\\d+(?:.\\d{0," + (fixed || -1) + "})?"); + return num.toString().match(re)[0]; + }; + + const timingInfo = { + get firstPaintAfterLoadTime() { + return 0; + }, + get requestTime() { + return timing.navigationStart / 1000; + }, + get startLoadTime() { + return timing.navigationStart / 1000; + }, + get commitLoadTime() { + return timing.responseStart / 1000; + }, + get finishDocumentLoadTime() { + return timing.domContentLoadedEventEnd / 1000; + }, + get finishLoadTime() { + return timing.loadEventEnd / 1000; + }, + get firstPaintTime() { + const fpEntry = performance.getEntriesByType("paint")[0] || { + startTime: timing.loadEventEnd / 1000, + }; + return toFixed((fpEntry.startTime + performance.timeOrigin) / 1000, 3); + }, + }; + + window.chrome.loadTimes = function loadTimes() { + return { ...protocolInfo, ...timingInfo }; + }; + patchToString(window.chrome.loadTimes, "loadTimes"); +} + +const isSecureOrigin = document.location.protocol.startsWith("https"); +if (window.chrome && !("runtime" in window.chrome) && isSecureOrigin) { + const STATIC_DATA = { + OnInstalledReason: { + CHROME_UPDATE: "chrome_update", + INSTALL: "install", + SHARED_MODULE_UPDATE: "shared_module_update", + UPDATE: "update", + }, + OnRestartRequiredReason: { + APP_UPDATE: "app_update", + OS_UPDATE: "os_update", + PERIODIC: "periodic", + }, + PlatformArch: { + ARM: "arm", + ARM64: "arm64", + MIPS: "mips", + MIPS64: "mips64", + X86_32: "x86-32", + X86_64: "x86-64", + }, + PlatformNaclArch: { + ARM: "arm", + MIPS: "mips", + MIPS64: "mips64", + X86_32: "x86-32", + X86_64: "x86-64", + }, + PlatformOs: { + ANDROID: "android", + CROS: "cros", + LINUX: "linux", + MAC: "mac", + OPENBSD: "openbsd", + WIN: "win", + }, + RequestUpdateCheckStatus: { + NO_UPDATE: "no_update", + THROTTLED: "throttled", + UPDATE_AVAILABLE: "update_available", + }, + }; + + const makeCustomRuntimeErrors = (preamble, method, extensionId) => ({ + NoMatchingSignature: new TypeError(preamble + "No matching signature."), + MustSpecifyExtensionID: new TypeError( + preamble + + `${method} called from a webpage must specify an Extension ID (string) for its first argument.`, + ), + InvalidExtensionID: new TypeError(preamble + `Invalid extension id: '${extensionId}'`), + }); + + const isValidExtensionId = (value) => value.length === 32 && value.toLowerCase().match(/^[a-p]+$/); + + const sendMessageHandler = { + apply(target, _ctx, args) { + const [extensionId, options, responseCallback] = args || []; + const errorPreamble = + "Error in invocation of runtime.sendMessage(optional string extensionId, any message, optional object options, optional function responseCallback): "; + const Errors = makeCustomRuntimeErrors(errorPreamble, "chrome.runtime.sendMessage()", extensionId); + + const noArguments = args.length === 0; + const tooManyArguments = args.length > 4; + const incorrectOptions = options && typeof options !== "object"; + const incorrectResponseCallback = responseCallback && typeof responseCallback !== "function"; + + if (noArguments || tooManyArguments || incorrectOptions || incorrectResponseCallback) { + throw Errors.NoMatchingSignature; + } + + if (args.length < 2) { + throw Errors.MustSpecifyExtensionID; + } + + if (typeof extensionId !== "string") { + throw Errors.NoMatchingSignature; + } + + if (!isValidExtensionId(extensionId)) { + throw Errors.InvalidExtensionID; + } + + return undefined; + }, + }; + + const connectHandler = { + apply(target, _ctx, args) { + const [extensionId, connectInfo] = args || []; + const errorPreamble = + "Error in invocation of runtime.connect(optional string extensionId, optional object connectInfo): "; + const Errors = makeCustomRuntimeErrors(errorPreamble, "chrome.runtime.connect()", extensionId); + + const noArguments = args.length === 0; + const emptyStringArgument = args.length === 1 && extensionId === ""; + if (noArguments || emptyStringArgument) { + throw Errors.MustSpecifyExtensionID; + } + + const tooManyArguments = args.length > 2; + const incorrectConnectInfoType = connectInfo && typeof connectInfo !== "object"; + if (tooManyArguments || incorrectConnectInfoType) { + throw Errors.NoMatchingSignature; + } + + const extensionIdIsString = typeof extensionId === "string"; + if (extensionIdIsString && extensionId === "") { + throw Errors.MustSpecifyExtensionID; + } + if (extensionIdIsString && !isValidExtensionId(extensionId)) { + throw Errors.InvalidExtensionID; + } + + const validateConnectInfo = (info) => { + if (args.length > 1) { + throw Errors.NoMatchingSignature; + } + if (Object_keys(info).length === 0) { + throw Errors.MustSpecifyExtensionID; + } + Object_entries(info).forEach(([key, value]) => { + const isExpected = ["name", "includeTlsChannelId"].includes(key); + if (!isExpected) { + throw new TypeError(errorPreamble + `Unexpected property: '${key}'.`); + } + const mismatch = (propName, expected, found) => + TypeError( + errorPreamble + + `Error at property '${propName}': Invalid type: expected ${expected}, found ${found}.`, + ); + if (key === "name" && typeof value !== "string") { + throw mismatch(key, "string", typeof value); + } + if (key === "includeTlsChannelId" && typeof value !== "boolean") { + throw mismatch(key, "boolean", typeof value); + } + }); + }; + + if (typeof extensionId === "object") { + validateConnectInfo(extensionId); + throw Errors.MustSpecifyExtensionID; + } + + const makeConnectResponse = () => { + const onSomething = () => ({ + addListener: function addListener() {}, + dispatch: function dispatch() {}, + hasListener: function hasListener() {}, + hasListeners: function hasListeners() { + return false; + }, + removeListener: function removeListener() {}, + }); + + return { + name: "", + sender: undefined, + disconnect: function disconnect() {}, + onDisconnect: onSomething(), + onMessage: onSomething(), + postMessage: function postMessage() { + if (!arguments.length) { + throw new TypeError("Insufficient number of arguments."); + } + throw new Error("Attempting to use a disconnected port object"); + }, + }; + }; + + return makeConnectResponse(); + }, + }; + + window.chrome.runtime = { + ...STATIC_DATA, + get id() { + return undefined; + }, + connect: null, + sendMessage: null, + }; + + const sendMessageProxy = new Window_Proxy(function sendMessage() {}, sendMessageHandler); + const connectProxy = new Window_Proxy(function connect() {}, connectHandler); + patchToString(sendMessageProxy, "sendMessage"); + patchToString(connectProxy, "connect"); + window.chrome.runtime.sendMessage = sendMessageProxy; + window.chrome.runtime.connect = connectProxy; +} + +// Suppress Permission.query for automation-controlled +if (navigator.permissions?.query) { + if (isSecureOrigin && "Notification" in window) { + Object_defineProperty(Notification, "permission", { + get: () => "default", + configurable: true, + }); + } else if (!isSecureOrigin) { + const originalQuery = navigator.permissions.query; + navigator.permissions.query = function (parameters) { + if (parameters?.name === "notifications") { + const status = { state: "denied", onchange: null }; + if (typeof PermissionStatus !== "undefined") { + Object_setPrototypeOf(status, PermissionStatus.prototype); + } + return Promise_resolve(status); + } + return originalQuery.call(this, parameters); + }; + } +} + +// Remove CDC_ markers from document +const documentProps = Object_getOwnPropertyNames(document); +for (const prop of documentProps) { + if (prop.startsWith("cdc_")) { + delete document[prop]; + } +} diff --git a/packages/coding-agent/src/tools/puppeteer/04_stealth_iframe.js b/packages/coding-agent/src/tools/puppeteer/04_stealth_iframe.js new file mode 100644 index 000000000..f69a5557a --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/04_stealth_iframe.js @@ -0,0 +1,81 @@ +const addContentWindowProxy = (iframe) => { + const contentWindowProxy = { + get(target, key) { + if (key === "self") return this; + if (key === "frameElement") return iframe; + if (key === "0") return undefined; + return Reflect.get(target, key); + }, + }; + + if (!iframe.contentWindow) { + const proxy = new Window_Proxy(window, contentWindowProxy); + Object_defineProperty(iframe, "contentWindow", { + get() { + return proxy; + }, + set(newValue) { + return newValue; + }, + enumerable: true, + configurable: false, + }); + } +}; + +const handleIframeCreation = (target, thisArg, args) => { + const iframe = target.apply(thisArg, args); + const originalIframe = iframe; + const originalSrcdoc = originalIframe.srcdoc; + + Object_defineProperty(iframe, "srcdoc", { + configurable: true, + get() { + return originalSrcdoc; + }, + set(newValue) { + addContentWindowProxy(this); + Object_defineProperty(iframe, "srcdoc", { + configurable: false, + writable: false, + value: originalSrcdoc, + }); + originalIframe.srcdoc = newValue; + }, + }); + + return iframe; +}; + +const addIframeCreationSniffer = () => { + const originalCreateElement = document.createElement; + const handler = { + apply(target, thisArg, args) { + const isIframe = args && args.length && `${args[0]}`.toLowerCase() === "iframe"; + if (!isIframe) { + return target.apply(thisArg, args); + } + return handleIframeCreation(target, thisArg, args); + }, + get(target, key) { + return Reflect.get(target, key); + }, + }; + + const proxied = new Window_Proxy(originalCreateElement, handler); + Object_defineProperty(document, "createElement", { + value: proxied, + writable: true, + configurable: true, + }); + Object_defineProperty(document.createElement, "toString", { + value: Function_toString.bind(originalCreateElement), + writable: false, + configurable: true, + enumerable: false, + }); +}; + +try { + addIframeCreationSniffer(); +} catch {} diff --git a/packages/coding-agent/src/tools/puppeteer/05_stealth_webgl.js b/packages/coding-agent/src/tools/puppeteer/05_stealth_webgl.js new file mode 100644 index 000000000..2c9d1b4e5 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/05_stealth_webgl.js @@ -0,0 +1,71 @@ +const vendors = ["Intel Inc.", "NVIDIA Corporation", "AMD"]; +const renderers = [ + "Intel(R) Iris(TM) Plus Graphics 640", + "Intel(R) HD Graphics 630", + "NVIDIA GeForce GTX 1050 Ti", + "NVIDIA GeForce GTX 1060", + "NVIDIA GeForce RTX 3060", + "AMD Radeon RX 580", + "AMD Radeon Pro 560", +]; +const vendor = vendors[Math_floor(Math_random() * vendors.length)]; +const renderer = renderers[Math_floor(Math_random() * renderers.length)]; + +const getParameterProxyHandler = { + apply(target, thisArg, args) { + const param = args[0]; + // UNMASKED_VENDOR_WEBGL = 0x9245 + if (param === 0x9245) return vendor; + // UNMASKED_RENDERER_WEBGL = 0x9246 + if (param === 0x9246) return renderer; + return target.apply(thisArg, args); + }, +}; + +// Hook WebGL contexts +const hookWebGL = (proto) => { + const originalGetParameter = proto.getParameter; + Object_defineProperty(proto, "getParameter", { + value: new Window_Proxy(originalGetParameter, getParameterProxyHandler), + writable: true, + configurable: true, + enumerable: true, + }); +}; + +if (window.WebGLRenderingContext) { + hookWebGL(WebGLRenderingContext.prototype); +} +if (window.WebGL2RenderingContext) { + hookWebGL(WebGL2RenderingContext.prototype); +} + +// Also mask getShaderPrecisionFormat for software rendering detection +const precisionMask = (proto) => { + const original = proto.getShaderPrecisionFormat; + if (!original) return; + Object_defineProperty(proto, "getShaderPrecisionFormat", { + value: function (shaderType, precisionType) { + const result = original.call(this, shaderType, precisionType); + if (result) { + // Hardware typically has higher precision than SwiftShader defaults + return { + precision: Math_max(result.precision, 23), + rangeMin: Math_min(result.rangeMin, 127), + rangeMax: Math_max(result.rangeMax, 127), + }; + } + return result; + }, + writable: true, + configurable: true, + enumerable: true, + }); +}; + +if (window.WebGLRenderingContext) { + precisionMask(WebGLRenderingContext.prototype); +} +if (window.WebGL2RenderingContext) { + precisionMask(WebGL2RenderingContext.prototype); +} diff --git a/packages/coding-agent/src/tools/puppeteer/06_stealth_screen.js b/packages/coding-agent/src/tools/puppeteer/06_stealth_screen.js new file mode 100644 index 000000000..1ba685442 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/06_stealth_screen.js @@ -0,0 +1,71 @@ +// Generate consistent "real" screen dimensions based on viewport +const width = window.innerWidth; +const height = window.innerHeight; +const availWidth = width; +const availHeight = Math_max(height - 40, 0); // Account for taskbar +const colorDepth = 24; +const pixelDepth = 24; + +const defineScreenProp = (prop, value) => { + try { + Object_defineProperty(window.Screen?.prototype ?? window.screen, prop, { + get: () => value, + configurable: true, + enumerable: true, + }); + } catch {} +}; + +// Override screen properties +for (const [prop, descriptor] of Object_entries({ + width, + height, + availWidth, + availHeight, + availLeft: 0, + availTop: 0, + colorDepth, + pixelDepth, +})) { + defineScreenProp(prop, descriptor); +} + +// Ensure outer dimensions match screen for consistency +const chromeFrameHeight = 85; +Object_defineProperty(window, "outerWidth", { + get: () => window.innerWidth, + configurable: true, + enumerable: true, +}); +Object_defineProperty(window, "outerHeight", { + get: () => window.innerHeight + chromeFrameHeight, + configurable: true, + enumerable: true, +}); + +if (window.visualViewport) { + const defineVvpProp = (prop, value) => { + try { + Object_defineProperty(window.visualViewport, prop, { + get: () => value, + configurable: true, + enumerable: true, + }); + } catch {} + }; + + defineVvpProp("width", width); + defineVvpProp("height", height); + defineVvpProp("scale", 1); + defineVvpProp("offsetLeft", 0); + defineVvpProp("offsetTop", 0); + defineVvpProp("pageLeft", 0); + defineVvpProp("pageTop", 0); +} + +// Consistent devicePixelRatio +Object_defineProperty(window, "devicePixelRatio", { + get: () => 1, + configurable: true, + enumerable: true, +}); diff --git a/packages/coding-agent/src/tools/puppeteer/07_stealth_fonts.js b/packages/coding-agent/src/tools/puppeteer/07_stealth_fonts.js new file mode 100644 index 000000000..237912763 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/07_stealth_fonts.js @@ -0,0 +1,97 @@ +const commonFonts = [ + "Arial", + "Arial Black", + "Arial Narrow", + "Arial Rounded MT Bold", + "Book Antiqua", + "Bookman Old Style", + "Calibri", + "Cambria", + "Cambria Math", + "Century", + "Century Gothic", + "Century Schoolbook", + "Comic Sans MS", + "Consolas", + "Courier", + "Courier New", + "Garamond", + "Geneva", + "Georgia", + "Gill Sans", + "Gill Sans MT", + "Helvetica", + "Helvetica Neue", + "Impact", + "Lucida Console", + "Lucida Grande", + "Lucida Sans Unicode", + "MS Gothic", + "MS PGothic", + "MS Sans Serif", + "MS Serif", + "Palatino", + "Palatino Linotype", + "Segoe Print", + "Segoe Script", + "Segoe UI", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Tahoma", + "Times", + "Times New Roman", + "Trebuchet MS", + "Verdana", + "Wingdings", + "Wingdings 2", + "Wingdings 3", + "Apple Color Emoji", + "Apple SD Gothic Neo", + "Helvetica Neue", + "Hoefler Text", + "Menlo", + "Monaco", + "San Francisco", + "SF Pro Display", + "SF Pro Text", +]; + +// Override queryLocalFonts if present (Local Font Access API) +if ("queryLocalFonts" in window) { + Object_defineProperty(window, "queryLocalFonts", { + value: async () => { + return commonFonts.map((family) => ({ + family, + fullName: family, + postscriptName: family.replace(/\s+/g, ""), + style: "Regular", + blob: () => Promise_resolve(new Window_Blob([])), + })); + }, + writable: true, + configurable: true, + enumerable: true, + }); +} + +// Hide fonts-unique tracking via canvas +const originalGetContext = HTMLCanvasElement.prototype.getContext; +HTMLCanvasElement.prototype.getContext = function (type, options) { + const ctx = originalGetContext.call(this, type, options); + if (ctx && type === "2d") { + const originalFillText = ctx.fillText; + ctx.fillText = function (text, x, y, maxWidth) { + // Add tiny imperceptible noise to text rendering + const noiseX = (Math_random() - 0.5) * 0.02; + const noiseY = (Math_random() - 0.5) * 0.02; + return originalFillText.call( + this, + text, + x + noiseX, + y + noiseY, + maxWidth, + ); + }; + } + return ctx; +}; diff --git a/packages/coding-agent/src/tools/puppeteer/08_stealth_audio.js b/packages/coding-agent/src/tools/puppeteer/08_stealth_audio.js new file mode 100644 index 000000000..c2f120656 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/08_stealth_audio.js @@ -0,0 +1,51 @@ +// Spoof AudioContext latency values to look like real hardware +const spoofLatency = (proto) => { + Object_defineProperty(proto, "baseLatency", { + get: () => 0.005, // ~5ms typical for real hardware + configurable: true, + enumerable: true, + }); + Object_defineProperty(proto, "outputLatency", { + get: () => 0.01, // ~10ms typical + configurable: true, + enumerable: true, + }); +}; + +if (window.AudioContext) { + spoofLatency(AudioContext.prototype); +} +if (window.OfflineAudioContext) { + // For offline context, add subtle randomness to prevent deterministic fingerprints + const OriginalOfflineAudioContext = window.OfflineAudioContext; + window.OfflineAudioContext = class extends OriginalOfflineAudioContext { + constructor(numberOfChannels, length, sampleRate) { + super(numberOfChannels, length, sampleRate); + + // Hook startRendering to add noise + const originalStartRendering = this.startRendering.bind(this); + this.startRendering = async () => { + const buffer = await originalStartRendering(); + // Add imperceptible noise to prevent deterministic hash + for (let c = 0; c < buffer.numberOfChannels; c++) { + const channel = buffer.getChannelData(c); + for (let i = 0; i < channel.length; i++) { + if (Math_random() < 0.001) { + channel[i] += (Math_random() - 0.5) * 1e-8; + } + } + } + return buffer; + }; + } + }; +} + +// Also spoof sampleRate consistency +if (window.AudioContext) { + Object_defineProperty(AudioContext.prototype, "sampleRate", { + get: () => 48000, // Common hardware rate + configurable: true, + enumerable: true, + }); +} diff --git a/packages/coding-agent/src/tools/puppeteer/09_stealth_locale.js b/packages/coding-agent/src/tools/puppeteer/09_stealth_locale.js new file mode 100644 index 000000000..81b00947b --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/09_stealth_locale.js @@ -0,0 +1,46 @@ +// Define a consistent locale profile +const locale = "en-US"; +const languages = ["en-US", "en"]; +const timezone = "America/New_York"; + +// Override navigator language properties +Object_defineProperty(navigator, "language", { + get: () => locale, + configurable: true, + enumerable: true, +}); +Object_defineProperty(navigator, "languages", { + get: () => [...languages], + configurable: true, + enumerable: true, +}); + +// Override Intl.DateTimeFormat for timezone consistency +const OriginalDateTimeFormat = Intl_DateTimeFormat; +Intl.DateTimeFormat = class extends OriginalDateTimeFormat { + constructor(locales, options) { + const mergedOptions = { ...options, timeZone: timezone }; + super(locales, mergedOptions); + } + resolvedOptions() { + const options = super.resolvedOptions(); + options.timeZone = timezone; + return options; + } +}; + +// Ensure Date timezone is consistent +const originalDateConstructor = Date_constructor; +const originalToString = originalDateConstructor.prototype.toString; +const originalToTimeString = originalDateConstructor.prototype.toTimeString; + +Date.prototype.toString = function () { + return originalToString + .call(this) + .replace(/\(.*\)$/, "(Eastern Standard Time)"); +}; +Date.prototype.toTimeString = function () { + return originalToTimeString + .call(this) + .replace(/\(.*\)$/, "(Eastern Standard Time)"); +}; diff --git a/packages/coding-agent/src/tools/puppeteer/10_stealth_plugins.js b/packages/coding-agent/src/tools/puppeteer/10_stealth_plugins.js new file mode 100644 index 000000000..403b959ef --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/10_stealth_plugins.js @@ -0,0 +1,206 @@ +const mimeTypesData = [ + { + type: "application/pdf", + suffixes: "pdf", + description: "", + __pluginName: "Chrome PDF Viewer", + }, + { + type: "application/x-google-chrome-pdf", + suffixes: "pdf", + description: "Portable Document Format", + __pluginName: "Chrome PDF Plugin", + }, + { + type: "application/x-nacl", + suffixes: "", + description: "Native Client Executable", + __pluginName: "Native Client", + }, + { + type: "application/x-pnacl", + suffixes: "", + description: "Portable Native Client Executable", + __pluginName: "Native Client", + }, +]; + +const pluginsData = [ + { + name: "Chrome PDF Plugin", + filename: "internal-pdf-viewer", + description: "Portable Document Format", + __mimeTypes: ["application/x-google-chrome-pdf"], + }, + { + name: "Chrome PDF Viewer", + filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", + description: "", + __mimeTypes: ["application/pdf"], + }, + { + name: "Native Client", + filename: "internal-nacl-plugin", + description: "", + __mimeTypes: ["application/x-nacl", "application/x-pnacl"], + }, +]; + +const defineProp = (obj, prop, value) => + Object_defineProperty(obj, prop, { + value, + writable: false, + enumerable: false, + configurable: true, + }); + +const generateFunctionMocks = (proto, itemMainProp, dataArray) => ({ + item: new Window_Proxy(proto.item, { + apply(target, ctx, args) { + if (!args.length) { + throw new TypeError( + `Failed to execute 'item' on '${proto[Symbol.toStringTag]}': 1 argument required, but only 0 present.`, + ); + } + const isInteger = args[0] && Number.isInteger(Number(args[0])); + return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null; + }, + }), + namedItem: new Window_Proxy(proto.namedItem, { + apply(target, ctx, args) { + if (!args.length) { + throw new TypeError( + `Failed to execute 'namedItem' on '${proto[Symbol.toStringTag]}': 1 argument required, but only 0 present.`, + ); + } + return dataArray.find(item => item[itemMainProp] === args[0]) || null; + }, + }), + refresh: proto.refresh + ? new Window_Proxy(proto.refresh, { + apply() { + return undefined; + }, + }) + : undefined, +}); + +const generateMagicArray = (dataArray, proto, itemProto, itemMainProp) => { + const makeItem = (data) => { + const item = {}; + for (const prop of Object_keys(data)) { + if (prop.startsWith("__")) continue; + defineProp(item, prop, data[prop]); + } + return patchItem(item, data); + }; + + const patchItem = (item, data) => { + let descriptor = Object_getOwnPropertyDescriptors(item); + if (itemProto === Plugin.prototype) { + descriptor = { + ...descriptor, + length: { + value: data.__mimeTypes.length, + writable: false, + enumerable: false, + configurable: true, + }, + }; + } + + const obj = Object_create(itemProto, descriptor); + const blacklist = [...Object_keys(data), "length", "enabledPlugin"]; + return new Window_Proxy(obj, { + ownKeys(target) { + return Reflect.ownKeys(target).filter(key => !blacklist.includes(key)); + }, + getOwnPropertyDescriptor(target, prop) { + if (blacklist.includes(prop)) return undefined; + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + }); + }; + + const magicArray = []; + dataArray.forEach(data => { + magicArray.push(makeItem(data)); + }); + + magicArray.forEach(entry => { + defineProp(magicArray, entry[itemMainProp], entry); + }); + + const magicArrayObj = Object_create(proto, { + ...Object_getOwnPropertyDescriptors(magicArray), + length: { + value: magicArray.length, + writable: false, + enumerable: false, + configurable: true, + }, + }); + + const functionMocks = generateFunctionMocks(proto, itemMainProp, magicArray); + + return new Window_Proxy(magicArrayObj, { + get(target, key = "") { + if (key === "item") return functionMocks.item; + if (key === "namedItem") return functionMocks.namedItem; + if (proto === PluginArray.prototype && key === "refresh") return functionMocks.refresh; + return Reflect.get(target, key); + }, + ownKeys(target) { + const keys = []; + const typeProps = magicArray.map(entry => entry[itemMainProp]); + typeProps.forEach((_, index) => keys.push(`${index}`)); + typeProps.forEach(propName => keys.push(propName)); + return keys; + }, + getOwnPropertyDescriptor(target, prop) { + if (prop === "length") return undefined; + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + }); +}; + +const generateMimeTypeArray = (data) => + generateMagicArray(data, MimeTypeArray.prototype, MimeType.prototype, "type"); +const generatePluginArray = (data) => + generateMagicArray(data, PluginArray.prototype, Plugin.prototype, "name"); + +const mimeTypes = generateMimeTypeArray(mimeTypesData); +const plugins = generatePluginArray(pluginsData); + +for (const pluginData of pluginsData) { + pluginData.__mimeTypes.forEach((type, index) => { + plugins[pluginData.name][index] = mimeTypes[type]; + Object_defineProperty(plugins[pluginData.name], type, { + value: mimeTypes[type], + writable: false, + enumerable: false, + configurable: true, + }); + Object_defineProperty(mimeTypes[type], "enabledPlugin", { + value: + type === "application/x-pnacl" + ? mimeTypes["application/x-nacl"].enabledPlugin + : new Window_Proxy(plugins[pluginData.name], {}), + writable: false, + enumerable: false, + configurable: true, + }); + }); +} + +const patchNavigator = (name, value) => + Object_defineProperty(Object_getPrototypeOf(navigator), name, { + get() { + return value; + }, + }); + +if (!("plugins" in navigator) || navigator.plugins.length === 0) { + patchNavigator("plugins", plugins); + patchNavigator("mimeTypes", mimeTypes); +} diff --git a/packages/coding-agent/src/tools/puppeteer/11_stealth_hardware.js b/packages/coding-agent/src/tools/puppeteer/11_stealth_hardware.js new file mode 100644 index 000000000..078a9a5e1 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/11_stealth_hardware.js @@ -0,0 +1,8 @@ +const navigatorProto = Object_getPrototypeOf(navigator); +if (navigatorProto && "hardwareConcurrency" in navigatorProto) { + Object_defineProperty(navigatorProto, "hardwareConcurrency", { + get: () => 4, + configurable: true, + enumerable: true, + }); +} diff --git a/packages/coding-agent/src/tools/puppeteer/12_stealth_codecs.js b/packages/coding-agent/src/tools/puppeteer/12_stealth_codecs.js new file mode 100644 index 000000000..1d11dbe8e --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/12_stealth_codecs.js @@ -0,0 +1,40 @@ +const parseInput = (arg) => { + const [mime, codecStr] = arg.trim().split(";"); + let codecs = []; + if (codecStr && codecStr.includes('codecs="')) { + codecs = codecStr + .trim() + .replace('codecs="', "") + .replace('"', "") + .trim() + .split(",") + .filter(Boolean) + .map(item => item.trim()); + } + return { mime, codecStr, codecs }; +}; + +const originalCanPlayType = HTMLMediaElement.prototype.canPlayType; +Object_defineProperty(HTMLMediaElement.prototype, "canPlayType", { + value: new Window_Proxy(originalCanPlayType, { + apply(target, ctx, args) { + if (!args || !args.length) { + return target.apply(ctx, args); + } + const { mime, codecs } = parseInput(args[0]); + if (mime === "video/mp4" && codecs.includes("avc1.42E01E")) { + return "probably"; + } + if (mime === "audio/x-m4a" && !codecs.length) { + return "maybe"; + } + if (mime === "audio/aac" && !codecs.length) { + return "probably"; + } + return target.apply(ctx, args); + }, + }), + writable: true, + configurable: true, + enumerable: true, +}); diff --git a/packages/coding-agent/src/tools/puppeteer/13_stealth_worker.js b/packages/coding-agent/src/tools/puppeteer/13_stealth_worker.js new file mode 100644 index 000000000..deed8fc63 --- /dev/null +++ b/packages/coding-agent/src/tools/puppeteer/13_stealth_worker.js @@ -0,0 +1,74 @@ +const patchWorkerConstructor = (name, OriginalWorker) => { + if (typeof OriginalWorker !== "function") return; + + const buildWrappedUrl = (scriptURL, options) => { + const ua = navigator.userAgent; + const platform = navigator.platform; + const uaData = navigator.userAgentData && typeof navigator.userAgentData.toJSON === "function" + ? navigator.userAgentData.toJSON() + : navigator.userAgentData; + + const preludeLines = [ + "try {", + `const ua = ${JSON.stringify(ua)};`, + `const platform = ${JSON.stringify(platform)};`, + "Object_defineProperty(self.navigator, 'userAgent', { get: () => ua, configurable: true });", + "Object_defineProperty(self.navigator, 'platform', { get: () => platform, configurable: true });", + ]; + + if (uaData) { + preludeLines.push(`const uaData = ${JSON.stringify(uaData)};`); + preludeLines.push( + "Object_defineProperty(self.navigator, 'userAgentData', { get: () => uaData, configurable: true });", + ); + } + + preludeLines.push("} catch (e) {};"); + const prelude = preludeLines.join("\n"); + const importLine = options?.type === "module" + ? `import ${JSON.stringify(String(scriptURL))};` + : `importScripts(${JSON.stringify(String(scriptURL))});`; + const blob = new Window_Blob([prelude, "\n", importLine], { type: "application/javascript" }); + const url = URL.createObjectURL(blob); + return url; + }; + + const handler = { + construct(target, args) { + try { + const scriptURL = args?.[0]; + const options = args?.[1]; + if (!scriptURL) { + return new target(...(args || [])); + } + const wrappedUrl = buildWrappedUrl(scriptURL, options); + const worker = new target(wrappedUrl, options); + URL.revokeObjectURL(wrappedUrl); + return worker; + } catch { + return new target(...(args || [])); + } + }, + apply(target, thisArg, args) { + return Reflect.apply(target, thisArg, args || []); + }, + }; + + const proxied = new Window_Proxy(OriginalWorker, handler); + Object_defineProperty(window, name, { + value: proxied, + writable: true, + configurable: true, + }); + Object_defineProperty(window[name], "toString", { + value: Function_toString.bind(OriginalWorker), + writable: false, + configurable: true, + enumerable: false, + }); +}; + +try { + patchWorkerConstructor("Worker", window.Worker); + patchWorkerConstructor("SharedWorker", window.SharedWorker); +} catch {} diff --git a/packages/natives/package.json b/packages/natives/package.json index 0f8c64f2a..82ae6e6f4 100644 --- a/packages/natives/package.json +++ b/packages/natives/package.json @@ -35,7 +35,7 @@ "@oh-my-pi/pi-utils": "workspace:*" }, "devDependencies": { - "@types/node": "^25.0.10" + "@types/node": "^25.2.0" }, "engines": { "bun": ">=1.3.7" diff --git a/packages/react-edit-benchmark/package.json b/packages/react-edit-benchmark/package.json index af5acddf3..7c959396d 100644 --- a/packages/react-edit-benchmark/package.json +++ b/packages/react-edit-benchmark/package.json @@ -22,6 +22,6 @@ }, "devDependencies": { "@types/diff": "^8.0.0", - "bun-types": "^1.3.7" + "bun-types": "^1.3.8" } } diff --git a/packages/stats/package.json b/packages/stats/package.json index c64491bf0..5b7cd9d21 100644 --- a/packages/stats/package.json +++ b/packages/stats/package.json @@ -56,7 +56,7 @@ "recharts": "^3.7.0" }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.2.0", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3" }, diff --git a/packages/utils/package.json b/packages/utils/package.json index 4980018ea..c43288905 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -31,7 +31,7 @@ "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { - "@types/node": "^25.0.10" + "@types/node": "^25.2.0" }, "engines": { "bun": ">=1.3.7"