← Commit history

identity in-process via koffi (win32.js); no-wiggle skill documents the final mechanism

John Lauer ·5d7607bb41 ·2mo ago ·parent 5bf5835
4 files changed +643−174
dev-skills/pup-no-wiggle/SKILL.md+13
@@ -92,6 +92,19 @@ NOT the identity signal. The set-diff ("windows-after minus windows-before under the mutex") is the OLD birth capture and is DELETED — it could not disambiguate simultaneous births and refused. Do not bring it back. +### How it is implemented now (v2.0.313+): in-process, zero ab calls, zero PowerShell++`src/win32.js` binds user32 through **koffi** (prebuilt FFI, installed by AD's `npm install` on+spawn, no build step). Birth: `findChromeWindowByTitle(token)` polls EnumWindows + GetWindowText+every 50ms (2.5s cap) for the unique `PUPBIRTH-<seq>-<rand>` token, then the caller runs+`win32.setProp(hwnd, "AdomPupSid=<sessionId>")` the instant it knows the sessionId (measured:+match in ~130ms, tag 6ms later). Recovery: `findWindowByProp(name)` is one EnumWindows + GetProp+sweep. The 30s visibility sweep (`fixHiddenOrOffscreen`) is in-process too. PowerShell survives+only as the fallback when koffi fails to load (`/status` reports `win32.inProcess`). John's rule:+"i frown upon running powershell scripts cuz they're heavy and slow" - do not add new ones.+`desktop_enum_windows` is never used for identity; it remains only as the cached geometry snapshot+the periodic sweeps read (45s TTL when every session is resolved).+ ### Re-finding a window (runtime, and after a restart)  - **Fast path:** the in-memory hwnd, validated with `hwndBelongsToPup` before use.
src/package-lock.json+443−171
@@ -1,18 +1,19 @@ {   "name": "adom-puppeteer-bridge",-  "version": "1.9.291",+  "version": "2.0.248",   "lockfileVersion": 3,   "requires": true,   "packages": {     "": {       "name": "adom-puppeteer-bridge",-      "version": "1.9.291",+      "version": "2.0.248",       "dependencies": {+        "koffi": "^3.1.6",         "puppeteer": "^24.43.1"       },       "optionalDependencies": {         "keytar": "^7.9.0",-        "sharp": "^0.34.5"+        "sharp": "^0.35.0"       }     },     "node_modules/@babel/code-frame": {@@ -39,10 +40,9 @@       }     },     "node_modules/@emnapi/runtime": {-      "version": "1.9.0",-      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",-      "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",-      "license": "MIT",+      "version": "1.11.3",+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",+      "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",       "optional": true,       "dependencies": {         "tslib": "^2.4.0"@@ -59,57 +59,72 @@       }     },     "node_modules/@img/sharp-darwin-arm64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",-      "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz",+      "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==",       "cpu": [         "arm64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "darwin"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-darwin-arm64": "1.2.4"+        "@img/sharp-libvips-darwin-arm64": "1.3.2"       }     },     "node_modules/@img/sharp-darwin-x64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",-      "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz",+      "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==",       "cpu": [         "x64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "darwin"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-darwin-x64": "1.2.4"+        "@img/sharp-libvips-darwin-x64": "1.3.2"+      }+    },+    "node_modules/@img/sharp-freebsd-wasm32": {+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz",+      "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==",+      "optional": true,+      "os": [+        "freebsd"+      ],+      "dependencies": {+        "@img/sharp-wasm32": "0.35.3"+      },+      "engines": {+        "node": ">=20.9.0"+      },+      "funding": {+        "url": "https://opencollective.com/libvips"       }     },     "node_modules/@img/sharp-libvips-darwin-arm64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",-      "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz",+      "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==",       "cpu": [         "arm64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "darwin"@@ -119,13 +134,12 @@       }     },     "node_modules/@img/sharp-libvips-darwin-x64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",-      "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz",+      "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==",       "cpu": [         "x64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "darwin"@@ -135,13 +149,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-arm": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",-      "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz",+      "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==",       "cpu": [         "arm"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -151,13 +164,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-arm64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",-      "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz",+      "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==",       "cpu": [         "arm64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -167,13 +179,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-ppc64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",-      "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz",+      "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==",       "cpu": [         "ppc64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -183,13 +194,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-riscv64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",-      "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz",+      "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==",       "cpu": [         "riscv64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -199,13 +209,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-s390x": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",-      "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz",+      "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==",       "cpu": [         "s390x"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -215,13 +224,12 @@       }     },     "node_modules/@img/sharp-libvips-linux-x64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",-      "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz",+      "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==",       "cpu": [         "x64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -231,13 +239,12 @@       }     },     "node_modules/@img/sharp-libvips-linuxmusl-arm64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",-      "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz",+      "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==",       "cpu": [         "arm64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -247,13 +254,12 @@       }     },     "node_modules/@img/sharp-libvips-linuxmusl-x64": {-      "version": "1.2.4",-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",-      "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",+      "version": "1.3.2",+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz",+      "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==",       "cpu": [         "x64"       ],-      "license": "LGPL-3.0-or-later",       "optional": true,       "os": [         "linux"@@ -263,257 +269,485 @@       }     },     "node_modules/@img/sharp-linux-arm": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",-      "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz",+      "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==",       "cpu": [         "arm"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-arm": "1.2.4"+        "@img/sharp-libvips-linux-arm": "1.3.2"       }     },     "node_modules/@img/sharp-linux-arm64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",-      "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz",+      "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==",       "cpu": [         "arm64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-arm64": "1.2.4"+        "@img/sharp-libvips-linux-arm64": "1.3.2"       }     },     "node_modules/@img/sharp-linux-ppc64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",-      "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz",+      "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==",       "cpu": [         "ppc64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-ppc64": "1.2.4"+        "@img/sharp-libvips-linux-ppc64": "1.3.2"       }     },     "node_modules/@img/sharp-linux-riscv64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",-      "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz",+      "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==",       "cpu": [         "riscv64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-riscv64": "1.2.4"+        "@img/sharp-libvips-linux-riscv64": "1.3.2"       }     },     "node_modules/@img/sharp-linux-s390x": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",-      "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz",+      "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==",       "cpu": [         "s390x"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-s390x": "1.2.4"+        "@img/sharp-libvips-linux-s390x": "1.3.2"       }     },     "node_modules/@img/sharp-linux-x64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",-      "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz",+      "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==",       "cpu": [         "x64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linux-x64": "1.2.4"+        "@img/sharp-libvips-linux-x64": "1.3.2"       }     },     "node_modules/@img/sharp-linuxmusl-arm64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",-      "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz",+      "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==",       "cpu": [         "arm64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"+        "@img/sharp-libvips-linuxmusl-arm64": "1.3.2"       }     },     "node_modules/@img/sharp-linuxmusl-x64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",-      "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz",+      "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==",       "cpu": [         "x64"       ],-      "license": "Apache-2.0",       "optional": true,       "os": [         "linux"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-libvips-linuxmusl-x64": "1.2.4"+        "@img/sharp-libvips-linuxmusl-x64": "1.3.2"       }     },     "node_modules/@img/sharp-wasm32": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",-      "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz",+      "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==",+      "optional": true,+      "dependencies": {+        "@emnapi/runtime": "^1.11.1"+      },+      "engines": {+        "node": ">=20.9.0"+      },+      "funding": {+        "url": "https://opencollective.com/libvips"+      }+    },+    "node_modules/@img/sharp-webcontainers-wasm32": {+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz",+      "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==",       "cpu": [         "wasm32"       ],-      "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",       "optional": true,       "dependencies": {-        "@emnapi/runtime": "^1.7.0"+        "@img/sharp-wasm32": "0.35.3"       },       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       }     },     "node_modules/@img/sharp-win32-arm64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",-      "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz",+      "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==",       "cpu": [         "arm64"       ],-      "license": "Apache-2.0 AND LGPL-3.0-or-later",       "optional": true,       "os": [         "win32"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       }     },     "node_modules/@img/sharp-win32-ia32": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",-      "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz",+      "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==",       "cpu": [         "ia32"       ],-      "license": "Apache-2.0 AND LGPL-3.0-or-later",       "optional": true,       "os": [         "win32"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": "^20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       }     },     "node_modules/@img/sharp-win32-x64": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",-      "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz",+      "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==",       "cpu": [         "x64"       ],-      "license": "Apache-2.0 AND LGPL-3.0-or-later",       "optional": true,       "os": [         "win32"       ],       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       }     },+    "node_modules/@koromix/koffi-darwin-arm64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-darwin-arm64/-/koffi-darwin-arm64-3.1.6.tgz",+      "integrity": "sha512-8FHyXGCZN7/iQf4f7W5BRysmtdlAFvSx6FpmX4u6wmkZiX/2e9hIRdGLiZYlHGudlcA18UmXB/cMiyhJ7fJkzA==",+      "cpu": [+        "arm64"+      ],+      "optional": true,+      "os": [+        "darwin"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-darwin-x64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-darwin-x64/-/koffi-darwin-x64-3.1.6.tgz",+      "integrity": "sha512-uzx/jqFQuSHqgg1zaRidTBTCfj8Y9M0SDTO8HeoI9s9fJhiJ1mbB9TTwJO5c2hiMnuWg2m1byczC8BaIH6cG/w==",+      "cpu": [+        "x64"+      ],+      "optional": true,+      "os": [+        "darwin"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-freebsd-arm64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-arm64/-/koffi-freebsd-arm64-3.1.6.tgz",+      "integrity": "sha512-PjpTVrsCK5YTtixOw7VsseYXJOyoY6k0qBt+bf0T9h3wyV06y73rALsorFDDEoYpLUBZO7R6EIMs6CpUrEkNTQ==",+      "cpu": [+        "arm64"+      ],+      "optional": true,+      "os": [+        "freebsd"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-freebsd-ia32": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-ia32/-/koffi-freebsd-ia32-3.1.6.tgz",+      "integrity": "sha512-ETYwL820HtFwYoOVzgyvmFmzTHRo9DJtGYTxa5Nb7ajaa5ldCum0jbmUJ3PMECxFTLxD5Q6PYZ8Xbp101bGeSg==",+      "cpu": [+        "ia32"+      ],+      "optional": true,+      "os": [+        "freebsd"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-freebsd-x64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-x64/-/koffi-freebsd-x64-3.1.6.tgz",+      "integrity": "sha512-BkqxkNXhAAT9toU2stvLwx1iKHPDx7h08NCICyBbjYEXkCAEr84igTkpE5V3XJ9xZZ2gKll7VvdhxorHtHUqZw==",+      "cpu": [+        "x64"+      ],+      "optional": true,+      "os": [+        "freebsd"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-linux-arm64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-arm64/-/koffi-linux-arm64-3.1.6.tgz",+      "integrity": "sha512-cM4XPm9ljbCrcPgXjzFYjDNxDUvvuR7TCYaEoo1AKjwZT/vmWhu2xN3pomfbsHh6aVn80SFA3enufQnaETW1rQ==",+      "cpu": [+        "arm64"+      ],+      "optional": true,+      "os": [+        "linux"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-linux-ia32": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-ia32/-/koffi-linux-ia32-3.1.6.tgz",+      "integrity": "sha512-l1SVTpO10iaQt8slbowJpzK4fbwQZ7ufj9tmCyAcIwWUpyAbPS83mJMctU72If6N9/gCS2wuRqwnYB2uPLLhLg==",+      "cpu": [+        "ia32"+      ],+      "optional": true,+      "os": [+        "linux"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-linux-loong64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-loong64/-/koffi-linux-loong64-3.1.6.tgz",+      "integrity": "sha512-KpTJpMSbIdCVFU26ynt0xy4x15h+y6AwPJxj2+iVxJhCzJf4oisCPc0YH2VnutuLV2nVzSrFm7sL/WSOqPgkXw==",+      "cpu": [+        "loong64"+      ],+      "optional": true,+      "os": [+        "linux"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-linux-riscv64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-riscv64/-/koffi-linux-riscv64-3.1.6.tgz",+      "integrity": "sha512-YdFNpsywnXiYOYQlDAatf7TJLnspbGXdmfwIZhf82kbKSflYUsq4tI6NmoUOzM89oIWDGpYqd4Hz3xL7tsyXsw==",+      "cpu": [+        "riscv64"+      ],+      "optional": true,+      "os": [+        "linux"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-linux-x64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-x64/-/koffi-linux-x64-3.1.6.tgz",+      "integrity": "sha512-Xx5mpr9VcaMCXfvbqIiLIWIL9Iuu6F4r3iMXg7+zZCqYUFZPFwJgiDQBLxctHv2OYgIfAoaaHMW0GC1cJkHfbA==",+      "cpu": [+        "x64"+      ],+      "optional": true,+      "os": [+        "linux"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-openbsd-ia32": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-openbsd-ia32/-/koffi-openbsd-ia32-3.1.6.tgz",+      "integrity": "sha512-39Np4QTxhTlTT6RRveIeP+TnbzrwuDJ0UMyHxEZ+oGtzmb9GqWhl9T1oyehG6v/O+c4BffafG2NwLkCZ7tDKWw==",+      "cpu": [+        "ia32"+      ],+      "optional": true,+      "os": [+        "openbsd"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-openbsd-x64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-openbsd-x64/-/koffi-openbsd-x64-3.1.6.tgz",+      "integrity": "sha512-3EynGn3ycQRqaMWGmUJ0tdtuQdStByqSy/tJ0ZGKWizbMGdFAE73YpgLsyd8BDvwnKWytVG/OLNb5nDHpfd9Dg==",+      "cpu": [+        "x64"+      ],+      "optional": true,+      "os": [+        "openbsd"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-win32-arm64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-win32-arm64/-/koffi-win32-arm64-3.1.6.tgz",+      "integrity": "sha512-27FdPPRtT4xbO9bsd2OZa95M5YQ7bcJ8QjCRO57UUMI21REfkDegjqKwqo/CFlugxXlJf5IYtG2rq4BEYIrvxg==",+      "cpu": [+        "arm64"+      ],+      "optional": true,+      "os": [+        "win32"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-win32-ia32": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-win32-ia32/-/koffi-win32-ia32-3.1.6.tgz",+      "integrity": "sha512-5mVelLKVDup4eoxZOpCzCyMPxoctsg+Qe4J9O5BP4KbBEdqoOEqaNEBBRgNzXcdr2g+GGfmIUo+oVR1NvIdiJw==",+      "cpu": [+        "ia32"+      ],+      "optional": true,+      "os": [+        "win32"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },+    "node_modules/@koromix/koffi-win32-x64": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/@koromix/koffi-win32-x64/-/koffi-win32-x64-3.1.6.tgz",+      "integrity": "sha512-lPKjAaHz0aoiZXT/wDVqH+joR5y3lCZj1s9Bk5qx/DGRq+0MK8Ib8VoqiBOrJ69NG5AsJSvn0tQDcSqfRgLmBQ==",+      "cpu": [+        "x64"+      ],+      "optional": true,+      "os": [+        "win32"+      ],+      "funding": {+        "url": "https://liberapay.com/Koromix"+      }+    },     "node_modules/@puppeteer/browsers": {       "version": "2.13.2",       "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz",@@ -1190,9 +1424,9 @@       "optional": true     },     "node_modules/ip-address": {-      "version": "10.2.0",-      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",-      "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",+      "version": "10.5.0",+      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz",+      "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==",       "engines": {         "node": ">= 12"       }@@ -1218,10 +1452,19 @@       "license": "MIT"     },     "node_modules/js-yaml": {-      "version": "4.1.1",-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",-      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",-      "license": "MIT",+      "version": "4.3.1",+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",+      "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",+      "funding": [+        {+          "type": "github",+          "url": "https://github.com/sponsors/puzrin"+        },+        {+          "type": "github",+          "url": "https://github.com/sponsors/nodeca"+        }+      ],       "dependencies": {         "argparse": "^2.0.1"       },@@ -1247,6 +1490,32 @@         "prebuild-install": "^7.0.1"       }     },+    "node_modules/koffi": {+      "version": "3.1.6",+      "resolved": "https://registry.npmjs.org/koffi/-/koffi-3.1.6.tgz",+      "integrity": "sha512-ln60chEb3o7Du1ayjwl6BFiNN1wZK+3cTM2wWGiHLEzCY/FdTIN1ER5VWDwHq7J/j4tSnnrHaH5ABS1EO6+6ag==",+      "hasInstallScript": true,+      "funding": {+        "url": "https://liberapay.com/Koromix"+      },+      "optionalDependencies": {+        "@koromix/koffi-darwin-arm64": "3.1.6",+        "@koromix/koffi-darwin-x64": "3.1.6",+        "@koromix/koffi-freebsd-arm64": "3.1.6",+        "@koromix/koffi-freebsd-ia32": "3.1.6",+        "@koromix/koffi-freebsd-x64": "3.1.6",+        "@koromix/koffi-linux-arm64": "3.1.6",+        "@koromix/koffi-linux-ia32": "3.1.6",+        "@koromix/koffi-linux-loong64": "3.1.6",+        "@koromix/koffi-linux-riscv64": "3.1.6",+        "@koromix/koffi-linux-x64": "3.1.6",+        "@koromix/koffi-openbsd-ia32": "3.1.6",+        "@koromix/koffi-openbsd-x64": "3.1.6",+        "@koromix/koffi-win32-arm64": "3.1.6",+        "@koromix/koffi-win32-ia32": "3.1.6",+        "@koromix/koffi-win32-x64": "3.1.6"+      }+    },     "node_modules/lines-and-columns": {       "version": "1.2.4",       "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",@@ -1622,10 +1891,9 @@       "optional": true     },     "node_modules/semver": {-      "version": "7.7.4",-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",-      "license": "ISC",+      "version": "7.8.5",+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",       "bin": {         "semver": "bin/semver.js"       },@@ -1634,48 +1902,52 @@       }     },     "node_modules/sharp": {-      "version": "0.34.5",-      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",-      "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",-      "hasInstallScript": true,-      "license": "Apache-2.0",+      "version": "0.35.3",+      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz",+      "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==",       "optional": true,       "dependencies": {-        "@img/colour": "^1.0.0",+        "@img/colour": "^1.1.0",         "detect-libc": "^2.1.2",-        "semver": "^7.7.3"+        "semver": "^7.8.5"       },       "engines": {-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"+        "node": ">=20.9.0"       },       "funding": {         "url": "https://opencollective.com/libvips"       },       "optionalDependencies": {-        "@img/sharp-darwin-arm64": "0.34.5",-        "@img/sharp-darwin-x64": "0.34.5",-        "@img/sharp-libvips-darwin-arm64": "1.2.4",-        "@img/sharp-libvips-darwin-x64": "1.2.4",-        "@img/sharp-libvips-linux-arm": "1.2.4",-        "@img/sharp-libvips-linux-arm64": "1.2.4",-        "@img/sharp-libvips-linux-ppc64": "1.2.4",-        "@img/sharp-libvips-linux-riscv64": "1.2.4",-        "@img/sharp-libvips-linux-s390x": "1.2.4",-        "@img/sharp-libvips-linux-x64": "1.2.4",-        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",-        "@img/sharp-libvips-linuxmusl-x64": "1.2.4",-        "@img/sharp-linux-arm": "0.34.5",-        "@img/sharp-linux-arm64": "0.34.5",-        "@img/sharp-linux-ppc64": "0.34.5",-        "@img/sharp-linux-riscv64": "0.34.5",-        "@img/sharp-linux-s390x": "0.34.5",-        "@img/sharp-linux-x64": "0.34.5",-        "@img/sharp-linuxmusl-arm64": "0.34.5",-        "@img/sharp-linuxmusl-x64": "0.34.5",-        "@img/sharp-wasm32": "0.34.5",-        "@img/sharp-win32-arm64": "0.34.5",-        "@img/sharp-win32-ia32": "0.34.5",-        "@img/sharp-win32-x64": "0.34.5"+        "@img/sharp-darwin-arm64": "0.35.3",+        "@img/sharp-darwin-x64": "0.35.3",+        "@img/sharp-freebsd-wasm32": "0.35.3",+        "@img/sharp-libvips-darwin-arm64": "1.3.2",+        "@img/sharp-libvips-darwin-x64": "1.3.2",+        "@img/sharp-libvips-linux-arm": "1.3.2",+        "@img/sharp-libvips-linux-arm64": "1.3.2",+        "@img/sharp-libvips-linux-ppc64": "1.3.2",+        "@img/sharp-libvips-linux-riscv64": "1.3.2",+        "@img/sharp-libvips-linux-s390x": "1.3.2",+        "@img/sharp-libvips-linux-x64": "1.3.2",+        "@img/sharp-libvips-linuxmusl-arm64": "1.3.2",+        "@img/sharp-libvips-linuxmusl-x64": "1.3.2",+        "@img/sharp-linux-arm": "0.35.3",+        "@img/sharp-linux-arm64": "0.35.3",+        "@img/sharp-linux-ppc64": "0.35.3",+        "@img/sharp-linux-riscv64": "0.35.3",+        "@img/sharp-linux-s390x": "0.35.3",+        "@img/sharp-linux-x64": "0.35.3",+        "@img/sharp-linuxmusl-arm64": "0.35.3",+        "@img/sharp-linuxmusl-x64": "0.35.3",+        "@img/sharp-webcontainers-wasm32": "0.35.3",+        "@img/sharp-win32-arm64": "0.35.3",+        "@img/sharp-win32-ia32": "0.35.3",+        "@img/sharp-win32-x64": "0.35.3"+      },+      "peerDependenciesMeta": {+        "@types/node": {+          "optional": true+        }       }     },     "node_modules/simple-concat": {
src/package.json+13−3
@@ -1,16 +1,26 @@ {   "name": "adom-puppeteer-bridge",-  "version": "1.9.291",+  "version": "2.0.248",   "description": "Puppeteer Bridge \u2014 persistent browser instance for visual debugging via Adom Desktop",   "main": "server.js",   "scripts": {     "start": "node server.js"   },   "dependencies": {-    "puppeteer": "^24.43.1"+    "puppeteer": "^24.43.1",+    "koffi": "^3.1.6"   },   "optionalDependencies": {     "keytar": "^7.9.0",-    "sharp": "^0.34.5"+    "sharp": "^0.35.0"+  },+  "overrides": {+    "js-yaml": "^4.3.1",+    "ip-address": "^10.3.1"+  },+  "_securityNotes": {+    "why": "adom/pup-bridge#57 (dependabot). sharp is ours (optional dep) so it is bumped directly; js-yaml (via cosmiconfig) and ip-address (via socks) are TRANSITIVE under puppeteer, so they are pinned with overrides rather than by forking the tree.",+    "extract-zip": "GHSA-jmr9-qjv8-65gv has NO patched release upstream. It reaches us only through @puppeteer/browsers, which uses it to unpack the Chrome-for-Testing archive that pup itself downloads from Google's CDN over HTTPS \u2014 never user-supplied or third-party zips \u2014 so the symlink-traversal path is not reachable in this bridge. Re-evaluate if that ever changes.",+    "reviewed": "2026-08-19"   } }
src/win32.jsadded+174
@@ -0,0 +1,174 @@+'use strict';+// In-process Win32 calls for window identity (v2.0.310). John: "i frown upon running powershell+// scripts cuz they're heavy and slow." Every PowerShell spawn cost a process + an Add-Type compile+// (hundreds of ms). These are direct user32 calls through koffi (prebuilt FFI, no build step): a+// SetProp or a full EnumWindows+GetProp sweep is microseconds, synchronous, and leaves no trace in+// ab's activity log. Identity doctrine (dev-skills/pup-no-wiggle): the window PROPERTY is the id.+//+// Every export degrades to null/false when koffi is unavailable (non-Windows, or the module failed+// to install), so callers can fall back to the PowerShell path and nothing hard-fails.++let user32 = null;+let fn = null;+let loadError = null;++function load() {+  if (fn || loadError) return !!fn;+  if (process.platform !== 'win32') { loadError = 'not win32'; return false; }+  try {+    const koffi = require('koffi');+    user32 = koffi.load('user32.dll');+    const EnumProc = koffi.proto('bool __stdcall PupEnumProc(intptr_t hwnd, intptr_t lparam)');+    fn = {+      koffi,+      SetPropW: user32.func('bool __stdcall SetPropW(intptr_t hwnd, const char16_t* name, intptr_t data)'),+      GetPropW: user32.func('intptr_t __stdcall GetPropW(intptr_t hwnd, const char16_t* name)'),+      RemovePropW: user32.func('intptr_t __stdcall RemovePropW(intptr_t hwnd, const char16_t* name)'),+      EnumWindows: user32.func('bool __stdcall EnumWindows(PupEnumProc* cb, intptr_t lparam)'),+      IsWindow: user32.func('bool __stdcall IsWindow(intptr_t hwnd)'),+      GetWindowThreadProcessId: user32.func('uint32_t __stdcall GetWindowThreadProcessId(intptr_t hwnd, _Out_ uint32_t* pid)'),+      GetWindowTextW: user32.func('int __stdcall GetWindowTextW(intptr_t hwnd, void* buf, int n)'),+      GetClassNameW: user32.func('int __stdcall GetClassNameW(intptr_t hwnd, void* buf, int n)'),+      IsWindowVisible: user32.func('bool __stdcall IsWindowVisible(intptr_t hwnd)'),+      IsIconic: user32.func('bool __stdcall IsIconic(intptr_t hwnd)'),+      ShowWindow: user32.func('bool __stdcall ShowWindow(intptr_t hwnd, int cmd)'),+      GetWindowLongW: user32.func('int32_t __stdcall GetWindowLongW(intptr_t hwnd, int idx)'),+      GetSystemMetrics: user32.func('int __stdcall GetSystemMetrics(int idx)'),+      SetWindowPos: user32.func('bool __stdcall SetWindowPos(intptr_t hwnd, intptr_t after, int x, int y, int cx, int cy, uint32_t flags)'),+      GetWindowRect: user32.func('bool __stdcall GetWindowRect(intptr_t hwnd, void* rect)'),+      GetWindowPlacement: user32.func('bool __stdcall GetWindowPlacement(intptr_t hwnd, void* wp)'),+      EnumProc,+    };+    return true;+  } catch (e) { loadError = e && e.message ? e.message : String(e); fn = null; return false; }+}++// Tag a window with a named property (value 1). The property lives with the window, in Chrome's+// process, so it outlives pup restarts. Returns true on success, null when koffi is unavailable.+function setProp(hwnd, name) {+  if (!load()) return null;+  try { return !!fn.SetPropW(Number(hwnd), String(name), 1); } catch (e) { return false; }+}++function hasProp(hwnd, name) {+  if (!load()) return null;+  try { return Number(fn.GetPropW(Number(hwnd), String(name))) !== 0; } catch (e) { return false; }+}++function removeProp(hwnd, name) {+  if (!load()) return null;+  try { fn.RemovePropW(Number(hwnd), String(name)); return true; } catch (e) { return false; }+}++function isWindow(hwnd) {+  if (!load()) return null;+  try { return !!fn.IsWindow(Number(hwnd)); } catch (e) { return false; }+}++function windowPid(hwnd) {+  if (!load()) return null;+  try { const out = [0]; fn.GetWindowThreadProcessId(Number(hwnd), out); return Number(out[0]) || null; } catch (e) { return null; }+}++// Find the ONE top-level window carrying the property. Only pup ever sets these names, so no class+// or geometry test is needed: the property IS the proof. Returns the hwnd, 0 when none, null when+// koffi is unavailable.+function findWindowByProp(name) {+  if (!load()) return null;+  let found = 0;+  const cb = fn.koffi.register((hwnd) => {+    try { if (Number(fn.GetPropW(Number(hwnd), String(name))) !== 0) { found = Number(hwnd); return false; } } catch (e) {}+    return true;+  }, fn.koffi.pointer(fn.EnumProc));+  try { fn.EnumWindows(cb, 0); } catch (e) {} finally { try { fn.koffi.unregister(cb); } catch (e) {} }+  return found;+}++// Rebuild the whole sessionId -> hwnd map in one sweep: every top-level window whose property+// name starts with the prefix. Used at recovery so a restart never enumerates more than once.+function findAllByPropPrefix(prefix, candidates) {+  // Win32 has no "list my props by prefix" without EnumPropsEx; callers pass the sessionIds they+  // want resolved (cheap: one GetProp per (window, sessionId) pair, all in-process).+  if (!load()) return null;+  const out = new Map();+  const names = candidates.map(s => [s, prefix + s]);+  const cb = fn.koffi.register((hwnd) => {+    for (const [sid, nm] of names) {+      try { if (!out.has(sid) && Number(fn.GetPropW(Number(hwnd), nm)) !== 0) { out.set(sid, Number(hwnd)); break; } } catch (e) {}+    }+    return out.size < names.length;+  }, fn.koffi.pointer(fn.EnumProc));+  try { fn.EnumWindows(cb, 0); } catch (e) {} finally { try { fn.koffi.unregister(cb); } catch (e) {} }+  return out;+}++// UTF-16 out-buffers: pass a Buffer as the void* and decode exactly the returned length.+function windowText(hwnd) {+  if (!load()) return null;+  try { const buf = Buffer.alloc(1024); const n = fn.GetWindowTextW(Number(hwnd), buf, 512); return n > 0 ? buf.toString('utf16le', 0, n * 2) : ''; } catch (e) { return ''; }+}+function className(hwnd) {+  if (!load()) return null;+  try { const buf = Buffer.alloc(256); const n = fn.GetClassNameW(Number(hwnd), buf, 128); return n > 0 ? buf.toString('utf16le', 0, n * 2) : ''; } catch (e) { return ''; }+}+// Find the top-level Chrome frame whose title contains `token` (the unique birth title). Returns+// {hwnd, pid} or null when none; undefined when koffi is unavailable. One in-process pass.+function findChromeWindowByTitle(token) {+  if (!load()) return undefined;+  let hit = null;+  const cb = fn.koffi.register((hwnd) => {+    try {+      if (className(hwnd) !== 'Chrome_WidgetWin_1') return true;+      const t = windowText(hwnd);+      if (t && t.includes(token)) { hit = { hwnd: Number(hwnd), pid: windowPid(hwnd) }; return false; }+    } catch (e) {}+    return true;+  }, fn.koffi.pointer(fn.EnumProc));+  try { fn.EnumWindows(cb, 0); } catch (e) {} finally { try { fn.koffi.unregister(cb); } catch (e) {} }+  return hit;+}+function windowRect(hwnd) {+  if (!load()) return null;+  try { const b = Buffer.alloc(16); if (!fn.GetWindowRect(Number(hwnd), b)) return null; return { L: b.readInt32LE(0), T: b.readInt32LE(4), R: b.readInt32LE(8), B: b.readInt32LE(12) }; } catch (e) { return null; }+}+// The restore rect from WINDOWPLACEMENT (a minimized window reports the -32000 placeholder from+// GetWindowRect, so position must be judged from the placement's rcNormalPosition).+function normalRect(hwnd) {+  if (!load()) return null;+  try { const b = Buffer.alloc(44); b.writeInt32LE(44, 0); if (!fn.GetWindowPlacement(Number(hwnd), b)) return null; return { L: b.readInt32LE(28), T: b.readInt32LE(32), R: b.readInt32LE(36), B: b.readInt32LE(40) }; } catch (e) { return null; }+}+// The visibility invariant, in-process (v2.0.314; was one PowerShell + Add-Type per 30s sweep): no+// titled, captioned top-level window of a pup-owned Chrome process may be HIDDEN or OFF-SCREEN.+// Hidden -> SW_SHOWNOACTIVATE. Off the virtual desktop -> moved back at (vx+10, vy+10) without+// activation or z-change. Minimized is the user's choice and is left alone. Returns repair records.+function fixHiddenOrOffscreen(pids) {+  if (!load()) return null;+  const pidSet = new Set(pids.map(Number));+  const out = [];+  const vx = fn.GetSystemMetrics(76), vy = fn.GetSystemMetrics(77), vw = fn.GetSystemMetrics(78), vh = fn.GetSystemMetrics(79);+  const cb = fn.koffi.register((hwnd) => {+    try {+      const h = Number(hwnd);+      if (!pidSet.has(windowPid(h))) return true;+      if ((fn.GetWindowLongW(h, -16) & 0x00C00000) === 0) return true;   // WS_CAPTION: real windows only+      const title = windowText(h); if (!title) return true;+      if (!fn.IsWindowVisible(h)) { fn.ShowWindow(h, 4); out.push({ kind: fn.IsWindowVisible(h) ? 'RESHOWN' : 'RESHOW-FAILED', hwnd: h, title }); return true; }+      if (fn.IsIconic(h)) return true;+      const r = normalRect(h); if (!r) return true;+      const L = r.L, T = r.T, W = r.R - r.L, H = r.B - r.T;+      if (L < vx + vw && L + W > vx && T < vy + vh && T + H > vy) return true;   // overlaps the desktop: fine+      const nw = (W > 200 && W <= vw) ? W : (vw - 20), nh = (H > 200 && H <= vh) ? H : (vh - 20);+      fn.SetWindowPos(h, 0, vx + 10, vy + 10, nw, nh, 0x14);   // NOACTIVATE | NOZORDER+      const c = windowRect(h);+      const fixedNow = !!(c && c.L < vx + vw && c.R > vx && c.T < vy + vh && c.B > vy);+      out.push({ kind: fixedNow ? 'MOVED-ONSCREEN' : 'MOVE-FAILED', hwnd: h, title });+    } catch (e) {}+    return true;+  }, fn.koffi.pointer(fn.EnumProc));+  try { fn.EnumWindows(cb, 0); } catch (e) {} finally { try { fn.koffi.unregister(cb); } catch (e) {} }+  return out;+}+function available() { return load(); }+function why() { return loadError; }++module.exports = { available, why, setProp, hasProp, removeProp, isWindow, windowPid, windowText, className, findWindowByProp, findAllByPropPrefix, findChromeWindowByTitle, windowRect, normalRect, fixHiddenOrOffscreen };