diff --git a/package-lock.json b/package-lock.json index 6adcc47..3e18299 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "claw3d", - "version": "0.1.0", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claw3d", - "version": "0.1.0", + "version": "0.1.5", "license": "MIT", "dependencies": { "@noble/ed25519": "^3.0.0", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.5.0", - "@vercel/otel": "^2.1.0", "canvas-confetti": "^1.9.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -163,7 +162,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -470,7 +468,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -511,7 +508,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1959,141 +1955,6 @@ "node": ">=12.4.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", - "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz", - "integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.211.0.tgz", - "integrity": "sha512-O5nPwzgg2JHzo59kpQTPUOTzFi0Nv5LxryG27QoXBciX3zWM3z83g+SNOHhiQVYRWFSxoWn1JM2TGD5iNjOwdA==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "@opentelemetry/core": "2.5.0", - "@opentelemetry/resources": "2.5.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.5.0.tgz", - "integrity": "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/resources": "2.5.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz", - "integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/resources": "2.5.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", - "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@peculiar/asn1-cms": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", @@ -2259,7 +2120,6 @@ "integrity": "sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "playwright": "1.58.0" }, @@ -2315,7 +2175,6 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz", "integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", @@ -3069,6 +2928,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3089,6 +2949,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -3170,7 +3031,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/canvas-confetti": { "version": "1.9.0", @@ -3286,7 +3148,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3297,7 +3158,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3322,7 +3182,6 @@ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", "license": "MIT", - "peer": true, "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", @@ -3400,7 +3259,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -3918,24 +3776,6 @@ "react": ">= 16.8.0" } }, - "node_modules/@vercel/otel": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vercel/otel/-/otel-2.1.0.tgz", - "integrity": "sha512-Zwu2Cu4t46DzBnY1DQSTxZ4MBLVfYsOjnlWuZuLRWnmVPX+SNrVHbs3ssiJ6uvY1J1JJswor4zSn8mHYxzYeBA==", - "license": "MIT", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <2.0.0", - "@opentelemetry/api-logs": ">=0.200.0 <0.300.0", - "@opentelemetry/instrumentation": ">=0.200.0 <0.300.0", - "@opentelemetry/resources": ">=2.0.0 <3.0.0", - "@opentelemetry/sdk-logs": ">=0.200.0 <0.300.0", - "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0", - "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0" - } - }, "node_modules/@vitest/expect": { "version": "4.0.18", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", @@ -4057,8 +3897,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4066,15 +3906,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4118,6 +3949,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -4495,7 +4327,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4724,18 +4555,11 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "clsx": "^2.1.1" }, @@ -4754,7 +4578,6 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -5112,7 +4935,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/draco3d": { "version": "1.5.7", @@ -5431,7 +5255,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5633,7 +5456,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6532,18 +6354,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-in-the-middle": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.5.tgz", - "integrity": "sha512-0InH9/4oDCBRzWXhpOqusspLBrVfK1vPvbn9Wxl8DAQ8yyx5fWJRETICSwkiAMaYntjJAMBP1R4B6cQnEUYVEA==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -7148,7 +6958,6 @@ "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -7631,6 +7440,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -8599,12 +8409,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9178,6 +8982,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -9193,6 +8998,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -9205,7 +9011,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/promise-worker-transferable": { "version": "1.0.4", @@ -9295,7 +9102,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9305,7 +9111,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -9518,19 +9323,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -10292,7 +10084,6 @@ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" @@ -10323,8 +10114,7 @@ "version": "0.183.2", "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/three-mesh-bvh": { "version": "0.8.3", @@ -10416,7 +10206,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10752,7 +10541,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11026,7 +10814,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -11135,7 +10922,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11487,7 +11273,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/app/api/office/picture-model/route.ts b/src/app/api/office/picture-model/route.ts index 0f527b4..a4fdee7 100644 --- a/src/app/api/office/picture-model/route.ts +++ b/src/app/api/office/picture-model/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; -import { GatewayClient } from "@/lib/gateway/GatewayClient"; +import { NodeGatewayClient } from "@/lib/gateway/nodeGatewayClient"; import { generatePictureModelViaGateway, MAX_PICTURE_MODEL_UPLOAD_BYTES, @@ -8,7 +8,7 @@ import { export const runtime = "nodejs"; export async function POST(request: Request) { - const gatewayClient = new GatewayClient(); + const gatewayClient = new NodeGatewayClient(); try { const MULTIPART_OVERHEAD_ALLOWANCE = 1024; const contentLengthHeader = request.headers.get("content-length"); @@ -85,7 +85,6 @@ export async function POST(request: Request) { disableDeviceAuth: false, }); - Buffer.from(arrayBuffer).toString("base64"); const result = await generatePictureModelViaGateway({ client: gatewayClient, summary: { @@ -107,6 +106,6 @@ export async function POST(request: Request) { : "Failed to generate the 3D model from the uploaded image."; return NextResponse.json({ error: message }, { status: 500 }); } finally { - gatewayClient.disconnect(); + gatewayClient.close(); } } diff --git a/src/features/agents/operations/agentFleetHydration.ts b/src/features/agents/operations/agentFleetHydration.ts index 1592b04..535dd9a 100644 --- a/src/features/agents/operations/agentFleetHydration.ts +++ b/src/features/agents/operations/agentFleetHydration.ts @@ -1,7 +1,7 @@ import { - buildAgentMainSessionKey, isSameSessionKey, } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import { type GatewayModelPolicySnapshot } from "@/lib/gateway/models"; import { isTemporarySkillAgentName, diff --git a/src/features/agents/operations/agentFleetHydrationDerivation.ts b/src/features/agents/operations/agentFleetHydrationDerivation.ts index 3b33144..c857057 100644 --- a/src/features/agents/operations/agentFleetHydrationDerivation.ts +++ b/src/features/agents/operations/agentFleetHydrationDerivation.ts @@ -1,4 +1,4 @@ -import { buildAgentMainSessionKey } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import { createDefaultAgentAvatarProfile } from "@/lib/avatars/profile"; import { resolveConfiguredModelKey, type GatewayModelPolicySnapshot } from "@/lib/gateway/models"; import { diff --git a/src/features/agents/operations/latestUpdateWorkflow.ts b/src/features/agents/operations/latestUpdateWorkflow.ts index 2ac8846..495152d 100644 --- a/src/features/agents/operations/latestUpdateWorkflow.ts +++ b/src/features/agents/operations/latestUpdateWorkflow.ts @@ -1,4 +1,4 @@ -import { parseAgentIdFromSessionKey } from "@/lib/gateway/GatewayClient"; +import { parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; export type LatestUpdateKind = "heartbeat" | "cron" | null; diff --git a/src/features/agents/screens/AgentsPageScreen.tsx b/src/features/agents/screens/AgentsPageScreen.tsx index 394aa32..6b132dd 100644 --- a/src/features/agents/screens/AgentsPageScreen.tsx +++ b/src/features/agents/screens/AgentsPageScreen.tsx @@ -1363,7 +1363,6 @@ const AgentsPageScreen = () => { (!connectPromptReady || (gatewayUrl.trim().length > 0 && !shouldPromptForConnect && - token.trim().length > 0 && (!didAttemptGatewayConnect || status === "connecting"))) ) { return ( diff --git a/src/features/agents/state/gatewayEventIngressWorkflow.ts b/src/features/agents/state/gatewayEventIngressWorkflow.ts index 9b000b6..1d53d7b 100644 --- a/src/features/agents/state/gatewayEventIngressWorkflow.ts +++ b/src/features/agents/state/gatewayEventIngressWorkflow.ts @@ -1,6 +1,7 @@ import { resolveExecApprovalEventEffects, type ExecApprovalEventEffects } from "@/features/agents/approvals/execApprovalLifecycleWorkflow"; import type { AgentState } from "@/features/agents/state/store"; -import { parseAgentIdFromSessionKey, type EventFrame } from "@/lib/gateway/GatewayClient"; +import type { EventFrame } from "@/lib/gateway/GatewayClient"; +import { parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; export type CronTranscriptIntent = { agentId: string; diff --git a/src/features/agents/state/gatewayRuntimeEventHandler.ts b/src/features/agents/state/gatewayRuntimeEventHandler.ts index 1b53f6a..af89764 100644 --- a/src/features/agents/state/gatewayRuntimeEventHandler.ts +++ b/src/features/agents/state/gatewayRuntimeEventHandler.ts @@ -25,8 +25,8 @@ import { import { type EventFrame, isSameSessionKey, - parseAgentIdFromSessionKey, } from "@/lib/gateway/GatewayClient"; +import { parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; import { normalizeAssistantDisplayText } from "@/lib/text/assistantText"; import { extractText, diff --git a/src/features/office/hooks/useRemoteOfficePresence.ts b/src/features/office/hooks/useRemoteOfficePresence.ts index 059e726..37c5c41 100644 --- a/src/features/office/hooks/useRemoteOfficePresence.ts +++ b/src/features/office/hooks/useRemoteOfficePresence.ts @@ -6,10 +6,10 @@ import type { SummaryStatusSnapshot, } from "@/features/agents/state/runtimeEventBridge"; import { - buildAgentMainSessionKey, GatewayClient, isGatewayDisconnectLikeError, } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import { buildOfficePresenceSnapshotFromGateway } from "@/lib/office/gatewayPresence"; import type { OfficePresenceSnapshot } from "@/lib/office/presence"; diff --git a/src/features/office/screens/OfficeScreen.tsx b/src/features/office/screens/OfficeScreen.tsx index 237dea8..ebc8600 100644 --- a/src/features/office/screens/OfficeScreen.tsx +++ b/src/features/office/screens/OfficeScreen.tsx @@ -17,11 +17,13 @@ import { GatewayConnectScreen } from "@/features/agents/components/GatewayConnec import { useAgentStore, type AgentState } from "@/features/agents/state/store"; import { GatewayClient, - buildAgentMainSessionKey, type EventFrame, isSameSessionKey, - parseAgentIdFromSessionKey, } from "@/lib/gateway/GatewayClient"; +import { + buildAgentMainSessionKey, + parseAgentIdFromSessionKey, +} from "@/lib/gateway/sessionKeys"; import { useRuntimeConnection } from "@/lib/runtime/useRuntimeConnection"; import { createStudioSettingsCoordinator, diff --git a/src/features/office/tasks/useTaskBoardController.ts b/src/features/office/tasks/useTaskBoardController.ts index 93fe18e..613b8f3 100644 --- a/src/features/office/tasks/useTaskBoardController.ts +++ b/src/features/office/tasks/useTaskBoardController.ts @@ -32,11 +32,11 @@ import { type CronJobSummary, } from "@/lib/cron/types"; import { - parseAgentIdFromSessionKey, type EventFrame, type GatewayClient, type GatewayStatus, } from "@/lib/gateway/GatewayClient"; +import { parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; import { resolveTaskBoardPreference, type StudioTaskBoardPreference, diff --git a/src/lib/gateway/GatewayClient.ts b/src/lib/gateway/GatewayClient.ts index 06c186a..0c1110c 100644 --- a/src/lib/gateway/GatewayClient.ts +++ b/src/lib/gateway/GatewayClient.ts @@ -3,7 +3,6 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { GatewayBrowserClient, - clearGatewayBrowserSessionStorage, type GatewayHelloOk, } from "./openclaw/GatewayBrowserClient"; import type { @@ -34,6 +33,10 @@ const gatewayDebugLog = (message: string, details?: Record) => console.info("[gateway-client]", message); }; import { probeCustomRuntime } from "@/lib/runtime/custom/http"; +export { + buildAgentMainSessionKey, + parseAgentIdFromSessionKey, +} from "@/lib/gateway/sessionKeys"; export type ReqFrame = { type: "req"; @@ -79,17 +82,6 @@ export const parseGatewayFrame = (raw: string): GatewayFrame | null => { } }; -export const buildAgentMainSessionKey = (agentId: string, mainKey: string) => { - const trimmedAgent = agentId.trim(); - const trimmedKey = mainKey.trim() || "main"; - return `agent:${trimmedAgent}:${trimmedKey}`; -}; - -export const parseAgentIdFromSessionKey = (sessionKey: string): string | null => { - const match = sessionKey.match(/^agent:([^:]+):/); - return match ? match[1] : null; -}; - export const isSameSessionKey = (a: string, b: string) => { const left = a.trim(); const right = b.trim(); @@ -1181,7 +1173,6 @@ export const useGatewayConnection = ( return; } client.disconnect(); - clearGatewayBrowserSessionStorage(); }, [client, selectedAdapterType]); const clearError = useCallback(() => { @@ -1198,7 +1189,6 @@ export const useGatewayConnection = ( (selectedAdapterType === "custom" || !hasLastKnownGoodState || !gatewayUrl.trim() || - (selectedAdapterType === "openclaw" && !token.trim()) || wasManualDisconnectRef.current || Boolean(error)); diff --git a/src/lib/gateway/agentConfig.ts b/src/lib/gateway/agentConfig.ts index d0b56e5..7f40ee0 100644 --- a/src/lib/gateway/agentConfig.ts +++ b/src/lib/gateway/agentConfig.ts @@ -1,4 +1,5 @@ -import { GatewayResponseError, type GatewayClient } from "@/lib/gateway/GatewayClient"; +import type { GatewayClient } from "@/lib/gateway/GatewayClient"; +import { GatewayResponseError } from "@/lib/gateway/errors"; export type AgentHeartbeatActiveHours = { start: string; diff --git a/src/lib/gateway/nodeGatewayClient.ts b/src/lib/gateway/nodeGatewayClient.ts index f5f5952..4fb135e 100644 --- a/src/lib/gateway/nodeGatewayClient.ts +++ b/src/lib/gateway/nodeGatewayClient.ts @@ -1,6 +1,7 @@ import { createHash, randomUUID } from "node:crypto"; import { getPublicKeyAsync, signAsync, utils } from "@noble/ed25519"; import { GatewayResponseError } from "@/lib/gateway/errors"; +export { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; type GatewayResponseFrame = { type: "res"; @@ -172,12 +173,6 @@ const withTimeout = async ( } }; -export const buildAgentMainSessionKey = (agentId: string, mainKey: string) => { - const trimmedAgent = agentId.trim(); - const trimmedKey = mainKey.trim() || "main"; - return `agent:${trimmedAgent}:${trimmedKey}`; -}; - export class NodeGatewayClient { private socket: WebSocket | null = null; private pending = new Map(); @@ -365,6 +360,10 @@ export class NodeGatewayClient { return response; } + async call(method: string, params: unknown): Promise { + return this.request(method, params); + } + close() { this.closed = true; this.rejectAllPending(new Error("Remote gateway client closed.")); @@ -377,6 +376,10 @@ export class NodeGatewayClient { } } + disconnect() { + this.close(); + } + private rejectAllPending(error: Error) { const entries = [...this.pending.values()]; this.pending.clear(); diff --git a/src/lib/gateway/sessionKeys.ts b/src/lib/gateway/sessionKeys.ts new file mode 100644 index 0000000..3269ba0 --- /dev/null +++ b/src/lib/gateway/sessionKeys.ts @@ -0,0 +1,10 @@ +export const buildAgentMainSessionKey = (agentId: string, mainKey: string) => { + const trimmedAgent = agentId.trim(); + const trimmedKey = mainKey.trim() || "main"; + return `agent:${trimmedAgent}:${trimmedKey}`; +}; + +export const parseAgentIdFromSessionKey = (sessionKey: string): string | null => { + const match = sessionKey.match(/^agent:([^:]+):/); + return match ? match[1] : null; +}; diff --git a/src/lib/office/eventTriggers.ts b/src/lib/office/eventTriggers.ts index fe34320..c767abd 100644 --- a/src/lib/office/eventTriggers.ts +++ b/src/lib/office/eventTriggers.ts @@ -16,8 +16,8 @@ import { import type { EventFrame } from "@/lib/gateway/GatewayClient"; import { isSameSessionKey, - parseAgentIdFromSessionKey, } from "@/lib/gateway/GatewayClient"; +import { parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; import type { OfficeCleaningCue, SessionEpochSnapshot, diff --git a/src/lib/office/gatewayPresence.ts b/src/lib/office/gatewayPresence.ts index 21d0f13..532fa3d 100644 --- a/src/lib/office/gatewayPresence.ts +++ b/src/lib/office/gatewayPresence.ts @@ -2,7 +2,7 @@ import type { SummaryPreviewSnapshot, SummaryStatusSnapshot, } from "@/features/agents/state/runtimeEventBridge"; -import { buildAgentMainSessionKey } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import type { OfficeAgentPresence, OfficePresenceSnapshot } from "@/lib/office/presence"; type GatewayAgentsListEntry = { diff --git a/src/lib/office/pictureModelGeneration.ts b/src/lib/office/pictureModelGeneration.ts index 54ad2a4..c1dfbfc 100644 --- a/src/lib/office/pictureModelGeneration.ts +++ b/src/lib/office/pictureModelGeneration.ts @@ -1,4 +1,5 @@ -import { buildAgentMainSessionKey, type GatewayClient } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; +import type { GatewayClient } from "@/lib/gateway/GatewayClient"; import { createGatewayAgent, removeGatewayAgentFromConfigOnly, @@ -12,6 +13,10 @@ import type { const PICTURE_MODEL_AGENT_NAME = "Picture Modeler"; export const MAX_PICTURE_MODEL_UPLOAD_BYTES = 12 * 1024 * 1024; +type GatewayLikeClient = { + call(method: string, params: unknown): Promise; +}; + const isRecord = (value: unknown): value is Record => Boolean(value && typeof value === "object" && !Array.isArray(value)); @@ -26,7 +31,7 @@ const resolveRunId = (payload: unknown): string => { return runId; }; -const resolveMainKey = async (client: GatewayClient): Promise => { +const resolveMainKey = async (client: GatewayLikeClient): Promise => { const result = (await client.call("agents.list", {})) as { mainKey?: unknown }; return typeof result?.mainKey === "string" && result.mainKey.trim() ? result.mainKey.trim() @@ -227,7 +232,7 @@ const parseRecipeFromHistory = ( }; export const generatePictureModelViaGateway = async (params: { - client: GatewayClient; + client: GatewayLikeClient; summary: { fileName: string; aspectRatio: number; @@ -239,15 +244,16 @@ export const generatePictureModelViaGateway = async (params: { }; }): Promise => { let helperAgentId: string | null = null; + const configClient = params.client as unknown as GatewayClient; try { const created = await createGatewayAgent({ - client: params.client, + client: configClient, name: `${PICTURE_MODEL_AGENT_NAME} ${Date.now()}`, }); helperAgentId = created.id; await updateGatewayAgentOverrides({ - client: params.client, + client: configClient, agentId: helperAgentId, overrides: { tools: { @@ -276,7 +282,7 @@ export const generatePictureModelViaGateway = async (params: { if (helperAgentId) { try { await removeGatewayAgentFromConfigOnly({ - client: params.client, + client: configClient, agentId: helperAgentId, }); } catch { diff --git a/src/lib/runtime/custom/provider.ts b/src/lib/runtime/custom/provider.ts index 554fb5d..5922c76 100644 --- a/src/lib/runtime/custom/provider.ts +++ b/src/lib/runtime/custom/provider.ts @@ -5,10 +5,7 @@ import type { GatewayStatus, } from "@/lib/gateway/GatewayClient"; import type { GatewayClient } from "@/lib/gateway/GatewayClient"; -import { - buildAgentMainSessionKey, - parseAgentIdFromSessionKey, -} from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey, parseAgentIdFromSessionKey } from "@/lib/gateway/sessionKeys"; import { fetchCustomRuntimeJson, normalizeCustomBaseUrl, diff --git a/src/lib/skills/install-gateway.ts b/src/lib/skills/install-gateway.ts index 031d8d8..a0e8437 100644 --- a/src/lib/skills/install-gateway.ts +++ b/src/lib/skills/install-gateway.ts @@ -1,4 +1,5 @@ -import { buildAgentMainSessionKey, type GatewayClient } from "@/lib/gateway/GatewayClient"; +import type { GatewayClient } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import { removeGatewayAgentFromConfigOnly, updateGatewayAgentOverrides, diff --git a/src/lib/skills/remove-gateway.ts b/src/lib/skills/remove-gateway.ts index 40bf23d..6c6fa77 100644 --- a/src/lib/skills/remove-gateway.ts +++ b/src/lib/skills/remove-gateway.ts @@ -1,4 +1,5 @@ -import { buildAgentMainSessionKey, type GatewayClient } from "@/lib/gateway/GatewayClient"; +import type { GatewayClient } from "@/lib/gateway/GatewayClient"; +import { buildAgentMainSessionKey } from "@/lib/gateway/sessionKeys"; import { removeGatewayAgentFromConfigOnly, updateGatewayAgentOverrides, diff --git a/tests/unit/pictureModelRoute.test.ts b/tests/unit/pictureModelRoute.test.ts index 4c1e2aa..c159c35 100644 --- a/tests/unit/pictureModelRoute.test.ts +++ b/tests/unit/pictureModelRoute.test.ts @@ -1,16 +1,16 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const connectMock = vi.fn(async function connect() {}); -const disconnectMock = vi.fn(function disconnect() {}); -const gatewayClientCtorMock = vi.fn(function GatewayClientMock( +const closeMock = vi.fn(function close() {}); +const gatewayClientCtorMock = vi.fn(function NodeGatewayClientMock( this: Record, ) { this.connect = connectMock; - this.disconnect = disconnectMock; + this.close = closeMock; }); -vi.mock("@/lib/gateway/GatewayClient", () => ({ - GatewayClient: gatewayClientCtorMock, +vi.mock("@/lib/gateway/nodeGatewayClient", () => ({ + NodeGatewayClient: gatewayClientCtorMock, })); vi.mock("@/lib/office/pictureModelGeneration", () => ({ @@ -164,7 +164,7 @@ describe("POST /api/office/picture-model", () => { expect(gatewayClientCtorMock).toHaveBeenCalledTimes(1); expect(connectMock).toHaveBeenCalledTimes(1); - expect(disconnectMock).toHaveBeenCalledTimes(1); + expect(closeMock).toHaveBeenCalledTimes(1); expect(response.status).toBe(200); await expect(response.json()).resolves.toMatchObject({ fileName: "demo.png", diff --git a/tests/unit/useGatewayConnection.test.ts b/tests/unit/useGatewayConnection.test.ts index 354db7b..5cba900 100644 --- a/tests/unit/useGatewayConnection.test.ts +++ b/tests/unit/useGatewayConnection.test.ts @@ -78,6 +78,7 @@ const setupAndImportHook = async (gatewayUrl: string | null) => { schedulePatch: (patch: unknown) => void; flushPending: () => Promise; }) => { + status: "disconnected" | "connecting" | "connected"; gatewayUrl: string; token: string; selectedAdapterType: "openclaw" | "hermes" | "demo" | "custom"; @@ -242,6 +243,65 @@ describe("useGatewayConnection", () => { expect(captured.clientName).toBe("webchat-ui"); }); + it("auto_reconnects_openclaw_with_last_known_good_without_a_browser_token", async () => { + const { useGatewayConnection, captured } = await setupAndImportHook(null); + const coordinator = { + loadSettings: async () => null, + loadSettingsEnvelope: async () => ({ + settings: { + version: 1, + gateway: { + url: "wss://pi5.myth-coho.ts.net", + token: "", + adapterType: "openclaw", + lastKnownGood: { + url: "wss://pi5.myth-coho.ts.net", + token: "", + adapterType: "openclaw", + }, + }, + focused: {}, + avatars: {}, + analytics: {}, + voiceReplies: {}, + office: {}, + deskAssignments: {}, + standup: {}, + taskBoard: {}, + }, + localGatewayDefaults: null, + }), + schedulePatch: () => {}, + flushPending: async () => {}, + }; + + const Probe = () => { + const state = useGatewayConnection(coordinator); + return createElement( + "div", + null, + createElement("div", { "data-testid": "status" }, state.status), + createElement("div", { "data-testid": "token" }, state.token), + createElement( + "div", + { "data-testid": "shouldPromptForConnect" }, + state.shouldPromptForConnect ? "yes" : "no" + ) + ); + }; + + render(createElement(Probe)); + + await waitFor(() => { + expect(captured.url).toBe("ws://localhost:3000/api/gateway/ws"); + }); + await waitFor(() => { + expect(screen.getByTestId("status")).toHaveTextContent("connected"); + }); + expect(screen.getByTestId("token")).toHaveTextContent(""); + expect(screen.getByTestId("shouldPromptForConnect")).toHaveTextContent("no"); + }); + it("keeps_control_ui_identity_for_local_openclaw_connections", async () => { const { useGatewayConnection, captured } = await setupAndImportHook(null); const coordinator = {