diff --git a/bun.lock b/bun.lock index aa2c52d5608..1bd154090f0 100644 --- a/bun.lock +++ b/bun.lock @@ -415,6 +415,7 @@ "name": "@opencode-ai/desktop", "version": "1.18.15", "dependencies": { + "@effect/platform-node": "catalog:", "@zip.js/zip.js": "2.7.62", "drizzle-orm": "catalog:", "effect": "catalog:", @@ -605,6 +606,21 @@ "solid-js", ], }, + "packages/posts": { + "name": "@opencode-ai/posts", + "dependencies": { + "@astrojs/mdx": "7.0.5", + "@fontsource/commit-mono": "5.3.0", + "astro": "7.1.3", + }, + "devDependencies": { + "@astrojs/check": "0.9.6", + "@astrojs/cloudflare": "14.1.4", + "@types/bun": "catalog:", + "typescript": "catalog:", + "wrangler": "4.110.0", + }, + }, "packages/protocol": { "name": "@opencode-ai/protocol", "version": "1.17.11", @@ -1242,7 +1258,7 @@ "@astrojs/check": ["@astrojs/check@0.9.6", "", { "dependencies": { "@astrojs/language-server": "^2.16.1", "chokidar": "^4.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA=="], - "@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="], + "@astrojs/cloudflare": ["@astrojs/cloudflare@14.1.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.1", "@astrojs/underscore-redirects": "1.0.3", "@cloudflare/vite-plugin": "^1.39.0", "piccolore": "^0.1.3", "vite": "^8.0.13" }, "peerDependencies": { "astro": "^7.0.0", "wrangler": "^4.83.0" } }, "sha512-Zyo1E/5/dmegmKODbwUzOd67euNd6oKcbllhAwe3uFFprA7mIFNkpF6yBBa78vIkpMNuU13MkBJHCLd+yJSuEA=="], "@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], @@ -1268,7 +1284,7 @@ "@astrojs/compiler-rs": ["@astrojs/compiler-rs@0.3.2", "", { "dependencies": { "@astrojs/compiler-binding": "0.3.2" } }, "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg=="], - "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.1", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], "@astrojs/language-server": ["@astrojs/language-server@2.16.13", "", { "dependencies": { "@astrojs/compiler": "^2.13.1", "@astrojs/yaml2ts": "^0.2.4", "@jridgewell/sourcemap-codec": "^1.5.5", "@volar/kit": "~2.4.28", "@volar/language-core": "~2.4.28", "@volar/language-server": "~2.4.28", "@volar/language-service": "~2.4.28", "muggle-string": "^0.4.1", "tinyglobby": "^0.2.16", "volar-service-css": "0.0.71", "volar-service-emmet": "0.0.71", "volar-service-html": "0.0.71", "volar-service-prettier": "0.0.71", "volar-service-typescript": "0.0.71", "volar-service-typescript-twoslash-queries": "0.0.71", "volar-service-yaml": "0.0.71", "vscode-html-languageservice": "^5.6.2", "vscode-uri": "^3.1.0" }, "peerDependencies": { "prettier": "^3.0.0", "prettier-plugin-astro": ">=0.11.0" }, "optionalPeers": ["prettier", "prettier-plugin-astro"], "bin": { "astro-ls": "./bin/nodeServer.js" } }, "sha512-ekOa+CYprEq5n4EJC1qTIAhLk49HZIUQuFwrEuF+3JK/pdMaYnWoREFUI2A0KEPOJiFA2kamBzKzbYljDvUxLg=="], @@ -1276,7 +1292,7 @@ "@astrojs/markdown-satteri": ["@astrojs/markdown-satteri@0.3.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.1", "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "satteri": "^0.9.1" } }, "sha512-6Lvt/bQZEBW+zzdhPblvfZEy5PGEYJaUsUqaCgwHeRPxZJL1gc9I+DRLKWJjjYTWDzVUTzXlMq4WwSK+X34CVw=="], - "@astrojs/mdx": ["@astrojs/mdx@4.3.14", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.11", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA=="], + "@astrojs/mdx": ["@astrojs/mdx@7.0.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/markdown-remark": "7.2.2", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "^0.3.1", "astro": "^7.0.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw=="], "@astrojs/node": ["@astrojs/node@11.1.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^7.0.0" } }, "sha512-utAqBE1eIcAhyIkBUnNIWrzj4vyTiOlbbqcjUSE2Yz5XrMu+9sQdBwp/UmZXfJ9wjaEIHf26HInNLUUmTOFeEw=="], @@ -1290,9 +1306,9 @@ "@astrojs/starlight": ["@astrojs/starlight@0.34.3", "", { "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", "@astrojs/sitemap": "^3.3.0", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", "astro-expressive-code": "^0.41.1", "bcp-47": "^2.1.0", "hast-util-from-html": "^2.0.1", "hast-util-select": "^6.0.2", "hast-util-to-string": "^3.0.0", "hastscript": "^9.0.0", "i18next": "^23.11.5", "js-yaml": "^4.1.0", "klona": "^2.0.6", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", "pagefind": "^1.3.0", "rehype": "^13.0.1", "rehype-format": "^5.0.0", "remark-directive": "^3.0.0", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile": "^6.0.2" }, "peerDependencies": { "astro": "^5.5.0" } }, "sha512-MAuD3NF+E+QXJJuVKofoR6xcPTP4BJmYWeOBd03udVdubNGVnPnSWVZAi+ZtnTofES4+mJdp8BNGf+ubUxkiiA=="], - "@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], + "@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], - "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.0", "", {}, "sha512-qZxHwVnmb5FXuvRsaIGaqWgnftjCuMY+GSbaVZdBmE4j8AfgPqKPxYp8SUERyJcjpKCEmO4wD6ybuGH8A2kVRQ=="], + "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.3", "", {}, "sha512-cxnGSw+sJigBLdX4TMSZKkzV6C3gMLJMucDk2W+n281Xhie68T2/9f1+1NMNDCZsc5i0FED7Qt5I10g2O9wtZg=="], "@astrojs/vercel": ["@astrojs/vercel@11.0.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@vercel/analytics": "^1.6.1", "@vercel/functions": "^3.4.3", "@vercel/nft": "^1.3.2", "@vercel/routing-utils": "^5.3.3", "esbuild": "^0.28.0", "tinyglobby": "^0.2.15" }, "peerDependencies": { "astro": "^7.0.0" } }, "sha512-cag6EfSclaXiya6R7TT52m+IechBXt+m57wtj1L0ijGJg8cToN3Tuhj9o+JBGk+IaH0wp1Eh/Qlc2NZKs/Q3HA=="], @@ -1516,7 +1532,7 @@ "@bufbuild/protoplugin": ["@bufbuild/protoplugin@2.13.0", "", { "dependencies": { "@bufbuild/protobuf": "2.13.0", "@typescript/vfs": "^1.6.2", "typescript": "5.4.5" } }, "sha512-32eMChKaL/A8Hh5AfMmXSdnuyznN85uoEjoyWiWeRrvtQOtpqX/v1R9PDe0g9vMIgzznK9inMT3CUaal0kjLUQ=="], - "@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], + "@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], @@ -1710,6 +1726,8 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], + "@fontsource/commit-mono": ["@fontsource/commit-mono@5.3.0", "", {}, "sha512-Bs+zlEWHDgk/cCVWEMJJaA7LbBU3Ixi5Zums+SSwMKdm2N7RJ4P7jCJTwsMHRt5WFiXYB0tu0q9HMfkn0YsLsQ=="], + "@fontsource/ibm-plex-mono": ["@fontsource/ibm-plex-mono@5.2.5", "", {}, "sha512-G09N3GfuT9qj3Ax2FDZvKqZttzM3v+cco2l8uXamhKyXLdmlaUDH5o88/C3vtTHj2oT7yRKsvxz9F+BXbWKMYA=="], "@fontsource/inter": ["@fontsource/inter@5.3.0", "", {}, "sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g=="], @@ -1736,47 +1754,47 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.3", "", { "dependencies": { "@img/sharp-wasm32": "0.35.3" }, "os": "freebsd" }, "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw=="], "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.2", "", { "os": "linux", "cpu": "none" }, "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.2" }, "os": "linux", "cpu": "ppc64" }, "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA=="], "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.2" }, "os": "linux", "cpu": "none" }, "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], @@ -1784,9 +1802,9 @@ "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], "@internationalized/date": ["@internationalized/date@3.12.3", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q=="], @@ -2048,6 +2066,8 @@ "@opencode-ai/plugin": ["@opencode-ai/plugin@workspace:packages/plugin"], + "@opencode-ai/posts": ["@opencode-ai/posts@workspace:packages/posts"], + "@opencode-ai/protocol": ["@opencode-ai/protocol@workspace:packages/protocol"], "@opencode-ai/schema": ["@opencode-ai/schema@workspace:packages/schema"], @@ -3392,7 +3412,7 @@ "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], - "astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], + "astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], "astro-expressive-code": ["astro-expressive-code@0.41.7", "", { "dependencies": { "rehype-expressive-code": "^0.41.7" }, "peerDependencies": { "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" } }, "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ=="], @@ -3652,7 +3672,7 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], "cookie-es": ["cookie-es@2.0.1", "", {}, "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA=="], @@ -4098,7 +4118,7 @@ "follow-redirects": ["follow-redirects@1.16.0", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], - "fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], + "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], "fontkit": ["fontkit@2.0.4", "", { "dependencies": { "@swc/helpers": "^0.5.12", "brotli": "^1.3.2", "clone": "^2.1.2", "dfa": "^1.2.0", "fast-deep-equal": "^3.1.3", "restructure": "^3.0.0", "tiny-inflate": "^1.0.3", "unicode-properties": "^1.4.0", "unicode-trie": "^2.0.0" } }, "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g=="], @@ -4646,7 +4666,7 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], + "magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="], "make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], @@ -4862,7 +4882,7 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], + "neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], "nf3": ["nf3@0.1.12", "", {}, "sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ=="], @@ -4988,17 +5008,17 @@ "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], - "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], "p-map": ["p-map@7.0.6", "", {}, "sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg=="], - "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + "p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], "p-retry": ["p-retry@8.0.0", "", { "dependencies": { "is-network-error": "^1.3.0" } }, "sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A=="], - "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + "p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], @@ -5400,7 +5420,7 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@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" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -5478,7 +5498,7 @@ "sort-keys-length": ["sort-keys-length@1.0.1", "", { "dependencies": { "sort-keys": "^1.0.0" } }, "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -5742,7 +5762,7 @@ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], - "unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], + "unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], @@ -6070,28 +6090,30 @@ "@antfu/install-pkg/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], - "@astrojs/cloudflare/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.51.1", "", { "dependencies": { "@cloudflare/unenv-preset": "2.16.1", "miniflare": "5.20260801.1-alpha", "unenv": "2.0.0-rc.24", "workerd": "1.20260801.1", "wrangler": "4.120.0", "ws": "8.21.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0 || ^8.0.0" }, "bin": { "cf-vite": "bin/cf-vite" } }, "sha512-qVTv67W3yLPIVZcq3L7z6yoqNGYA6W9PzDwHMGeku7gNhimNj+Bf84A8ukLdfz2EpVkzLam4pPbqoQYY8bawJA=="], "@astrojs/cloudflare/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], + "@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], "@astrojs/markdown-remark/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "@astrojs/markdown-remark/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], - "@astrojs/markdown-satteri/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], - "@astrojs/markdown-satteri/@astrojs/prism": ["@astrojs/prism@4.0.2", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA=="], - "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.11", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.6", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ=="], + "@astrojs/mdx/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.2", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.3.0", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ=="], - "@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.2.2", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-FGfmK84zSNcrsBd0dl1gXE9JvZYElp8EXQa2jpHVAxG4deGKAp43wspxFupjADJX7MSsMRHwYCnfT6EyVmgeFQ=="], + + "@astrojs/mdx/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "@astrojs/mdx/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], "@astrojs/node/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.2", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.3.0", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ=="], - "@astrojs/node/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], - "@astrojs/react/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.2", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.3.0", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ=="], "@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], @@ -6102,14 +6124,18 @@ "@astrojs/solid-js/vite-plugin-solid": ["vite-plugin-solid@2.11.10", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw=="], + "@astrojs/starlight/@astrojs/mdx": ["@astrojs/mdx@4.3.14", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.11", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA=="], + + "@astrojs/starlight/astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], + "@astrojs/starlight/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], + "@astrojs/vercel/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.2", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.3.0", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ=="], "@astrojs/vercel/@vercel/analytics": ["@vercel/analytics@1.6.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg=="], - "@astrojs/vercel/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], - "@asyncapi/parser/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="], "@asyncapi/parser/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], @@ -6308,8 +6334,6 @@ "@mdx-js/mdx/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], - "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], - "@modelcontextprotocol/sdk/hono": ["hono@4.13.1", "", {}, "sha512-kdJoFVv2xmayw6cY09H7AbMJMt8Jn5jdlEdXsP7AGBdF2DIptVlKlOLKXP41yPip4/a3yQPv9gVcJYI8YY04dw=="], "@modelcontextprotocol/sdk/jose": ["jose@6.2.8", "", {}, "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ=="], @@ -6398,6 +6422,8 @@ "@opencode-ai/enterprise/vite": ["vite@7.1.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw=="], + "@opencode-ai/posts/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], + "@opencode-ai/session-ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], "@opencode-ai/session-ui/vite": ["vite@7.1.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw=="], @@ -6424,9 +6450,9 @@ "@opencode-ai/updates/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], - "@opencode-ai/www/@astrojs/cloudflare": ["@astrojs/cloudflare@14.1.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.1", "@astrojs/underscore-redirects": "1.0.3", "@cloudflare/vite-plugin": "^1.39.0", "piccolore": "^0.1.3", "vite": "^8.0.13" }, "peerDependencies": { "astro": "^7.0.0", "wrangler": "^4.83.0" } }, "sha512-Zyo1E/5/dmegmKODbwUzOd67euNd6oKcbllhAwe3uFFprA7mIFNkpF6yBBa78vIkpMNuU13MkBJHCLd+yJSuEA=="], + "@opencode-ai/web/@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="], - "@opencode-ai/www/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], + "@opencode-ai/web/astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], "@opencode-ai/www/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], @@ -6464,6 +6490,8 @@ "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@scalar/astro/astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], + "@scalar/types/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], "@scalar/types/type-fest": ["type-fest@5.8.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA=="], @@ -6630,23 +6658,25 @@ "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "astro/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], - "astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "astro/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], - "astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], + "astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], - "astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], "astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + "astro/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], "astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - "astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + "astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "astro-expressive-code/astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], "axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], @@ -6656,8 +6686,6 @@ "bin-links/write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], - "blume/@astrojs/mdx": ["@astrojs/mdx@7.0.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/markdown-remark": "7.2.2", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "^0.3.1", "astro": "^7.0.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw=="], - "blume/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], "blume/@pierre/diffs": ["@pierre/diffs@1.3.5", "", { "dependencies": { "@pierre/theme": "2.0.0", "@pierre/theming": "1.0.1", "@shikijs/transformers": "^3.0.0 || ^4.0.0", "diff": "9.0.0", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0 || ^4.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-BhaLEiUvR+BdIyOYdogA4JLQjluWPubuwySmmIqEkcE0FwIWRbgJgHNC/r884dlxEr89fO4hTk/sBln0a7NSOw=="], @@ -6668,8 +6696,6 @@ "blume/ai": ["ai@7.0.64", "", { "dependencies": { "@ai-sdk/gateway": "4.0.51", "@ai-sdk/provider": "4.0.7", "@ai-sdk/provider-utils": "5.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-29Ufm56e53pRzIPueWzZiwJsgxmZStaBFpwEkVM89sqyIIax/pzDm+ez/ksJ/CcY9UCuYzvCQpE2zkFHICvS3w=="], - "blume/astro": ["astro@7.1.3", "", { "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", "@astrojs/markdown-satteri": "0.3.4", "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unstorage": "^1.17.5", "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0 || ^0.35.0" }, "peerDependencies": { "@astrojs/markdown-remark": "7.2.1" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA=="], - "blume/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "blume/dompurify": ["dompurify@3.4.13", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ=="], @@ -6680,14 +6706,10 @@ "blume/node-html-parser": ["node-html-parser@9.0.1", "", { "dependencies": { "css-select": "^5.1.0", "entities": "^8.0.0" } }, "sha512-QrdiYYm1NnLRXsMXThUgVcF/syWfWIgHFmy8hylWMGFbHFtnRuXLBxxGQvIm3xaIJMUDJ0ayOmT/FGJYT3pZIw=="], - "blume/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="], - "blume/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], "blume/react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="], - "blume/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@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" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], - "blume/string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="], "blume/typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], @@ -6700,6 +6722,8 @@ "builder-util/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "cliui/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=="], "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -6786,8 +6810,6 @@ "esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "estree-util-to-js/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], - "execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -6856,6 +6878,8 @@ "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + "miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="], "miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], @@ -6882,8 +6906,6 @@ "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - - "ow/dot-prop": ["dot-prop@6.0.1", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA=="], "oxc-parser/@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], @@ -6922,6 +6944,8 @@ "readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "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=="], "roarr/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], @@ -6938,6 +6962,8 @@ "sort-keys/is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "sst/aws4fetch": ["aws4fetch@1.0.18", "", {}, "sha512-3Cf+YaUl07p24MoQ46rFwulAmiyCwH2+1zw1ZyPAX5OtJ34Hh185DwB8y/qRLb6cYYYtSFJ9pthyLc0MD4e8sQ=="], "sst/jose": ["jose@5.2.3", "", {}, "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA=="], @@ -6970,6 +6996,8 @@ "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "toolbeam-docs-theme/astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], + "tree-sitter-bash/node-addon-api": ["node-addon-api@8.9.1", "", {}, "sha512-4eUQWVPCUUUiBjLnHS3cXWeC6ryoPUc0U3rP7IuzapoGbzMqd/r6KKO0clr0b+snQhsrueFEhCZDdK+LK7hxKg=="], "tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], @@ -7032,6 +7060,8 @@ "yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "zod-to-ts/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@actions/github/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], @@ -7046,7 +7076,13 @@ "@ai-sdk/xai/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], - "@astrojs/cloudflare/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare": ["miniflare@5.20260801.1-alpha", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.29.0", "workerd": "1.20260801.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-BHPVzIDA6mbx7LefxpvkXW7DHx9FKB9GorZatbnrrFTt3CVMU8zuUpbgyCuebwDKcTTOZos43ta8GQ0eMVEpxA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd": ["workerd@1.20260801.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260801.1", "@cloudflare/workerd-darwin-arm64": "1.20260801.1", "@cloudflare/workerd-linux-64": "1.20260801.1", "@cloudflare/workerd-linux-arm64": "1.20260801.1", "@cloudflare/workerd-windows-64": "1.20260801.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-/g9JGTyqnHtoIscpBHqKD8swE2V4StBs2i69PmLiOhH45OP95jCFICl4F1hKlgN57rqfni5LiCitttoX5OOkVA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/wrangler": ["wrangler@4.120.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "5.20260801.1-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260801.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260801.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-cBmu/MeaB/fPacC0JpATs4duTOCagBxrZo+vBzuTX06tLzwSyAHE1drlHUZ8rP0VqVz1fy3ReGYTiHdKkoHltg=="], "@astrojs/cloudflare/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], @@ -7056,6 +7092,8 @@ "@astrojs/cloudflare/wrangler/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], + "@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@astrojs/markdown-remark/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], @@ -7070,58 +7108,12 @@ "@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], - "@astrojs/markdown-satteri/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@astrojs/mdx/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.6", "", {}, "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q=="], - - "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], - - "@astrojs/mdx/@astrojs/markdown-remark/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@4.0.2", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA=="], "@astrojs/node/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "@astrojs/node/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], - - "@astrojs/node/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], - - "@astrojs/node/astro/@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], - - "@astrojs/node/astro/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], - - "@astrojs/node/astro/cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], - - "@astrojs/node/astro/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], - - "@astrojs/node/astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], - - "@astrojs/node/astro/fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], - - "@astrojs/node/astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], - - "@astrojs/node/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - - "@astrojs/node/astro/magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="], - - "@astrojs/node/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], - - "@astrojs/node/astro/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="], - - "@astrojs/node/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], - - "@astrojs/node/astro/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@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" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], - - "@astrojs/node/astro/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], - - "@astrojs/node/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], - - "@astrojs/node/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - - "@astrojs/node/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - - "@astrojs/node/astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "@astrojs/react/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], @@ -7130,50 +7122,48 @@ "@astrojs/solid-js/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.11", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.6", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ=="], + + "@astrojs/starlight/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + + "@astrojs/starlight/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], + + "@astrojs/starlight/astro/@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], + + "@astrojs/starlight/astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + + "@astrojs/starlight/astro/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "@astrojs/starlight/astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], + + "@astrojs/starlight/astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "@astrojs/starlight/astro/fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], + + "@astrojs/starlight/astro/magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], + + "@astrojs/starlight/astro/neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], + + "@astrojs/starlight/astro/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + + "@astrojs/starlight/astro/p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + + "@astrojs/starlight/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "@astrojs/starlight/astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "@astrojs/starlight/astro/unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], + + "@astrojs/starlight/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], + + "@astrojs/starlight/astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + + "@astrojs/starlight/astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@astrojs/starlight/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@astrojs/vercel/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "@astrojs/vercel/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], - - "@astrojs/vercel/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], - - "@astrojs/vercel/astro/@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], - - "@astrojs/vercel/astro/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], - - "@astrojs/vercel/astro/cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], - - "@astrojs/vercel/astro/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], - - "@astrojs/vercel/astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], - - "@astrojs/vercel/astro/fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], - - "@astrojs/vercel/astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], - - "@astrojs/vercel/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - - "@astrojs/vercel/astro/magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="], - - "@astrojs/vercel/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], - - "@astrojs/vercel/astro/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="], - - "@astrojs/vercel/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], - - "@astrojs/vercel/astro/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@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" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], - - "@astrojs/vercel/astro/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], - - "@astrojs/vercel/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], - - "@astrojs/vercel/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - - "@astrojs/vercel/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - - "@astrojs/vercel/astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "@asyncapi/parser/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], @@ -7454,6 +7444,14 @@ "@opencode-ai/enterprise/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@opencode-ai/posts/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], + + "@opencode-ai/posts/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + + "@opencode-ai/posts/wrangler/miniflare": ["miniflare@4.20260708.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", "workerd": "1.20260708.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-c94O9zRDISdqO18EHt6l0iF/fWgWt8p18PJvRsA/L/NJZ9Cfke3s/F5Blg1XXF7WDutVRzWVWy8Vy4LaT5ifsA=="], + + "@opencode-ai/posts/wrangler/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], + "@opencode-ai/session-ui/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "@opencode-ai/stats-app/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], @@ -7480,51 +7478,51 @@ "@opencode-ai/updates/wrangler/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], - "@opencode-ai/www/@astrojs/cloudflare/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], + "@opencode-ai/web/@astrojs/cloudflare/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.1", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], - "@opencode-ai/www/@astrojs/cloudflare/@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.3", "", {}, "sha512-cxnGSw+sJigBLdX4TMSZKkzV6C3gMLJMucDk2W+n281Xhie68T2/9f1+1NMNDCZsc5i0FED7Qt5I10g2O9wtZg=="], + "@opencode-ai/web/@astrojs/cloudflare/@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.0", "", {}, "sha512-qZxHwVnmb5FXuvRsaIGaqWgnftjCuMY+GSbaVZdBmE4j8AfgPqKPxYp8SUERyJcjpKCEmO4wD6ybuGH8A2kVRQ=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.51.1", "", { "dependencies": { "@cloudflare/unenv-preset": "2.16.1", "miniflare": "5.20260801.1-alpha", "unenv": "2.0.0-rc.24", "workerd": "1.20260801.1", "wrangler": "4.120.0", "ws": "8.21.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0 || ^8.0.0" }, "bin": { "cf-vite": "bin/cf-vite" } }, "sha512-qVTv67W3yLPIVZcq3L7z6yoqNGYA6W9PzDwHMGeku7gNhimNj+Bf84A8ukLdfz2EpVkzLam4pPbqoQYY8bawJA=="], + "@opencode-ai/web/@astrojs/cloudflare/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], - "@opencode-ai/www/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], - "@opencode-ai/www/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], + "@opencode-ai/web/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], - "@opencode-ai/www/astro/@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], + "@opencode-ai/web/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], - "@opencode-ai/www/astro/@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="], + "@opencode-ai/web/astro/@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], - "@opencode-ai/www/astro/cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], + "@opencode-ai/web/astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], - "@opencode-ai/www/astro/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], + "@opencode-ai/web/astro/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "@opencode-ai/www/astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + "@opencode-ai/web/astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], - "@opencode-ai/www/astro/fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], + "@opencode-ai/web/astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "@opencode-ai/www/astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], + "@opencode-ai/web/astro/fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], - "@opencode-ai/www/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@opencode-ai/web/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "@opencode-ai/www/astro/magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="], + "@opencode-ai/web/astro/magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "@opencode-ai/www/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], + "@opencode-ai/web/astro/neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], - "@opencode-ai/www/astro/p-limit": ["p-limit@7.3.1", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q=="], + "@opencode-ai/web/astro/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], - "@opencode-ai/www/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], + "@opencode-ai/web/astro/p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], - "@opencode-ai/www/astro/sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@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" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], + "@opencode-ai/web/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "@opencode-ai/www/astro/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], + "@opencode-ai/web/astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], - "@opencode-ai/www/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], + "@opencode-ai/web/astro/unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], - "@opencode-ai/www/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], + "@opencode-ai/web/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - "@opencode-ai/www/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], + "@opencode-ai/web/astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], - "@opencode-ai/www/astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@opencode-ai/web/astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@opencode-ai/www/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], @@ -7546,6 +7544,44 @@ "@pierre/trees/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "@scalar/astro/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + + "@scalar/astro/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], + + "@scalar/astro/astro/@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], + + "@scalar/astro/astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + + "@scalar/astro/astro/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "@scalar/astro/astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], + + "@scalar/astro/astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "@scalar/astro/astro/fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], + + "@scalar/astro/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "@scalar/astro/astro/magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], + + "@scalar/astro/astro/neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], + + "@scalar/astro/astro/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + + "@scalar/astro/astro/p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + + "@scalar/astro/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "@scalar/astro/astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "@scalar/astro/astro/unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], + + "@scalar/astro/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], + + "@scalar/astro/astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + + "@scalar/astro/astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="], "@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], @@ -7728,72 +7764,48 @@ "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "astro-expressive-code/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], - "astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "astro-expressive-code/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], - "astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "astro-expressive-code/astro/@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], - "astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "astro-expressive-code/astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], - "astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "astro-expressive-code/astro/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "astro-expressive-code/astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], - "astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "astro-expressive-code/astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + "astro-expressive-code/astro/fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], - "astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + "astro-expressive-code/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + "astro-expressive-code/astro/magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + "astro-expressive-code/astro/neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], - "astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + "astro-expressive-code/astro/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], - "astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + "astro-expressive-code/astro/p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], - "astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + "astro-expressive-code/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + "astro-expressive-code/astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], - "astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + "astro-expressive-code/astro/unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], - "astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + "astro-expressive-code/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - "astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + "astro-expressive-code/astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], - "astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + "astro-expressive-code/astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "astro/@clack/prompts/@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="], "astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], - - "astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], - - "astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], - - "astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], - - "astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], - - "astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], - "astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], @@ -7808,16 +7820,6 @@ "babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "blume/@astrojs/mdx/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.2", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.3.0", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ=="], - - "blume/@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.2.2", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.2", "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-FGfmK84zSNcrsBd0dl1gXE9JvZYElp8EXQa2jpHVAxG4deGKAp43wspxFupjADJX7MSsMRHwYCnfT6EyVmgeFQ=="], - - "blume/@astrojs/mdx/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], - - "blume/@astrojs/mdx/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], - - "blume/@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], - "blume/@clack/prompts/@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="], "blume/@pierre/diffs/@pierre/theme": ["@pierre/theme@2.0.0", "", {}, "sha512-yNDd9GYLQl1mEUJR8AneJ5e4ohLIHQd/wZLWr4fagt78vS2RwwZNW530vVgHqXFAyFVcFlRmGUD5ramXH46OXw=="], @@ -7844,36 +7846,6 @@ "blume/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.27", "", { "dependencies": { "@ai-sdk/provider": "4.0.7", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", "undici": "^7.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EzAn4pdgG5g0xXtH6lE2zyNmfjDQIDjATkfqzuidEI35g++hh4+07vnjzkT/RmGmIClPZiRj/Q2GMPV2V7mkHw=="], - "blume/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.1", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q=="], - - "blume/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.3.3", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.6.0" } }, "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ=="], - - "blume/astro/@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], - - "blume/astro/cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], - - "blume/astro/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], - - "blume/astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], - - "blume/astro/fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], - - "blume/astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], - - "blume/astro/magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="], - - "blume/astro/neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], - - "blume/astro/p-queue": ["p-queue@9.3.3", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA=="], - - "blume/astro/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], - - "blume/astro/unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], - - "blume/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], - - "blume/astro/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - "blume/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], "blume/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -7884,42 +7856,6 @@ "blume/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "blume/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], - - "blume/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], - - "blume/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], - - "blume/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], - - "blume/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], - - "blume/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], - - "blume/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], - - "blume/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], - - "blume/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], - - "blume/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], - - "blume/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], - - "blume/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], - - "blume/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], - - "blume/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], - - "blume/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], - - "blume/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], - - "blume/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], - - "blume/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], - "builder-util/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -8092,19 +8028,44 @@ "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "nitro/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - - - - - - - - - - - "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -8177,6 +8138,44 @@ "temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "toolbeam-docs-theme/astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + + "toolbeam-docs-theme/astro/@astrojs/telemetry": ["@astrojs/telemetry@3.2.1", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg=="], + + "toolbeam-docs-theme/astro/@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], + + "toolbeam-docs-theme/astro/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + + "toolbeam-docs-theme/astro/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "toolbeam-docs-theme/astro/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="], + + "toolbeam-docs-theme/astro/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "toolbeam-docs-theme/astro/fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], + + "toolbeam-docs-theme/astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "toolbeam-docs-theme/astro/magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], + + "toolbeam-docs-theme/astro/neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], + + "toolbeam-docs-theme/astro/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + + "toolbeam-docs-theme/astro/p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + + "toolbeam-docs-theme/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "toolbeam-docs-theme/astro/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "toolbeam-docs-theme/astro/unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], + + "toolbeam-docs-theme/astro/unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="], + + "toolbeam-docs-theme/astro/vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + + "toolbeam-docs-theme/astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "tw-to-css/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "tw-to-css/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -8259,57 +8258,21 @@ "@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.35.2", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.2", "@img/sharp-darwin-x64": "0.35.2", "@img/sharp-freebsd-wasm32": "0.35.2", "@img/sharp-libvips-darwin-arm64": "1.3.1", "@img/sharp-libvips-darwin-x64": "1.3.1", "@img/sharp-libvips-linux-arm": "1.3.1", "@img/sharp-libvips-linux-arm64": "1.3.1", "@img/sharp-libvips-linux-ppc64": "1.3.1", "@img/sharp-libvips-linux-riscv64": "1.3.1", "@img/sharp-libvips-linux-s390x": "1.3.1", "@img/sharp-libvips-linux-x64": "1.3.1", "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", "@img/sharp-libvips-linuxmusl-x64": "1.3.1", "@img/sharp-linux-arm": "0.35.2", "@img/sharp-linux-arm64": "0.35.2", "@img/sharp-linux-ppc64": "0.35.2", "@img/sharp-linux-riscv64": "0.35.2", "@img/sharp-linux-s390x": "0.35.2", "@img/sharp-linux-x64": "0.35.2", "@img/sharp-linuxmusl-arm64": "0.35.2", "@img/sharp-linuxmusl-x64": "0.35.2", "@img/sharp-webcontainers-wasm32": "0.35.2", "@img/sharp-win32-arm64": "0.35.2", "@img/sharp-win32-ia32": "0.35.2", "@img/sharp-win32-x64": "0.35.2" } }, "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260801.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-wuJWbXpKvncJi1P0GKS+iYpN5tHdb7JPJJ/+6ZQe8zzovHHVMkLJPNBsgWpqeUhpM3g9qTwEKd2rglNKejuh5A=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260801.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kwoZiTpnhNrF3+APx84Q/oAqvJ3sU9yefGagwm/ASaH/2W19x0vghkW/r4qCoHCK0WW7EPugZ+aXjgPMRtlq1Q=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260801.1", "", { "os": "linux", "cpu": "x64" }, "sha512-r0vAxCZH+Jih9Unm1yoyiByPNWNgawcKciOHDm5Q37ZVGOkKLsT9AtLe3yLSaul76WrKqtf+JP2n0WW32VBLJg=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260801.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-zWgpdZtSozvIgzQNmQiDSF8yEOQJUkRAWNsDXXzAAoy+fCn8YUoSibj3mpFSbZRvbUldeBEaW6SCdC2VEMkhNQ=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260801.1", "", { "os": "win32", "cpu": "x64" }, "sha512-2oQz+Ksu4ji6e/+ZoYX+tWQEcxAii2p7l+iR8kx48W1llMalaufAsmVxTlk+3/vrM7D3/2c0iK448e0UQTcIMg=="], - "@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], "@astrojs/cloudflare/wrangler/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], @@ -8325,76 +8288,10 @@ "@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], - "@astrojs/markdown-satteri/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "@astrojs/mdx/@astrojs/markdown-remark/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], - - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + "@astrojs/mdx/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@astrojs/node/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "@astrojs/node/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], - - "@astrojs/node/astro/@clack/prompts/@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="], - - "@astrojs/node/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "@astrojs/node/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], - - "@astrojs/node/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], - - "@astrojs/node/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], - - "@astrojs/node/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], - - "@astrojs/node/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], - - "@astrojs/node/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], - - "@astrojs/node/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], - - "@astrojs/node/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], - - "@astrojs/node/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], - - "@astrojs/node/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], - - "@astrojs/node/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], - - "@astrojs/node/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], - - "@astrojs/node/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - - "@astrojs/node/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], - - "@astrojs/node/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], - - "@astrojs/node/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - "@astrojs/react/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@astrojs/solid-js/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], @@ -8449,60 +8346,124 @@ "@astrojs/solid-js/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.6", "", {}, "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q=="], + + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], + + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@astrojs/starlight/astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@astrojs/starlight/astro/p-queue/p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@astrojs/starlight/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "@astrojs/starlight/astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@astrojs/starlight/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@astrojs/starlight/astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@astrojs/starlight/astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@astrojs/starlight/astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@astrojs/starlight/astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@astrojs/starlight/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "@astrojs/starlight/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "@astrojs/starlight/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "@astrojs/starlight/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "@astrojs/vercel/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "@astrojs/vercel/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], - - "@astrojs/vercel/astro/@clack/prompts/@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="], - - "@astrojs/vercel/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "@astrojs/vercel/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], - - "@astrojs/vercel/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], - - "@astrojs/vercel/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - - "@astrojs/vercel/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], - - "@astrojs/vercel/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], - - "@astrojs/vercel/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], @@ -8885,6 +8846,20 @@ "@opencode-ai/enterprise/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@opencode-ai/posts/wrangler/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "@opencode-ai/posts/wrangler/miniflare/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], + + "@opencode-ai/posts/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260708.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg=="], + + "@opencode-ai/posts/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260708.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA=="], + + "@opencode-ai/posts/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260708.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA=="], + + "@opencode-ai/posts/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260708.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA=="], + + "@opencode-ai/posts/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + "@opencode-ai/session-ui/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "@opencode-ai/session-ui/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], @@ -9159,67 +9134,127 @@ "@opencode-ai/updates/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], - "@opencode-ai/www/@astrojs/cloudflare/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare": ["miniflare@5.20260801.1-alpha", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.29.0", "workerd": "1.20260801.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-BHPVzIDA6mbx7LefxpvkXW7DHx9FKB9GorZatbnrrFTt3CVMU8zuUpbgyCuebwDKcTTOZos43ta8GQ0eMVEpxA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd": ["workerd@1.20260801.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260801.1", "@cloudflare/workerd-darwin-arm64": "1.20260801.1", "@cloudflare/workerd-linux-64": "1.20260801.1", "@cloudflare/workerd-linux-arm64": "1.20260801.1", "@cloudflare/workerd-windows-64": "1.20260801.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-/g9JGTyqnHtoIscpBHqKD8swE2V4StBs2i69PmLiOhH45OP95jCFICl4F1hKlgN57rqfni5LiCitttoX5OOkVA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare": ["miniflare@4.20260708.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", "workerd": "1.20260708.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-c94O9zRDISdqO18EHt6l0iF/fWgWt8p18PJvRsA/L/NJZ9Cfke3s/F5Blg1XXF7WDutVRzWVWy8Vy4LaT5ifsA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/wrangler": ["wrangler@4.120.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "5.20260801.1-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260801.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260801.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-cBmu/MeaB/fPacC0JpATs4duTOCagBxrZo+vBzuTX06tLzwSyAHE1drlHUZ8rP0VqVz1fy3ReGYTiHdKkoHltg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], - "@opencode-ai/www/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - "@opencode-ai/www/astro/@clack/prompts/@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="], + "@opencode-ai/web/astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - "@opencode-ai/www/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@opencode-ai/web/astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@opencode-ai/www/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], + "@opencode-ai/web/astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@opencode-ai/www/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], + "@opencode-ai/web/astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@opencode-ai/www/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], + "@opencode-ai/web/astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], + "@opencode-ai/web/astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], + "@opencode-ai/web/astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - "@opencode-ai/www/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], + "@opencode-ai/web/astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], + "@opencode-ai/web/astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - "@opencode-ai/www/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], + "@opencode-ai/web/astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - "@opencode-ai/www/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], + "@opencode-ai/web/astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - "@opencode-ai/www/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - "@opencode-ai/www/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - "@opencode-ai/www/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "@opencode-ai/web/astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@opencode-ai/www/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + "@opencode-ai/web/astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - "@opencode-ai/www/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "@opencode-ai/web/astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@opencode-ai/web/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "@opencode-ai/web/astro/p-queue/p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@opencode-ai/web/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "@opencode-ai/web/astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@opencode-ai/web/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@opencode-ai/web/astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@opencode-ai/web/astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@opencode-ai/web/astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@opencode-ai/web/astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@opencode-ai/web/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "@opencode-ai/web/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "@opencode-ai/web/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "@opencode-ai/web/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], "@opencode-ai/www/wrangler/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], @@ -9235,6 +9270,118 @@ "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + "@scalar/astro/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@scalar/astro/astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@scalar/astro/astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@scalar/astro/astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@scalar/astro/astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@scalar/astro/astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@scalar/astro/astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@scalar/astro/astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@scalar/astro/astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@scalar/astro/astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@scalar/astro/astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@scalar/astro/astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@scalar/astro/astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@scalar/astro/astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@scalar/astro/astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@scalar/astro/astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@scalar/astro/astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@scalar/astro/astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@scalar/astro/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "@scalar/astro/astro/p-queue/p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "@scalar/astro/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@scalar/astro/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@scalar/astro/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@scalar/astro/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@scalar/astro/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@scalar/astro/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@scalar/astro/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@scalar/astro/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@scalar/astro/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@scalar/astro/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@scalar/astro/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "@scalar/astro/astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@scalar/astro/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@scalar/astro/astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@scalar/astro/astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@scalar/astro/astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@scalar/astro/astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@scalar/astro/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "@scalar/astro/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "@scalar/astro/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "@scalar/astro/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.1.4", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg=="], "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -9323,6 +9470,118 @@ "archiver-utils/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "astro-expressive-code/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "astro-expressive-code/astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "astro-expressive-code/astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "astro-expressive-code/astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "astro-expressive-code/astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "astro-expressive-code/astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "astro-expressive-code/astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "astro-expressive-code/astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "astro-expressive-code/astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "astro-expressive-code/astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "astro-expressive-code/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "astro-expressive-code/astro/p-queue/p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "astro-expressive-code/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "astro-expressive-code/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "astro-expressive-code/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "astro-expressive-code/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "astro-expressive-code/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "astro-expressive-code/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "astro-expressive-code/astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "astro-expressive-code/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "astro-expressive-code/astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "astro-expressive-code/astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "astro-expressive-code/astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "astro-expressive-code/astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "astro-expressive-code/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "astro-expressive-code/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "astro-expressive-code/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "astro-expressive-code/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], "astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], @@ -9335,8 +9594,6 @@ "babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - "blume/@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@4.0.2", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA=="], - "blume/@shikijs/transformers/@shikijs/core/@shikijs/primitive": ["@shikijs/primitive@4.4.2", "", { "dependencies": { "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA=="], "blume/@tailwindcss/vite/@tailwindcss/node/lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], @@ -9369,16 +9626,6 @@ "blume/ai/@ai-sdk/provider-utils/undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], - "blume/astro/@astrojs/telemetry/is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], - - "blume/astro/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], - - "blume/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - - "blume/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], - - "blume/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], - "dir-compare/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "editorconfig/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -9445,7 +9692,6 @@ "nitro/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], @@ -9464,6 +9710,118 @@ "temp/rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "toolbeam-docs-theme/astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "toolbeam-docs-theme/astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "toolbeam-docs-theme/astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "toolbeam-docs-theme/astro/p-queue/p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "toolbeam-docs-theme/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "toolbeam-docs-theme/astro/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "toolbeam-docs-theme/astro/unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "toolbeam-docs-theme/astro/unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "toolbeam-docs-theme/astro/unstorage/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "toolbeam-docs-theme/astro/unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + "tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], @@ -9474,6 +9832,56 @@ "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.1" }, "os": "darwin", "cpu": "arm64" }, "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.1" }, "os": "darwin", "cpu": "x64" }, "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "os": "freebsd" }, "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.1", "", { "os": "linux", "cpu": "none" }, "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.1" }, "os": "linux", "cpu": "arm" }, "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.1" }, "os": "linux", "cpu": "ppc64" }, "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.1" }, "os": "linux", "cpu": "none" }, "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.1" }, "os": "linux", "cpu": "s390x" }, "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "cpu": "none" }, "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.2", "", { "os": "win32", "cpu": "x64" }, "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ=="], + "@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], "@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], @@ -9522,17 +9930,23 @@ "@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], - "@astrojs/node/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], - "@astrojs/node/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], - "@astrojs/node/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], - "@astrojs/vercel/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], - "@astrojs/vercel/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], - "@astrojs/vercel/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "@astrojs/starlight/@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@astrojs/starlight/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "@astrojs/starlight/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "@astrojs/starlight/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], @@ -9572,6 +9986,54 @@ "@opencode-ai/enterprise/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@opencode-ai/storybook/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], "@opencode-ai/storybook/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], @@ -9684,29 +10146,77 @@ "@opencode-ai/updates/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], - "@opencode-ai/www/@astrojs/cloudflare/@astrojs/internal-helpers/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.35.2", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.2", "@img/sharp-darwin-x64": "0.35.2", "@img/sharp-freebsd-wasm32": "0.35.2", "@img/sharp-libvips-darwin-arm64": "1.3.1", "@img/sharp-libvips-darwin-x64": "1.3.1", "@img/sharp-libvips-linux-arm": "1.3.1", "@img/sharp-libvips-linux-arm64": "1.3.1", "@img/sharp-libvips-linux-ppc64": "1.3.1", "@img/sharp-libvips-linux-riscv64": "1.3.1", "@img/sharp-libvips-linux-s390x": "1.3.1", "@img/sharp-libvips-linux-x64": "1.3.1", "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", "@img/sharp-libvips-linuxmusl-x64": "1.3.1", "@img/sharp-linux-arm": "0.35.2", "@img/sharp-linux-arm64": "0.35.2", "@img/sharp-linux-ppc64": "0.35.2", "@img/sharp-linux-riscv64": "0.35.2", "@img/sharp-linux-s390x": "0.35.2", "@img/sharp-linux-x64": "0.35.2", "@img/sharp-linuxmusl-arm64": "0.35.2", "@img/sharp-linuxmusl-x64": "0.35.2", "@img/sharp-webcontainers-wasm32": "0.35.2", "@img/sharp-win32-arm64": "0.35.2", "@img/sharp-win32-ia32": "0.35.2", "@img/sharp-win32-x64": "0.35.2" } }, "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260801.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-wuJWbXpKvncJi1P0GKS+iYpN5tHdb7JPJJ/+6ZQe8zzovHHVMkLJPNBsgWpqeUhpM3g9qTwEKd2rglNKejuh5A=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260801.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kwoZiTpnhNrF3+APx84Q/oAqvJ3sU9yefGagwm/ASaH/2W19x0vghkW/r4qCoHCK0WW7EPugZ+aXjgPMRtlq1Q=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260801.1", "", { "os": "linux", "cpu": "x64" }, "sha512-r0vAxCZH+Jih9Unm1yoyiByPNWNgawcKciOHDm5Q37ZVGOkKLsT9AtLe3yLSaul76WrKqtf+JP2n0WW32VBLJg=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260801.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-zWgpdZtSozvIgzQNmQiDSF8yEOQJUkRAWNsDXXzAAoy+fCn8YUoSibj3mpFSbZRvbUldeBEaW6SCdC2VEMkhNQ=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260801.1", "", { "os": "win32", "cpu": "x64" }, "sha512-2oQz+Ksu4ji6e/+ZoYX+tWQEcxAii2p7l+iR8kx48W1llMalaufAsmVxTlk+3/vrM7D3/2c0iK448e0UQTcIMg=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@opencode-ai/www/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@opencode-ai/www/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@opencode-ai/www/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@opencode-ai/web/@astrojs/cloudflare/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260708.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260708.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260708.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260708.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA=="], + + "@opencode-ai/web/@astrojs/cloudflare/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + + "@opencode-ai/web/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "@opencode-ai/web/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "@opencode-ai/web/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -9756,6 +10266,12 @@ "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@scalar/astro/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "@scalar/astro/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "@scalar/astro/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "@slack/bolt/express/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -9772,6 +10288,12 @@ "archiver-utils/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "astro-expressive-code/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "astro-expressive-code/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "astro-expressive-code/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "blume/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], @@ -9858,10 +10380,6 @@ "blume/@tailwindcss/vite/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "blume/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], - - "blume/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], - "js-beautify/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "js-beautify/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], @@ -9932,67 +10450,77 @@ "temp/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.18", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw=="], + "toolbeam-docs-theme/astro/unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "toolbeam-docs-theme/astro/unstorage/h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "toolbeam-docs-theme/astro/unstorage/h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + "tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], + + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], + "@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], "@opencode-ai/enterprise/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], + "@opencode-ai/posts/wrangler/miniflare/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + "@opencode-ai/storybook/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], "@opencode-ai/ui/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], "@opencode-ai/updates/wrangler/miniflare/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.1" }, "os": "darwin", "cpu": "arm64" }, "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.1" }, "os": "darwin", "cpu": "x64" }, "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "os": "freebsd" }, "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.1", "", { "os": "linux", "cpu": "none" }, "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.1" }, "os": "linux", "cpu": "arm" }, "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.1" }, "os": "linux", "cpu": "ppc64" }, "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.1" }, "os": "linux", "cpu": "none" }, "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.1" }, "os": "linux", "cpu": "s390x" }, "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "cpu": "none" }, "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog=="], - - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.2", "", { "os": "win32", "cpu": "x64" }, "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], @@ -10012,12 +10540,10 @@ "temp/rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], + "@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-freebsd-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - - "@opencode-ai/www/@astrojs/cloudflare/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-webcontainers-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + "@opencode-ai/web/@astrojs/cloudflare/wrangler/miniflare/sharp/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], } } diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 21e5573e17b..c7d768769fa 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -23,6 +23,7 @@ }, "main": "./out/main/index.js", "dependencies": { + "@effect/platform-node": "catalog:", "@zip.js/zip.js": "2.7.62", "effect": "catalog:", "electron-context-menu": "4.1.2", diff --git a/packages/desktop/src/main/files/apps.ts b/packages/desktop/src/main/files/apps.ts index 1aec1923606..8981f2838dd 100644 --- a/packages/desktop/src/main/files/apps.ts +++ b/packages/desktop/src/main/files/apps.ts @@ -1,103 +1,100 @@ import { execFile } from "node:child_process" -import { access, readFile, readdir } from "node:fs/promises" -import { dirname, extname, join } from "node:path" import util from "node:util" +import { Effect, FileSystem, Path } from "effect" const execFilePromise = util.promisify(execFile) -const exists = (path: string) => - access(path) - .then(() => true) - .catch(() => false) - -export function checkAppExists(appName: string) { +export const checkAppExists = Effect.fn("DesktopFiles.checkAppExists")(function* (appName: string) { if (process.platform === "win32") return true if (process.platform === "linux") return true - return checkMacosApp(appName) -} + return yield* checkMacosApp(appName) +}) -export function resolveAppPath(appName: string) { +export const resolveAppPath = Effect.fn("DesktopFiles.resolveAppPath")(function* (appName: string) { if (process.platform !== "win32") return appName - return resolveWindowsAppPath(appName) -} + return yield* resolveWindowsAppPath(appName) +}) -async function checkMacosApp(appName: string) { +const checkMacosApp = Effect.fn("DesktopFiles.checkMacosApp")(function* (appName: string) { + const fs = yield* FileSystem.FileSystem const locations = [`/Applications/${appName}.app`, `/System/Applications/${appName}.app`] const home = process.env.HOME if (home) locations.push(`${home}/Applications/${appName}.app`) for (const location of locations) { - if (await exists(location)) return true + if (yield* exists(fs, location)) return true } - return execFilePromise("which", [appName]) - .then(() => true) - .catch(() => false) -} + return yield* Effect.tryPromise(() => execFilePromise("which", [appName])).pipe( + Effect.as(true), + Effect.catch(() => Effect.succeed(false)), + ) +}) -async function resolveWindowsAppPath(appName: string): Promise { - let output: string - try { - output = await execFilePromise("where", [appName]).then((r) => r.stdout.toString()) - } catch { - return null - } +const resolveWindowsAppPath = Effect.fn("DesktopFiles.resolveWindowsAppPath")(function* (appName: string) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const result = yield* Effect.tryPromise(() => execFilePromise("where", [appName])).pipe( + Effect.catch(() => Effect.succeed(undefined)), + ) + if (!result) return null - const paths = output + const paths = result.stdout + .toString() .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => line.length > 0) - const hasExt = (path: string, ext: string) => extname(path).toLowerCase() === `.${ext}` + const hasExt = (value: string, ext: string) => path.extname(value).toLowerCase() === `.${ext}` const exe = paths.find((path) => hasExt(path, "exe")) if (exe) return exe - const resolveCmd = async (path: string) => { - const content = await readFile(path, "utf8") + const resolveCmd = Effect.fnUntraced(function* (file: string) { + const content = yield* fs.readFileString(file) for (const token of content.split('"').map((value: string) => value.trim())) { const lower = token.toLowerCase() if (!lower.includes(".exe")) continue const index = lower.indexOf("%~dp0") if (index >= 0) { - const base = dirname(path) + const base = path.dirname(file) const suffix = token.slice(index + 5) const resolved = suffix .replace(/\//g, "\\") .split("\\") .filter((part: string) => part && part !== ".") .reduce((current: string, part: string) => { - if (part === "..") return dirname(current) - return join(current, part) + if (part === "..") return path.dirname(current) + return path.join(current, part) }, base) - if (await exists(resolved)) return resolved + if (yield* exists(fs, resolved)) return resolved } - if (await exists(token)) return token + if (yield* exists(fs, token)) return token } return null - } + }) - for (const path of paths) { - if (hasExt(path, "cmd") || hasExt(path, "bat")) { - const resolved = await resolveCmd(path) + for (const file of paths) { + if (hasExt(file, "cmd") || hasExt(file, "bat")) { + const resolved = yield* resolveCmd(file) if (resolved) return resolved } - if (!extname(path)) { - const cmd = `${path}.cmd` - if (await exists(cmd)) { - const resolved = await resolveCmd(cmd) + if (!path.extname(file)) { + const cmd = `${file}.cmd` + if (yield* exists(fs, cmd)) { + const resolved = yield* resolveCmd(cmd) if (resolved) return resolved } - const bat = `${path}.bat` - if (await exists(bat)) { - const resolved = await resolveCmd(bat) + const bat = `${file}.bat` + if (yield* exists(fs, bat)) { + const resolved = yield* resolveCmd(bat) if (resolved) return resolved } } @@ -110,27 +107,31 @@ async function resolveWindowsAppPath(appName: string): Promise { .join("") if (key) { - for (const path of paths) { - const dirs = [dirname(path), dirname(dirname(path)), dirname(dirname(dirname(path)))] + for (const file of paths) { + const dirs = [path.dirname(file), path.dirname(path.dirname(file)), path.dirname(path.dirname(path.dirname(file)))] for (const dir of dirs) { - try { - for (const entry of await readdir(dir)) { - const candidate = join(dir, entry) - if (!hasExt(candidate, "exe")) continue - const stem = entry.replace(/\.exe$/i, "") - const name = stem - .split("") - .filter((value: string) => /[a-z0-9]/i.test(value)) - .map((value: string) => value.toLowerCase()) - .join("") - if (name.includes(key) || key.includes(name)) return candidate - } - } catch { - continue + const entries = yield* fs.readDirectory(dir).pipe(Effect.catch(() => Effect.succeed([]))) + for (const entry of entries) { + const candidate = path.join(dir, entry) + if (!hasExt(candidate, "exe")) continue + const stem = entry.replace(/\.exe$/i, "") + const name = stem + .split("") + .filter((value: string) => /[a-z0-9]/i.test(value)) + .map((value: string) => value.toLowerCase()) + .join("") + if (name.includes(key) || key.includes(name)) return candidate } } } } return paths[0] ?? null +}) + +function exists(fs: FileSystem.FileSystem, path: string) { + return fs.access(path).pipe( + Effect.as(true), + Effect.catch(() => Effect.succeed(false)), + ) } diff --git a/packages/desktop/src/main/files/attachment-picker.test.ts b/packages/desktop/src/main/files/attachment-picker.test.ts index f2d5d6b56cb..c9fefcb6e5a 100644 --- a/packages/desktop/src/main/files/attachment-picker.test.ts +++ b/packages/desktop/src/main/files/attachment-picker.test.ts @@ -1,7 +1,9 @@ import { describe, expect, test } from "bun:test" +import { NodeFileSystem } from "@effect/platform-node" import { mkdtemp, rm, truncate, writeFile } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" +import { Effect, FileSystem } from "effect" import { assertAttachmentBudget, createPickedFileAuthorizations, @@ -9,6 +11,9 @@ import { readAttachment, } from "./attachment-picker" +const run = (effect: Effect.Effect) => + Effect.runPromise(effect.pipe(Effect.provide(NodeFileSystem.layer))) + describe("assertAttachmentBudget", () => { test("accepts selections within the media ingest limit", () => { expect(() => @@ -25,7 +30,7 @@ describe("assertAttachmentBudget", () => { const file = join(directory, "example.txt") try { await writeFile(file, "lorem ipsum") - expect(new TextDecoder().decode(await readAttachment(file))).toBe("lorem ipsum") + expect(new TextDecoder().decode(await run(readAttachment(file)))).toBe("lorem ipsum") } finally { await rm(directory, { recursive: true, force: true }) } @@ -37,7 +42,7 @@ describe("assertAttachmentBudget", () => { try { await writeFile(file, "") await truncate(file, MAX_ATTACHMENT_BYTES + 1) - await expect(readAttachment(file)).rejects.toThrow("20 MB limit") + await expect(run(readAttachment(file))).rejects.toThrow("20 MB limit") } finally { await rm(directory, { recursive: true, force: true }) } @@ -45,16 +50,16 @@ describe("assertAttachmentBudget", () => { }) describe("picked file authorizations", () => { - const read = async (path: string) => new TextEncoder().encode(path).buffer + const read = (path: string) => Effect.sync(() => new TextEncoder().encode(path).buffer) test("keeps concurrent picker selections isolated", async () => { const authorizations = createPickedFileAuthorizations(read) const first = authorizations.add(1, ["a.txt", "b.txt"]) const second = authorizations.add(1, ["c.txt"]) - expect(new TextDecoder().decode(await authorizations.read(1, first, "a.txt"))).toBe("a.txt") - expect(new TextDecoder().decode(await authorizations.read(1, second, "c.txt"))).toBe("c.txt") - expect(new TextDecoder().decode(await authorizations.read(1, first, "b.txt"))).toBe("b.txt") + expect(new TextDecoder().decode(await run(authorizations.read(1, first, "a.txt")))).toBe("a.txt") + expect(new TextDecoder().decode(await run(authorizations.read(1, second, "c.txt")))).toBe("c.txt") + expect(new TextDecoder().decode(await run(authorizations.read(1, first, "b.txt")))).toBe("b.txt") }) test("releases unread files for one picker without affecting another", async () => { @@ -63,25 +68,29 @@ describe("picked file authorizations", () => { const second = authorizations.add(1, ["b.txt"]) authorizations.release(1, first) - await expect(authorizations.read(1, first, "a.txt")).rejects.toThrow("not selected") - expect(new TextDecoder().decode(await authorizations.read(1, second, "b.txt"))).toBe("b.txt") + await expect(run(authorizations.read(1, first, "a.txt"))).rejects.toThrow("not selected") + expect(new TextDecoder().decode(await run(authorizations.read(1, second, "b.txt")))).toBe("b.txt") }) test("keeps picker tokens scoped to their renderer", async () => { const authorizations = createPickedFileAuthorizations(read) const token = authorizations.add(1, ["a.txt"]) - await expect(authorizations.read(2, token, "a.txt")).rejects.toThrow("not selected") + await expect(run(authorizations.read(2, token, "a.txt"))).rejects.toThrow("not selected") }) test("charges actual reads against the selection budget", async () => { - const authorizations = createPickedFileAuthorizations(async (_path, maxBytes) => { - if (6 > maxBytes) throw new Error("budget exceeded") - return new ArrayBuffer(6) - }, 10) + const authorizations = createPickedFileAuthorizations( + (_path, maxBytes) => + Effect.sync(() => { + if (6 > maxBytes) throw new Error("budget exceeded") + return new ArrayBuffer(6) + }), + 10, + ) const token = authorizations.add(1, ["a.txt", "b.txt"]) - await authorizations.read(1, token, "a.txt") - await expect(authorizations.read(1, token, "b.txt")).rejects.toThrow("budget exceeded") + await run(authorizations.read(1, token, "a.txt")) + await expect(run(authorizations.read(1, token, "b.txt"))).rejects.toThrow("budget exceeded") }) }) diff --git a/packages/desktop/src/main/files/attachment-picker.ts b/packages/desktop/src/main/files/attachment-picker.ts index 60caf3cf11b..99cedb6be56 100644 --- a/packages/desktop/src/main/files/attachment-picker.ts +++ b/packages/desktop/src/main/files/attachment-picker.ts @@ -1,11 +1,11 @@ import { randomUUID } from "node:crypto" -import { open } from "node:fs/promises" +import { Effect, FileSystem } from "effect" import { nativeT } from "../native/translations" export const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024 export function createPickedFileAuthorizations( - read: (path: string, maxBytes: number) => Promise = readAttachment, + read: (path: string, maxBytes: number) => Effect.Effect, budget = MAX_ATTACHMENT_BYTES, ) { const selections = new Map; remaining: number }>() @@ -16,15 +16,15 @@ export function createPickedFileAuthorizations( selections.set(token, { sender, paths: new Set(paths), remaining: budget }) return token }, - async read(sender: number, token: string, path: string) { + read: Effect.fn("DesktopFiles.readPickedFile")(function* (sender: number, token: string, path: string) { const selection = selections.get(token) if (selection?.sender !== sender || !selection.paths.delete(path)) throw new Error(nativeT("desktop.picker.error.notSelected")) - const bytes = await read(path, selection.remaining) + const bytes = yield* read(path, selection.remaining) selection.remaining -= bytes.byteLength if (selection.paths.size === 0) selections.delete(token) return bytes - }, + }), release(sender: number, token: string) { if (selections.get(token)?.sender === sender) selections.delete(token) }, @@ -37,21 +37,23 @@ export function assertAttachmentBudget(files: { size: number }[]) { throw new Error(nativeT("desktop.picker.error.sizeLimit", { limit: MAX_ATTACHMENT_BYTES / 1024 / 1024 })) } -export async function readAttachment(filePath: string, maxBytes = MAX_ATTACHMENT_BYTES) { - const file = await open(filePath, "r") - try { - const info = await file.stat() - if (info.size > maxBytes) - throw new Error(nativeT("desktop.picker.error.sizeLimit", { limit: MAX_ATTACHMENT_BYTES / 1024 / 1024 })) - const bytes = Buffer.allocUnsafe(info.size) - let offset = 0 - while (offset < info.size) { - const result = await file.read(bytes, offset, info.size - offset, offset) - if (result.bytesRead === 0) break - offset += result.bytesRead - } - return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + offset) as ArrayBuffer - } finally { - await file.close() - } +export function readAttachment(filePath: string, maxBytes = MAX_ATTACHMENT_BYTES) { + return Effect.scoped( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const file = yield* fs.open(filePath, { flag: "r" }) + const info = yield* file.stat + if (info.size > FileSystem.Size(maxBytes)) + throw new Error(nativeT("desktop.picker.error.sizeLimit", { limit: MAX_ATTACHMENT_BYTES / 1024 / 1024 })) + + const bytes = new Uint8Array(Number(info.size)) + let offset = 0 + while (offset < bytes.byteLength) { + const read = Number(yield* file.read(bytes.subarray(offset))) + if (read === 0) break + offset += read + } + return bytes.buffer.slice(0, offset) + }), + ) } diff --git a/packages/desktop/src/main/files/index.ts b/packages/desktop/src/main/files/index.ts index a01d6050324..b73a4f2ee04 100644 --- a/packages/desktop/src/main/files/index.ts +++ b/packages/desktop/src/main/files/index.ts @@ -1,69 +1,98 @@ +export * as DesktopFiles from "./index" + import { execFile } from "node:child_process" -import { stat } from "node:fs/promises" -import { basename } from "node:path" import { clipboard, dialog, shell } from "electron" +import { Context, Effect, FileSystem, Layer, Path } from "effect" import type { DirectoryPickerOptions, FilePickerOptions, SaveFilePickerOptions } from "../../shared/ipc-contract" -import { writeLog } from "../native/logging" +import { scoped } from "../native/logging" import { nativeT } from "../native/translations" -import { assertAttachmentBudget, createPickedFileAuthorizations } from "./attachment-picker" +import { assertAttachmentBudget, createPickedFileAuthorizations, readAttachment } from "./attachment-picker" import { resolveExternalURL, resolveLocalFilePath } from "./external-url" -export function createFileCapabilities() { - const pickedFiles = createPickedFileAuthorizations() +export type Interface = ReturnType + +export class Service extends Context.Service()("opencode/desktop/DesktopFiles") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + return Service.of(make(fs, path)) + }), +) + +function make(fs: FileSystem.FileSystem, path: Path.Path) { + const pickedFiles = createPickedFileAuthorizations((file, maxBytes) => + readAttachment(file, maxBytes).pipe(Effect.provideService(FileSystem.FileSystem, fs)), + ) return { - async openDirectoryPicker(options?: DirectoryPickerOptions) { - const result = await dialog.showOpenDialog({ - properties: ["openDirectory", ...(options?.multiple ? ["multiSelections" as const] : []), "createDirectory"], - title: options?.title ?? nativeT("desktop.dialog.chooseFolder"), - defaultPath: options?.defaultPath, - }) + openDirectoryPicker: Effect.fn("DesktopFiles.openDirectoryPicker")(function* (options?: DirectoryPickerOptions) { + const result = yield* Effect.promise(() => + dialog.showOpenDialog({ + properties: ["openDirectory", ...(options?.multiple ? ["multiSelections" as const] : []), "createDirectory"], + title: options?.title ?? nativeT("desktop.dialog.chooseFolder"), + defaultPath: options?.defaultPath, + }), + ) if (result.canceled) return null return options?.multiple ? result.filePaths : result.filePaths[0] - }, - async openFilePicker(sender: number, options?: FilePickerOptions) { - const result = await dialog.showOpenDialog({ - properties: ["openFile", ...(options?.multiple ? ["multiSelections" as const] : [])], - title: options?.title ?? nativeT("desktop.dialog.chooseFile"), - defaultPath: options?.defaultPath, - filters: pickerFilters(options?.extensions), - }) + }), + openFilePicker: Effect.fn("DesktopFiles.openFilePicker")(function* (sender: number, options?: FilePickerOptions) { + const result = yield* Effect.promise(() => + dialog.showOpenDialog({ + properties: ["openFile", ...(options?.multiple ? ["multiSelections" as const] : [])], + title: options?.title ?? nativeT("desktop.dialog.chooseFile"), + defaultPath: options?.defaultPath, + filters: pickerFilters(options?.extensions), + }), + ) if (result.canceled) return null - const files = await Promise.all( - result.filePaths.map(async (path) => ({ path, name: basename(path), size: (await stat(path)).size })), + const files = yield* Effect.forEach( + result.filePaths, + Effect.fnUntraced(function* (file) { + const info = yield* fs.stat(file) + return { path: file, name: path.basename(file), size: Number(info.size) } + }), + { concurrency: "unbounded" }, ) assertAttachmentBudget(files) return { token: pickedFiles.add(sender, result.filePaths), files } - }, - readPickedFile: (sender: number, token: string, path: string) => pickedFiles.read(sender, token, path), - releasePickedFiles: (sender: number, token: string) => pickedFiles.release(sender, token), - async saveFilePicker(options?: SaveFilePickerOptions) { - const result = await dialog.showSaveDialog({ - title: options?.title ?? nativeT("desktop.dialog.saveFile"), - defaultPath: options?.defaultPath, - }) + }), + readPickedFile: pickedFiles.read, + releasePickedFiles: pickedFiles.release, + saveFilePicker: Effect.fn("DesktopFiles.saveFilePicker")(function* (options?: SaveFilePickerOptions) { + const result = yield* Effect.promise(() => + dialog.showSaveDialog({ + title: options?.title ?? nativeT("desktop.dialog.saveFile"), + defaultPath: options?.defaultPath, + }), + ) if (result.canceled) return null return result.filePath ?? null - }, - async openPath(path: string, application?: string) { - if (!application) return shell.openPath(path) - await new Promise((resolve, reject) => { - const command = - process.platform === "darwin" - ? { file: "open", arguments: ["-a", application, path] } - : { file: application, arguments: [path] } - execFile(command.file, command.arguments, (error) => (error ? reject(error) : resolve())) - }) - }, - async revealPath(path: string) { - const exists = await stat(path).then( - () => true, - () => false, + }), + openPath: Effect.fn("DesktopFiles.openPath")(function* (target: string, application?: string) { + if (!application) return yield* Effect.promise(() => shell.openPath(target)) + yield* Effect.tryPromise(() => + new Promise((resolve, reject) => { + const command = + process.platform === "darwin" + ? { file: "open", arguments: ["-a", application, target] } + : { file: application, arguments: [target] } + execFile(command.file, command.arguments, (error) => (error ? reject(error) : resolve())) + }), + ) + }), + revealPath: Effect.fn("DesktopFiles.revealPath")(function* (target: string) { + const exists = yield* fs.stat(target).pipe( + Effect.as(true), + Effect.catch(() => Effect.succeed(false)), ) if (!exists) return false - shell.showItemInFolder(path) + shell.showItemInFolder(target) return true - }, + }), readClipboardImage() { const image = clipboard.readImage() if (image.isEmpty()) return null @@ -73,25 +102,24 @@ export function createFileCapabilities() { } } -export function openExternalURL(value: string) { +export const openExternalURL = Effect.fn("DesktopFiles.openExternalURL")(function* (value: string) { const url = resolveExternalURL(value) if (!url) { - writeLog("window", "blocked external target", { url: value }, "warn") + yield* scoped("window", Effect.logWarning("blocked external target", { url: value })) return } - void shell.openExternal(url) -} + yield* Effect.promise(() => shell.openExternal(url)) +}) -export function openLocalFileURL(value: string) { +export const openLocalFileURL = Effect.fn("DesktopFiles.openLocalFileURL")(function* (value: string) { const path = resolveLocalFilePath(value) if (!path) { - writeLog("window", "blocked local file target", { url: value }, "warn") + yield* scoped("window", Effect.logWarning("blocked local file target", { url: value })) return } - void shell.openPath(path).then((error) => { - if (error) writeLog("window", "failed to open local file", { path, error }, "error") - }) -} + const error = yield* Effect.promise(() => shell.openPath(path)) + if (error) yield* scoped("window", Effect.logError("failed to open local file", { path, error })) +}) function pickerFilters(extensions?: string[]) { if (!extensions?.length) return undefined diff --git a/packages/desktop/src/main/index.ts b/packages/desktop/src/main/index.ts index c8a373ea616..38169f573d5 100644 --- a/packages/desktop/src/main/index.ts +++ b/packages/desktop/src/main/index.ts @@ -1,111 +1,32 @@ +import { NodeFileSystem, NodePath, NodeRuntime } from "@effect/platform-node" import { app } from "electron" -import { Deferred, Effect, Fiber } from "effect" -import type { ServerReadyData } from "../shared/ipc-contract" -import { checkAppExists, resolveAppPath } from "./files/apps" -import { - registerIpcHandlers, - registerUpdaterIpcHandlers, - registerWslInitialization, - registerWslIpcHandlers, -} from "./ipc" -import { - acquireApplicationLock, - configureApplication, - loadProxyEnvironment, - preferApplicationEnvironment, - prepareDesktop, -} from "./lifecycle/environment" -import { createApplicationLifecycle } from "./lifecycle" -import { finishFirstLaunchOnboarding, isFirstLaunchOnboardingPending } from "./lifecycle/onboarding" -import { exportDebugLogs, startNetworkLogging, writeLog } from "./native/logging" -import { createMenu, sendMenuCommand } from "./native/menu" -import { setNativeTranslations } from "./native/translations" -import { startBackgroundCli } from "./service/background-service" -import { forwardInitializationFailure } from "./service/initialization" -import { getDefaultServerUrl, setDefaultServerUrl } from "./service/server-settings" -import { createUpdaterIpc, setupAutoUpdater, showUpdaterDialog, startAutoUpdater } from "./updater" -import { getLastFocusedWindow, setBackgroundColor } from "./windows" -import { startWsl } from "./wsl/start" +import { Effect, Layer } from "effect" +import { Ipc } from "./ipc" +import { DesktopInitialization } from "./lifecycle/desktop-initialization" +import { ApplicationLifecycle } from "./lifecycle" +import { BackgroundService } from "./service/background-service" +import { DesktopCli } from "./service/desktop-cli" +import { UpdaterLive } from "./updater/live" -const main = Effect.gen(function* () { - const logger = configureApplication() - if (!acquireApplicationLock()) return - preferApplicationEnvironment(logger) - loadProxyEnvironment(logger) - const lifecycle = createApplicationLifecycle(logger) - const serverReady = Deferred.makeUnsafe() - const wslReady = Promise.withResolvers() - logger.log("starting v2 background service") - const backgroundTask = yield* Effect.promise(() => startBackgroundCli(logger)).pipe(Effect.forkChild) - - yield* Effect.promise(() => app.whenReady()) - yield* prepareDesktop(logger) - - const updater = yield* Effect.promise(() => setupAutoUpdater(lifecycle.prepareToRestart)) - const menu = { - trigger: (id: string) => { - const win = getLastFocusedWindow() - if (win) sendMenuCommand(win, id) - }, - checkForUpdates: () => void showUpdaterDialog(updater), - relaunch: lifecycle.relaunch, - } - registerIpcHandlers({ - relaunch: lifecycle.relaunch, - awaitInitialization: Effect.fnUntraced( - function* () { - logger.log("awaiting server ready") - const result = yield* Deferred.await(serverReady) - logger.log("server ready", { url: result.url }) - return result - }, - (effect) => Effect.runPromise(effect), - ), - consumeInitialDeepLinks: lifecycle.consumeInitialDeepLinks, - getDefaultServerUrl, - setDefaultServerUrl, - isFirstLaunchOnboardingPending, - finishFirstLaunchOnboarding, - checkAppExists, - resolveAppPath: async (appName) => resolveAppPath(appName), - showUpdater: () => showUpdaterDialog(updater), - setBackgroundColor, - exportDebugLogs, - recordFatalRendererError: (error) => writeLog("renderer", "fatal renderer error", { ...error }, "error"), - setNativeTranslations: (bundle) => { - if (setNativeTranslations(bundle)) createMenu(menu) - }, +const runIpc = Effect.fn("Desktop.runIpc")(function* () { + const lifecycle = yield* ApplicationLifecycle.Service + const ipc = yield* Ipc.registerIpcHandlers + if (lifecycle.restoreWindows().length) ipc.installMenu() + yield* Effect.callback((resume) => { + const quit = () => resume(Effect.void) + app.once("will-quit", quit) + return Effect.sync(() => app.off("will-quit", quit)) }) - registerUpdaterIpcHandlers(createUpdaterIpc(updater)) - registerWslInitialization(wslReady.promise) - startAutoUpdater(updater) - yield* Effect.promise(() => startNetworkLogging()) - - const loadingTask = yield* Effect.gen(function* () { - const background = yield* Fiber.join(backgroundTask) - yield* Deferred.succeed(serverReady, { - url: background.url, - username: background.username, - password: background.password, - }) - logger.log("loading task finished") - - void startWsl(background, logger).then( - (wsl) => { - registerWslIpcHandlers(wsl.ipc) - lifecycle.setWslShutdown(wsl.stop) - wsl.start() - wslReady.resolve() - }, - (error) => { - logger.error("failed to start WSL manager", { error }) - wslReady.reject(error) - }, - ) - }).pipe(forwardInitializationFailure(serverReady), Effect.forkChild) - - if (lifecycle.restoreWindows().length) createMenu(menu) - yield* Fiber.await(loadingTask) }) -Effect.runFork(main) +runIpc().pipe( + Effect.provide(Ipc.layer), + Effect.provide(BackgroundService.layer), + Effect.provide(DesktopCli.layer), + Effect.provide(UpdaterLive.layer), + Effect.provide(DesktopInitialization.layer), + Effect.provide(ApplicationLifecycle.layer), + Effect.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer)), + Effect.scoped, + NodeRuntime.runMain, +) diff --git a/packages/desktop/src/main/ipc-events.ts b/packages/desktop/src/main/ipc-events.ts new file mode 100644 index 00000000000..dec0bbc57b6 --- /dev/null +++ b/packages/desktop/src/main/ipc-events.ts @@ -0,0 +1,26 @@ +import type { WebContents } from "electron" +import { Effect, Queue, Stream } from "effect" +import type { DesktopEvent } from "../shared/ipc-rpc/events" + +const queues = new Map>() + +export const bindIpcEvents = Effect.fn("IpcEvents.bind")(function* (senderId: number) { + const queue = yield* Queue.unbounded() + const previous = queues.get(senderId) + queues.set(senderId, queue) + if (previous) yield* Queue.shutdown(previous) + return Effect.fnUntraced(function* () { + if (queues.get(senderId) === queue) queues.delete(senderId) + yield* Queue.shutdown(queue) + })() +}) + +export function ipcEventStream(senderId: number) { + const queue = queues.get(senderId) + return queue ? Stream.fromQueue(queue) : Stream.empty +} + +export function emitIpcEvent(sender: WebContents, event: DesktopEvent) { + const queue = queues.get(sender.id) + if (queue) Queue.offerUnsafe(queue, event) +} diff --git a/packages/desktop/src/main/ipc-handlers/app.ts b/packages/desktop/src/main/ipc-handlers/app.ts new file mode 100644 index 00000000000..8d07e436ce1 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/app.ts @@ -0,0 +1,71 @@ +import { BrowserWindow } from "electron" +import { parseDesktopNativeBundle } from "@opencode-ai/app/i18n/desktop-native" +import { Effect } from "effect" +import { AppRpcs } from "../../shared/ipc-rpc" +import { openExternalURL } from "../files" +import { checkAppExists, resolveAppPath } from "../files/apps" +import { setForceFocus } from "../native/debug" +import { DesktopLogging, scoped } from "../native/logging" +import { createMenu, sendMenuCommand } from "../native/menu" +import { setNativeTranslations } from "../native/translations" +import { IpcPortHandoff } from "../ipc-transport" +import { ApplicationLifecycle } from "../lifecycle" +import { finishFirstLaunchOnboarding, isFirstLaunchOnboardingPending } from "../lifecycle/onboarding" +import { BackgroundService } from "../service/background-service" +import { getDefaultServerUrl, setDefaultServerUrl } from "../service/server-settings" +import { Updater } from "../updater" +import { getLastFocusedWindow, setBackgroundColor } from "../windows" +import { sender } from "./context" + +export const appHandlers = AppRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + const lifecycle = yield* ApplicationLifecycle.Service + const background = yield* BackgroundService.Service + const updater = yield* Updater.Service + const logging = yield* DesktopLogging.Service + const runFork = Effect.runForkWith(yield* Effect.context()) + return AppRpcs.of({ + AppAwaitInitialization: () => background.connection, + AppConsumeInitialDeepLinks: () => Effect.sync(lifecycle.consumeInitialDeepLinks), + AppGetDefaultServerUrl: () => Effect.sync(getDefaultServerUrl), + AppSetDefaultServerUrl: ({ url }) => Effect.sync(() => setDefaultServerUrl(url)), + AppIsFirstLaunchOnboardingPending: isFirstLaunchOnboardingPending, + AppFinishFirstLaunchOnboarding: ({ createDefaultProject }) => + finishFirstLaunchOnboarding(createDefaultProject).pipe(Effect.orDie), + AppCheckAppExists: ({ appName }) => checkAppExists(appName).pipe(Effect.orDie), + AppResolveAppPath: ({ appName }) => resolveAppPath(appName).pipe(Effect.orDie), + AppSetBackgroundColor: ({ color }) => Effect.sync(() => setBackgroundColor(color)), + AppExportDebugLogs: () => logging.exportDebug, + AppSetForceFocus: ({ enabled }, context) => promise(() => setForceFocus(sender(handoff, context), enabled)), + AppRecordFatalRendererError: ({ error }) => + scoped("renderer", Effect.logError("fatal renderer error", { ...error })), + AppSetNativeTranslations: ({ value }, context) => + Effect.sync(() => { + const contents = sender(handoff, context) + const win = BrowserWindow.fromWebContents(contents) + if (!win || win.isDestroyed() || win.webContents !== contents) { + throw new Error("Invalid native translation sender") + } + const bundle = parseDesktopNativeBundle(value) + if (!bundle) throw new Error("Invalid native translation bundle") + if (!setNativeTranslations(bundle)) return + createMenu({ + trigger: (id) => { + const win = getLastFocusedWindow() + if (win) sendMenuCommand(win, id) + }, + checkForUpdates: () => runFork(updater.show), + createWindow: lifecycle.createWindow, + openExternal: (url) => runFork(openExternalURL(url)), + relaunch: lifecycle.relaunch, + }) + }), + AppRelaunch: () => Effect.sync(lifecycle.relaunch), + }) + }), +) + +function promise(evaluate: () => A | Promise) { + return Effect.tryPromise(async () => evaluate()).pipe(Effect.orDie) +} diff --git a/packages/desktop/src/main/ipc-handlers/context.ts b/packages/desktop/src/main/ipc-handlers/context.ts new file mode 100644 index 00000000000..705a78a0214 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/context.ts @@ -0,0 +1,9 @@ +import type { IpcPortHandoff } from "../ipc-transport" + +export type RpcContext = { readonly client: { readonly id: number } } + +export function sender(handoff: IpcPortHandoff["Service"], context: RpcContext) { + const contents = handoff.sender(context.client.id) + if (!contents || contents.isDestroyed()) throw new Error("Renderer connection not found") + return contents +} diff --git a/packages/desktop/src/main/ipc-handlers/events.ts b/packages/desktop/src/main/ipc-handlers/events.ts new file mode 100644 index 00000000000..018bbb1d8d9 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/events.ts @@ -0,0 +1,14 @@ +import { Effect } from "effect" +import { EventRpcs } from "../../shared/ipc-rpc" +import { ipcEventStream } from "../ipc-events" +import { IpcPortHandoff } from "../ipc-transport" +import { sender } from "./context" + +export const eventHandlers = EventRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + return EventRpcs.of({ + DesktopEvents: (_request, context) => ipcEventStream(sender(handoff, context).id), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/files.ts b/packages/desktop/src/main/ipc-handlers/files.ts new file mode 100644 index 00000000000..a99d26175a1 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/files.ts @@ -0,0 +1,42 @@ +import { Effect } from "effect" +import { FileRpcs } from "../../shared/ipc-rpc" +import { DesktopFiles, openExternalURL, openLocalFileURL } from "../files" +import { IpcPortHandoff } from "../ipc-transport" +import { sender } from "./context" + +export const fileHandlers = FileRpcs.toLayer( + Effect.gen(function* () { + const files = yield* DesktopFiles.Service + const handoff = yield* IpcPortHandoff + return FileRpcs.of({ + FilesOpenDirectoryPicker: ({ options }) => files.openDirectoryPicker(options), + FilesOpenFilePicker: ({ options }, context) => + files + .openFilePicker( + sender(handoff, context).id, + options ? { ...options, extensions: options.extensions && [...options.extensions] } : undefined, + ) + .pipe(Effect.orDie), + FilesReadPickedFile: ({ token, path }, context) => + files + .readPickedFile(sender(handoff, context).id, token, path) + .pipe(Effect.map((buffer) => new Uint8Array(buffer)), Effect.orDie), + FilesReleasePickedFiles: ({ token }, context) => + Effect.sync(() => files.releasePickedFiles(sender(handoff, context).id, token)), + FilesSaveFilePicker: ({ options }) => files.saveFilePicker(options), + FilesOpenExternal: ({ url }) => openExternalURL(url), + FilesOpenLocalFile: ({ url }) => openLocalFileURL(url), + FilesOpenPath: ({ path, application }) => + files.openPath(path, application).pipe( + Effect.map((result) => result ?? null), + Effect.orDie, + ), + FilesRevealPath: ({ path }) => files.revealPath(path), + FilesReadClipboardImage: () => + Effect.sync(() => { + const image = files.readClipboardImage() + return image ? { ...image, buffer: new Uint8Array(image.buffer) } : null + }), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/menu.ts b/packages/desktop/src/main/ipc-handlers/menu.ts new file mode 100644 index 00000000000..0f484169685 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/menu.ts @@ -0,0 +1,27 @@ +import { BrowserWindow } from "electron" +import { Effect } from "effect" +import { MenuRpcs } from "../../shared/ipc-rpc" +import { IpcPortHandoff } from "../ipc-transport" +import { ApplicationLifecycle } from "../lifecycle" +import { runDesktopMenuAction } from "../native/menu-actions" +import { Updater } from "../updater" +import { sender } from "./context" + +export const menuHandlers = MenuRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + const lifecycle = yield* ApplicationLifecycle.Service + const updater = yield* Updater.Service + const runFork = Effect.runForkWith(yield* Effect.context()) + return MenuRpcs.of({ + MenuRunAction: ({ action }, context) => + Effect.sync(() => + runDesktopMenuAction(BrowserWindow.fromWebContents(sender(handoff, context)), action, { + checkForUpdates: () => runFork(updater.show), + createWindow: lifecycle.createWindow, + relaunch: lifecycle.relaunch, + }), + ), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/storage.ts b/packages/desktop/src/main/ipc-handlers/storage.ts new file mode 100644 index 00000000000..c01395410ab --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/storage.ts @@ -0,0 +1,29 @@ +import { Effect } from "effect" +import { StorageRpcs } from "../../shared/ipc-rpc" +import { DesktopStorage } from "../storage" + +export const storageHandlers = StorageRpcs.toLayer( + Effect.gen(function* () { + const storage = yield* DesktopStorage.Service + return StorageRpcs.of({ + StorageGet: ({ name, key }) => Effect.sync(() => storage.get(name, key)), + StorageSet: ({ name, key, value }) => Effect.sync(() => storage.set(name, key, value)), + StorageDelete: ({ name, key }) => storage.deleteValue(name, key).pipe(Effect.orDie), + StorageClear: ({ name }) => storage.clear(name).pipe(Effect.orDie), + StorageKeys: ({ name }) => Effect.sync(() => storage.keys(name)), + StorageLength: ({ name }) => Effect.sync(() => storage.length(name)), + DraftsGet: ({ key }) => Effect.sync(() => storage.drafts.get(key)), + DraftsSet: ({ key, value }) => Effect.sync(() => storage.drafts.set(key, value)), + DraftsDelete: ({ key }) => Effect.sync(() => storage.drafts.set(key, null)), + DraftsPutBlob: ({ data }) => + Effect.sync(() => + storage.drafts.putBlob(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer), + ), + DraftsGetBlob: ({ id }) => + Effect.sync(() => { + const data = storage.drafts.getBlob(id) + return data ? new Uint8Array(data) : null + }), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/updater.ts b/packages/desktop/src/main/ipc-handlers/updater.ts new file mode 100644 index 00000000000..be1f8d98af8 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/updater.ts @@ -0,0 +1,18 @@ +import { Effect } from "effect" +import { UpdaterRpcs } from "../../shared/ipc-rpc" +import { IpcPortHandoff } from "../ipc-transport" +import { Updater } from "../updater" +import { sender } from "./context" + +export const updaterHandlers = UpdaterRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + const updater = yield* Updater.Service + return UpdaterRpcs.of({ + UpdaterSubscribe: (_args, context) => updater.subscribe(sender(handoff, context)), + UpdaterUnsubscribe: (_args, context) => updater.unsubscribe(sender(handoff, context).id), + UpdaterCheck: () => updater.check, + UpdaterInstall: () => updater.install, + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/window.ts b/packages/desktop/src/main/ipc-handlers/window.ts new file mode 100644 index 00000000000..4cb48630c15 --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/window.ts @@ -0,0 +1,58 @@ +import { BrowserWindow } from "electron" +import { Effect } from "effect" +import { WindowRpcs } from "../../shared/ipc-rpc" +import { IpcPortHandoff } from "../ipc-transport" +import { + getPinchZoomEnabled, + getWindowID, + setPinchZoomEnabled, + setTitlebar, + setWindowThemeReady, + updateTitlebar, +} from "../windows" +import { sender } from "./context" + +export const windowHandlers = WindowRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + return WindowRpcs.of({ + WindowGetId: (_args, context) => + Effect.sync(() => { + const win = BrowserWindow.fromWebContents(sender(handoff, context)) + if (!win) throw new Error("Window not found") + const id = getWindowID(win) + if (!id) throw new Error("Window ID not found") + return id + }), + WindowThemeReady: (_args, context) => + Effect.sync(() => { + const win = BrowserWindow.fromWebContents(sender(handoff, context)) + if (!win) throw new Error("Window not found") + setWindowThemeReady(win) + }), + WindowGetFocused: (_args, context) => + Effect.sync(() => BrowserWindow.fromWebContents(sender(handoff, context))?.isFocused() ?? false), + WindowGetFullscreen: (_args, context) => + Effect.sync(() => BrowserWindow.fromWebContents(sender(handoff, context))?.isFullScreen() ?? false), + WindowSetFocus: (_args, context) => + Effect.sync(() => BrowserWindow.fromWebContents(sender(handoff, context))?.focus()), + WindowShow: (_args, context) => + Effect.sync(() => BrowserWindow.fromWebContents(sender(handoff, context))?.show()), + WindowGetZoomFactor: (_args, context) => Effect.sync(() => sender(handoff, context).getZoomFactor()), + WindowSetZoomFactor: ({ factor }, context) => + Effect.sync(() => { + const contents = sender(handoff, context) + contents.setZoomFactor(factor) + const win = BrowserWindow.fromWebContents(contents) + if (win) updateTitlebar(win) + }), + WindowGetPinchZoomEnabled: () => Effect.sync(getPinchZoomEnabled), + WindowSetPinchZoomEnabled: ({ enabled }) => Effect.sync(() => setPinchZoomEnabled(enabled)), + WindowSetTitlebar: ({ theme }, context) => + Effect.sync(() => { + const win = BrowserWindow.fromWebContents(sender(handoff, context)) + if (win) setTitlebar(win, theme) + }), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-handlers/wsl.ts b/packages/desktop/src/main/ipc-handlers/wsl.ts new file mode 100644 index 00000000000..8757ee0f99d --- /dev/null +++ b/packages/desktop/src/main/ipc-handlers/wsl.ts @@ -0,0 +1,27 @@ +import { Effect } from "effect" +import { WslRpcs } from "../../shared/ipc-rpc" +import { IpcPortHandoff } from "../ipc-transport" +import { Wsl } from "../wsl/start" +import { sender } from "./context" + +export const wslHandlers = WslRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + const wsl = yield* Wsl.Service + return WslRpcs.of({ + WslSubscribe: (_args, context) => wsl.subscribe(sender(handoff, context)), + WslUnsubscribe: (_args, context) => wsl.unsubscribe(sender(handoff, context).id), + WslGetState: () => wsl.getState(), + WslProbeRuntime: () => wsl.probeRuntime(), + WslRefreshDistros: () => wsl.refreshDistros(), + WslInstallWsl: () => wsl.installWsl(), + WslInstallDistro: ({ name }) => wsl.installDistro(name), + WslProbeAddable: ({ distros }) => wsl.probeAddable([...distros]), + WslInstallOpencode: ({ name }) => wsl.installOpencode(name), + WslOpenTerminal: ({ name }) => wsl.openTerminal(name), + WslAddServer: ({ distro }) => wsl.addServer(distro), + WslRemoveServer: ({ id }) => wsl.removeServer(id), + WslStartServer: ({ id }) => wsl.startServer(id), + }) + }), +) diff --git a/packages/desktop/src/main/ipc-transport.test.ts b/packages/desktop/src/main/ipc-transport.test.ts new file mode 100644 index 00000000000..b75fb463a8c --- /dev/null +++ b/packages/desktop/src/main/ipc-transport.test.ts @@ -0,0 +1,181 @@ +import { describe, expect, test } from "bun:test" +import { EventEmitter } from "node:events" +import { MessageChannel } from "node:worker_threads" +import type { MessagePortMain, WebContents } from "electron" +import { Context, Effect, Layer, ManagedRuntime, Option, Queue, Schema, Stream } from "effect" +import { Rpc, RpcClient, RpcClientError, RpcGroup, RpcMessage, RpcSerialization, RpcServer } from "effect/unstable/rpc" +import { IpcPortHandoff, IpcServerProtocolLive } from "./ipc-transport" + +describe("desktop RPC transport", () => { + test("keeps multiple renderer ports independent", async () => { + const handlers = TestRpcs.toLayer( + Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + return TestRpcs.of({ + "test.focused": (_request, context) => Effect.succeed(handoff.sender(context.client.id)?.id === 1), + "test.blob.put": ({ data }) => Effect.succeed([...data].join(",")), + "test.blob.get": () => Effect.succeed(new Uint8Array([3, 1, 4])), + "test.events": () => Stream.make(new TestEvent({ value: "session.new" })), + }) + }), + ) + const live = RpcServer.layer(TestRpcs).pipe(Layer.provide(handlers), Layer.provideMerge(IpcServerProtocolLive)) + const runtime = ManagedRuntime.make(live) + const handoff = await runtime.runPromise(IpcPortHandoff) + const first = new MessageChannel() + const second = new MessageChannel() + handoff.bind(sender(1), serverPort(first.port1)) + handoff.bind(sender(2), serverPort(second.port1)) + const firstClient = makeClient(first.port2) + const secondClient = makeClient(second.port2) + + const [focused, unfocused] = await Promise.all([callFocused(firstClient), callFocused(secondClient)]) + + expect(focused).toBe(true) + expect(unfocused).toBe(false) + expect(await putBlob(firstClient, new Uint8Array([2, 7, 1]))).toBe("2,7,1") + expect(await getBlob(firstClient)).toEqual(new Uint8Array([3, 1, 4])) + expect(await firstEvent(firstClient)).toEqual(new TestEvent({ value: "session.new" })) + + const reloaded = new MessageChannel() + handoff.bind(sender(1), serverPort(reloaded.port1)) + const reloadedClient = makeClient(reloaded.port2) + const [reloadedFocused, stillUnfocused] = await Promise.all([ + callFocused(reloadedClient), + callFocused(secondClient), + ]) + expect(reloadedFocused).toBe(true) + expect(stillUnfocused).toBe(false) + await Promise.all([firstClient.dispose(), secondClient.dispose(), reloadedClient.dispose()]) + await runtime.dispose() + }) +}) + +class TestEvent extends Schema.TaggedClass()("TestEvent", { value: Schema.String }) {} +const TestRpcs = RpcGroup.make( + Rpc.make("test.focused", { success: Schema.Boolean }), + Rpc.make("test.blob.put", { payload: { data: Schema.Uint8Array }, success: Schema.String }), + Rpc.make("test.blob.get", { success: Schema.Uint8Array }), + Rpc.make("test.events", { success: TestEvent, stream: true }), +) +type TestRpcClient = RpcClient.FromGroup + +class TestClient extends Context.Service()("opencode/desktop/TestClient") {} + +function makeClient(port: MessagePort) { + return ManagedRuntime.make( + Layer.effect(TestClient, RpcClient.make(TestRpcs)).pipe(Layer.provide(clientProtocol(port))), + ) +} + +function callFocused(runtime: ManagedRuntime.ManagedRuntime) { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* TestClient + return yield* client["test.focused"]() + }), + ) +} + +function putBlob(runtime: ManagedRuntime.ManagedRuntime, data: Uint8Array) { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* TestClient + return yield* client["test.blob.put"]({ data }) + }), + ) +} + +function getBlob(runtime: ManagedRuntime.ManagedRuntime) { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* TestClient + return yield* client["test.blob.get"]() + }), + ) +} + +function firstEvent(runtime: ManagedRuntime.ManagedRuntime) { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* TestClient + return yield* client["test.events"]().pipe(Stream.runHead, Effect.map(Option.getOrThrow)) + }), + ) +} + +function clientProtocol(port: MessagePort) { + return Layer.effect( + RpcClient.Protocol, + RpcClient.Protocol.make( + Effect.fnUntraced(function* (writeResponse, clientIds) { + const serialization = yield* RpcSerialization.RpcSerialization + const parser = serialization.makeUnsafe() + const inbound = yield* Queue.unbounded() + const onMessage = (event: MessageEvent) => + parser + .decode(event.data) + .forEach((message) => Queue.offerUnsafe(inbound, message as RpcMessage.FromServerEncoded)) + port.addEventListener("message", onMessage) + port.start() + yield* Effect.addFinalizer(() => + Effect.sync(() => { + port.removeEventListener("message", onMessage) + port.close() + }), + ) + yield* Stream.fromQueue(inbound).pipe( + Stream.runForEach((message) => + Effect.forEach(clientIds, (clientId) => writeResponse(clientId, message), { discard: true }), + ), + Effect.forkScoped, + ) + return { + send: (_clientId: number, request: RpcMessage.FromClientEncoded) => + Effect.sync(() => { + const encoded = parser.encode(request) + if (encoded !== undefined) port.postMessage(encoded) + }), + supportsAck: true, + supportsTransferables: false, + } + }), + ), + ).pipe(Layer.provide(RpcSerialization.layerMsgPack)) +} + +function sender(id: number) { + const events = new EventEmitter() + return { + id, + isDestroyed: () => false, + once: events.once.bind(events), + off: events.off.bind(events), + } as unknown as WebContents +} + +function serverPort(port: import("node:worker_threads").MessagePort) { + const listeners = new Map<(event: Electron.MessageEvent) => void, (data: unknown) => void>() + return { + on(event: string, listener: (event: Electron.MessageEvent) => void) { + if (event !== "message") { + port.on(event, listener) + return + } + const wrapped = (data: unknown) => listener({ data } as Electron.MessageEvent) + listeners.set(listener, wrapped) + port.on("message", wrapped) + }, + off(event: string, listener: (event: Electron.MessageEvent) => void) { + if (event !== "message") { + port.off(event, listener) + return + } + const wrapped = listeners.get(listener) + if (wrapped) port.off("message", wrapped) + }, + postMessage: port.postMessage.bind(port), + start: port.start.bind(port), + close: port.close.bind(port), + } as unknown as MessagePortMain +} diff --git a/packages/desktop/src/main/ipc-transport.ts b/packages/desktop/src/main/ipc-transport.ts new file mode 100644 index 00000000000..eae96a5f253 --- /dev/null +++ b/packages/desktop/src/main/ipc-transport.ts @@ -0,0 +1,124 @@ +import type { MessagePortMain, WebContents } from "electron" +import { Context, Effect, Layer, Option, Queue, Stream } from "effect" +import { RpcMessage, RpcSerialization, RpcServer } from "effect/unstable/rpc" +import { bindIpcEvents } from "./ipc-events" + +type PortBinding = { + readonly id: number + readonly sender: WebContents + readonly port: MessagePortMain + readonly parser: RpcSerialization.Parser + readonly onMessage: (event: Electron.MessageEvent) => void + readonly onClose: () => void + readonly unbindEvents: Effect.Effect +} + +type Handoff = { + readonly bind: (sender: WebContents, port: MessagePortMain) => void + readonly sender: (clientId: number) => WebContents | undefined +} + +export class IpcPortHandoff extends Context.Service()("opencode/desktop/IpcPortHandoff") {} + +export const IpcServerProtocolLive = Layer.unwrap( + Effect.gen(function* () { + const handoffs = yield* Queue.unbounded() + const bindings = new Map() + const senderBindings = new Map() + + const protocol = Layer.effect( + RpcServer.Protocol, + RpcServer.Protocol.make( + Effect.fnUntraced(function* (writeRequest) { + const serialization = yield* RpcSerialization.RpcSerialization + const disconnects = yield* Queue.unbounded() + const inbound = yield* Queue.unbounded() + const runFork = Effect.runForkWith(yield* Effect.context()) + let nextClientId = 0 + + const disconnect = Effect.fnUntraced(function* (id: number) { + const binding = bindings.get(id) + if (!binding) return + bindings.delete(id) + if (senderBindings.get(binding.sender.id) === id) senderBindings.delete(binding.sender.id) + binding.port.off("message", binding.onMessage) + binding.port.off("close", binding.onClose) + binding.sender.off("destroyed", binding.onClose) + yield* binding.unbindEvents + binding.port.close() + Queue.offerUnsafe(disconnects, id) + }) + + const bind = Effect.fnUntraced(function* (sender: WebContents, port: MessagePortMain) { + const previous = senderBindings.get(sender.id) + if (previous !== undefined) yield* disconnect(previous) + if (sender.isDestroyed()) { + port.close() + return + } + + const id = nextClientId++ + const parser = serialization.makeUnsafe() + const onMessage = (event: Electron.MessageEvent) => { + try { + parser + .decode(event.data) + .forEach((message) => + Queue.offerUnsafe(inbound, [id, message as RpcMessage.FromClientEncoded] as const), + ) + } catch { + return + } + } + const onClose = () => runFork(disconnect(id)) + const unbindEvents = yield* bindIpcEvents(sender.id) + const binding = { id, sender, port, parser, onMessage, onClose, unbindEvents } + bindings.set(id, binding) + senderBindings.set(sender.id, id) + port.on("message", onMessage) + port.on("close", onClose) + sender.once("destroyed", onClose) + port.start() + }) + + yield* Stream.fromQueue(handoffs).pipe( + Stream.runForEach(([sender, port]) => bind(sender, port)), + Effect.forkScoped, + ) + yield* Stream.fromQueue(inbound).pipe( + Stream.runForEach(([id, message]) => (bindings.has(id) ? writeRequest(id, message) : Effect.void)), + Effect.forkScoped, + ) + yield* Effect.addFinalizer(() => Effect.forEach([...bindings.keys()], disconnect, { discard: true })) + + return { + disconnects, + send: (clientId, response) => + Effect.sync(() => { + const binding = bindings.get(clientId) + if (!binding) return + const encoded = binding.parser.encode(response) + if (encoded !== undefined) binding.port.postMessage(encoded) + }), + end: disconnect, + clientIds: Effect.sync(() => new Set(bindings.keys())), + initialMessage: Effect.succeed(Option.none()), + supportsAck: true, + supportsTransferables: false, + supportsSpanPropagation: false, + } + }), + ), + ) + + return Layer.merge( + protocol, + Layer.succeed(IpcPortHandoff)({ + bind: (sender, port) => { + Queue.offerUnsafe(handoffs, [sender, port]) + }, + sender: (clientId) => bindings.get(clientId)?.sender, + }), + ) + }), +).pipe(Layer.provide(RpcSerialization.layerMsgPack)) diff --git a/packages/desktop/src/main/ipc.ts b/packages/desktop/src/main/ipc.ts index 44d67d0c395..7fb4158dd99 100644 --- a/packages/desktop/src/main/ipc.ts +++ b/packages/desktop/src/main/ipc.ts @@ -1,201 +1,73 @@ -import { BrowserWindow, ipcMain } from "electron" -import type { IpcMainEvent, IpcMainInvokeEvent } from "electron" -import { parseDesktopNativeBundle, type DesktopNativeBundle } from "@opencode-ai/app/i18n/desktop-native" +export * as Ipc from "./ipc" -import { - Ipc, - type FatalRendererError, - type IpcInvoke, - type IpcInvokeArgs, - type IpcInvokeResult, - type IpcSend, - type ServerReadyData, -} from "../shared/ipc-contract" -import { createFileCapabilities, openExternalURL, openLocalFileURL } from "./files" -import { setForceFocus } from "./native/debug" -import { runDesktopMenuAction } from "./native/menu-actions" -import { createDesktopStorage } from "./storage" -import { - getPinchZoomEnabled, - getWindowID, - setPinchZoomEnabled, - setTitlebar, - setWindowThemeReady, - updateTitlebar, -} from "./windows" -import type { UpdaterIpc } from "./updater" -import type { WslIpc } from "./wsl/ipc" +import { app, BrowserWindow, MessageChannelMain } from "electron" +import { Effect, Layer } from "effect" +import { RpcServer } from "effect/unstable/rpc" +import { DesktopRpcs } from "../shared/ipc-rpc" +import { IpcTransportPort } from "../shared/ipc-transport" +import { DesktopFiles, openExternalURL } from "./files" +import { appHandlers } from "./ipc-handlers/app" +import { eventHandlers } from "./ipc-handlers/events" +import { fileHandlers } from "./ipc-handlers/files" +import { menuHandlers } from "./ipc-handlers/menu" +import { storageHandlers } from "./ipc-handlers/storage" +import { updaterHandlers } from "./ipc-handlers/updater" +import { windowHandlers } from "./ipc-handlers/window" +import { wslHandlers } from "./ipc-handlers/wsl" +import { IpcPortHandoff, IpcServerProtocolLive } from "./ipc-transport" +import { ApplicationLifecycle } from "./lifecycle" +import { createMenu, sendMenuCommand } from "./native/menu" +import { DesktopStorage } from "./storage" +import { Updater } from "./updater" +import { getLastFocusedWindow } from "./windows" +import { Wsl } from "./wsl/start" -type MaybePromise = Value | Promise +const services = Layer.mergeAll(DesktopFiles.layer, DesktopStorage.layer, Wsl.layer) +const handlers = Layer.mergeAll( + appHandlers, + storageHandlers, + fileHandlers, + windowHandlers, + menuHandlers, + updaterHandlers, + wslHandlers, + eventHandlers, +) +export const layer = RpcServer.layer(DesktopRpcs, { disableFatalDefects: true }).pipe( + Layer.provide(handlers), + Layer.provideMerge(IpcServerProtocolLive), + Layer.provideMerge(services), +) -function handle( - channel: Channel, - listener: (event: IpcMainInvokeEvent, ...args: IpcInvokeArgs) => MaybePromise>, -) { - ipcMain.handle(channel, listener) -} - -function on( - channel: Channel, - listener: (event: IpcMainEvent, ...args: IpcSend[Channel]) => void, -) { - ipcMain.on(channel, listener) -} - -type Deps = { - relaunch: () => void - awaitInitialization: () => Promise - consumeInitialDeepLinks: () => Promise | string[] - getDefaultServerUrl: () => Promise | string | null - setDefaultServerUrl: (url: string | null) => Promise | void - isFirstLaunchOnboardingPending: () => Promise | boolean - finishFirstLaunchOnboarding: (createDefaultProject: boolean) => Promise | string | null - checkAppExists: (appName: string) => Promise | boolean - resolveAppPath: (appName: string) => Promise - showUpdater: () => Promise | void - setBackgroundColor: (color: string) => void - exportDebugLogs: () => Promise - recordFatalRendererError: (error: FatalRendererError) => Promise | void - setNativeTranslations: (bundle: DesktopNativeBundle) => void -} - -export function registerIpcHandlers(deps: Deps) { - const files = createFileCapabilities() - const storage = createDesktopStorage() - - handle(Ipc.app.awaitInitialization, () => deps.awaitInitialization()) - handle(Ipc.app.consumeInitialDeepLinks, () => deps.consumeInitialDeepLinks()) - handle(Ipc.app.getDefaultServerUrl, () => deps.getDefaultServerUrl()) - handle(Ipc.app.setDefaultServerUrl, (_event, url) => deps.setDefaultServerUrl(url)) - handle(Ipc.app.isFirstLaunchOnboardingPending, () => deps.isFirstLaunchOnboardingPending()) - handle(Ipc.app.finishFirstLaunchOnboarding, (_event, createDefaultProject) => - deps.finishFirstLaunchOnboarding(createDefaultProject), - ) - handle(Ipc.app.checkAppExists, (_event, appName) => deps.checkAppExists(appName)) - handle(Ipc.app.resolveAppPath, (_event, appName) => deps.resolveAppPath(appName)) - handle(Ipc.app.setBackgroundColor, (_event, color) => deps.setBackgroundColor(color)) - handle(Ipc.app.exportDebugLogs, () => deps.exportDebugLogs()) - handle(Ipc.app.setForceFocus, (event, enabled) => setForceFocus(event.sender, enabled)) - handle(Ipc.app.recordFatalRendererError, (_event, error) => deps.recordFatalRendererError(error)) - handle(Ipc.app.setNativeTranslations, (event, value) => { - const win = BrowserWindow.fromWebContents(event.sender) - if (!win || win.isDestroyed() || win.webContents !== event.sender || event.senderFrame !== event.sender.mainFrame) { - throw new Error("Invalid native translation sender") - } - const bundle = parseDesktopNativeBundle(value) - if (!bundle) throw new Error("Invalid native translation bundle") - deps.setNativeTranslations(bundle) - }) - handle(Ipc.storage.get, (_event, name, key) => { - return storage.get(name, key) - }) - handle(Ipc.storage.set, (_event, name, key, value) => storage.set(name, key, value)) - handle(Ipc.storage.delete, (_event, name, key) => storage.deleteValue(name, key)) - handle(Ipc.storage.clear, (_event, name) => storage.clear(name)) - handle(Ipc.storage.keys, (_event, name) => storage.keys(name)) - handle(Ipc.storage.length, (_event, name) => storage.length(name)) - handle(Ipc.drafts.get, (_event, key) => storage.drafts.get(key)) - handle(Ipc.drafts.set, (_event, key, value) => storage.drafts.set(key, value)) - handle(Ipc.drafts.delete, (_event, key) => storage.drafts.set(key, null)) - handle(Ipc.drafts.putBlob, (_event, data) => storage.drafts.putBlob(data)) - handle(Ipc.drafts.getBlob, (_event, id) => storage.drafts.getBlob(id)) - - handle(Ipc.files.openDirectoryPicker, (_event, options) => files.openDirectoryPicker(options)) - handle(Ipc.files.openFilePicker, (event, options) => files.openFilePicker(event.sender.id, options)) - handle(Ipc.files.readPickedFile, (event, token, path) => files.readPickedFile(event.sender.id, token, path)) - handle(Ipc.files.releasePickedFiles, (event, token) => files.releasePickedFiles(event.sender.id, token)) - handle(Ipc.files.saveFilePicker, (_event, options) => files.saveFilePicker(options)) - on(Ipc.files.openExternal, (_event, url) => openExternalURL(url)) - on(Ipc.files.openLocalFile, (_event, url) => openLocalFileURL(url)) - handle(Ipc.files.openPath, (_event, path, app) => files.openPath(path, app)) - handle(Ipc.files.revealPath, (_event, path) => files.revealPath(path)) - handle(Ipc.files.readClipboardImage, () => files.readClipboardImage()) - - handle(Ipc.window.getId, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - if (!win) throw new Error("Window not found") - const id = getWindowID(win) - if (!id) throw new Error("Window ID not found") - return id - }) - - handle(Ipc.window.themeReady, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - if (!win) throw new Error("Window not found") - setWindowThemeReady(win) - }) - - handle(Ipc.window.getFocused, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - return win?.isFocused() ?? false - }) - - handle(Ipc.window.getFullscreen, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - return win?.isFullScreen() ?? false - }) - - handle(Ipc.window.setFocus, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - win?.focus() - }) - - handle(Ipc.window.show, (event) => { - const win = BrowserWindow.fromWebContents(event.sender) - win?.show() - }) - - on(Ipc.app.relaunch, () => { - deps.relaunch() - }) - - handle(Ipc.window.getZoomFactor, (event) => event.sender.getZoomFactor()) - handle(Ipc.window.setZoomFactor, (event, factor) => { - event.sender.setZoomFactor(factor) - const win = BrowserWindow.fromWebContents(event.sender) - if (!win) return - updateTitlebar(win) - }) - handle(Ipc.window.getPinchZoomEnabled, () => getPinchZoomEnabled()) - handle(Ipc.window.setPinchZoomEnabled, (_event, enabled) => { - setPinchZoomEnabled(enabled) - }) - handle(Ipc.window.setTitlebar, (event, theme) => { - const win = BrowserWindow.fromWebContents(event.sender) - if (!win) return - setTitlebar(win, theme) - }) - handle(Ipc.menu.runAction, (event, action) => { - runDesktopMenuAction(BrowserWindow.fromWebContents(event.sender), action, { - checkForUpdates: () => void deps.showUpdater(), - relaunch: deps.relaunch, +export const registerIpcHandlers = Effect.gen(function* () { + const handoff = yield* IpcPortHandoff + const lifecycle = yield* ApplicationLifecycle.Service + const updater = yield* Updater.Service + const runFork = Effect.runForkWith(yield* Effect.context()) + const menu = { + trigger: (id: string) => { + const win = getLastFocusedWindow() + if (win) sendMenuCommand(win, id) + }, + checkForUpdates: () => runFork(updater.show), + createWindow: lifecycle.createWindow, + openExternal: (url: string) => runFork(openExternalURL(url)), + relaunch: lifecycle.relaunch, + } + const wire = (_event: Electron.Event, win: BrowserWindow) => { + win.webContents.on("did-finish-load", () => { + if (win.isDestroyed() || win.webContents.isDestroyed()) return + const channel = new MessageChannelMain() + handoff.bind(win.webContents, channel.port1) + win.webContents.postMessage(IpcTransportPort, null, [channel.port2]) }) + } + yield* Effect.sync(() => { + app.on("browser-window-created", wire) + BrowserWindow.getAllWindows().forEach((win) => wire({} as Electron.Event, win)) }) -} - -export function registerUpdaterIpcHandlers(updater: UpdaterIpc) { - handle(Ipc.updater.subscribe, (event) => updater.subscribe(event.sender)) - handle(Ipc.updater.unsubscribe, (event) => updater.unsubscribe(event.sender.id)) - handle(Ipc.updater.check, () => updater.check()) - handle(Ipc.updater.install, () => updater.install()) -} - -export function registerWslInitialization(ready: Promise) { - handle(Ipc.wsl.awaitInitialization, () => ready) -} - -export function registerWslIpcHandlers(wsl: WslIpc) { - handle(Ipc.wsl.subscribe, (event) => wsl.subscribe(event.sender)) - handle(Ipc.wsl.unsubscribe, (event) => wsl.unsubscribe(event.sender.id)) - handle(Ipc.wsl.getState, () => wsl.getState()) - handle(Ipc.wsl.probeRuntime, () => wsl.probeRuntime()) - handle(Ipc.wsl.refreshDistros, () => wsl.refreshDistros()) - handle(Ipc.wsl.installWsl, () => wsl.installWsl()) - handle(Ipc.wsl.installDistro, (_event, value) => wsl.installDistro(value)) - handle(Ipc.wsl.probeAddable, (_event, value) => wsl.probeAddable(value)) - handle(Ipc.wsl.installOpencode, (_event, value) => wsl.installOpencode(value)) - handle(Ipc.wsl.openTerminal, (_event, value) => wsl.openTerminal(value)) - handle(Ipc.wsl.addServer, (_event, value) => wsl.addServer(value)) - handle(Ipc.wsl.removeServer, (_event, value) => wsl.removeServer(value)) - handle(Ipc.wsl.startServer, (_event, value) => wsl.startServer(value)) -} + yield* Effect.addFinalizer(() => Effect.sync(() => app.off("browser-window-created", wire))) + return { + installMenu: () => createMenu(menu), + } +}) diff --git a/packages/desktop/src/main/lifecycle/desktop-initialization.ts b/packages/desktop/src/main/lifecycle/desktop-initialization.ts new file mode 100644 index 00000000000..f8153759e79 --- /dev/null +++ b/packages/desktop/src/main/lifecycle/desktop-initialization.ts @@ -0,0 +1,38 @@ +export * as DesktopInitialization from "./desktop-initialization" + +import { app } from "electron" +import { Context, Effect, Layer } from "effect" +import { DesktopLogging } from "../native/logging" +import { getStore } from "../storage/store" +import { + loadProxyEnvironment, + preferApplicationEnvironment, + prepareApplicationEnvironment, + prepareDesktop, +} from "./environment" +import { initializeFirstLaunchOnboarding } from "./onboarding" + +export interface Interface { + readonly version: string + readonly updaterStore: ReturnType +} + +export class Service extends Context.Service()("opencode/desktop/DesktopInitialization") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const logging = yield* DesktopLogging.Service + yield* initializeFirstLaunchOnboarding(app.getPath("userData")) + yield* prepareApplicationEnvironment + yield* preferApplicationEnvironment + yield* loadProxyEnvironment + yield* Effect.promise(() => app.whenReady()) + yield* logging.startNetwork + yield* prepareDesktop + return Service.of({ + version: app.getVersion(), + updaterStore: getStore("opencode.updater"), + }) + }), +) diff --git a/packages/desktop/src/main/lifecycle/environment.ts b/packages/desktop/src/main/lifecycle/environment.ts index 468eb2ff314..cc7ae5bd04d 100644 --- a/packages/desktop/src/main/lifecycle/environment.ts +++ b/packages/desktop/src/main/lifecycle/environment.ts @@ -1,18 +1,15 @@ import { randomUUID } from "node:crypto" -import { mkdirSync, rmSync } from "node:fs" import http from "node:http" import { homedir, tmpdir } from "node:os" -import { join } from "node:path" import { getCACertificates, setDefaultCACertificates } from "node:tls" import { app } from "electron" import contextMenu from "electron-context-menu" -import { Effect } from "effect" -import { CHANNEL, VERSION } from "../constants" -import { initCrashReporter, initLogging, type DesktopLogger } from "../native/logging" +import { Effect, FileSystem, Path } from "effect" +import { CHANNEL } from "../constants" +import { DesktopPaths } from "../paths" import { getUserShell, loadShellEnv } from "../service/shell-env" import { cleanupStoreFiles } from "../storage/cleanup" import { registerRendererProtocol, setDockIcon } from "../windows" -import { initializeFirstLaunchOnboarding } from "./onboarding" const appNames: Record = { dev: "OpenCode Dev", @@ -27,7 +24,8 @@ const appIDs: Record = { const testOnboarding = process.env.OPENCODE_TEST_ONBOARDING === "1" const jsCallStackFeature = "DocumentPolicyIncludeJSCallStacksInCrashReports" -export function configureApplication() { +export const configureApplication = Effect.fn("Application.configure")(function* () { + const path = yield* Path.Path contextMenu({ showSaveImageAs: true, showLookUpSelection: false, showSearchWithGoogle: false }) try { process.chdir(homedir()) @@ -35,30 +33,18 @@ export function configureApplication() { process.env.OPENCODE_DISABLE_EMBEDDED_WEB_UI = "true" const appID = app.isPackaged ? appIDs[CHANNEL] : "ai.opencode.desktop.dev" - const testRoot = createTestRoot() app.setName(app.isPackaged ? appNames[CHANNEL] : "OpenCode Dev") app.setAppUserModelId(appID) - app.setPath("userData", testRoot ? join(testRoot, "desktop") : join(app.getPath("appData"), appID)) - if (testRoot) app.setPath("sessionData", join(testRoot, "session")) - - initializeFirstLaunchOnboarding(app.getPath("userData")) - const logger = initLogging() - initCrashReporter() - loadSystemCertificates(logger) - logger.log("app starting", { - version: VERSION, - packaged: app.isPackaged, - onboardingTest: testOnboarding, - }) - - loadProxyEnvironment(logger) app.commandLine.appendSwitch("proxy-bypass-list", "<-loopback>") const features = app.commandLine.getSwitchValue("enable-features") app.commandLine.appendSwitch("enable-features", features ? `${jsCallStackFeature},${features}` : jsCallStackFeature) if (!app.isPackaged) app.commandLine.appendSwitch("remote-debugging-port", process.env.OPENCODE_DESKTOP_REMOTE_DEBUGGING_PORT ?? "9222") - return logger -} + + const testRoot = yield* createTestRoot() + app.setPath("userData", testRoot ? path.join(testRoot, "desktop") : path.join(app.getPath("appData"), appID)) + if (testRoot) app.setPath("sessionData", path.join(testRoot, "session")) +}) export function acquireApplicationLock() { if (app.requestSingleInstanceLock()) return true @@ -66,73 +52,80 @@ export function acquireApplicationLock() { return false } -export function preferApplicationEnvironment(logger: DesktopLogger) { +export const prepareApplicationEnvironment = Effect.gen(function* () { + yield* loadSystemCertificates + yield* loadProxyEnvironment +}) + +export const preferApplicationEnvironment = Effect.gen(function* () { const shell = process.platform === "win32" ? null : getUserShell() - const shellEnv = shell ? loadShellEnv(shell, logger) : null - if (!shellEnv?.XDG_STATE_HOME) delete process.env.XDG_STATE_HOME - Object.assign(process.env, { - ...shellEnv, - OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true", - OPENCODE_EXPERIMENTAL_FILEWATCHER: "true", - OPENCODE_CLIENT: "desktop", + const shellEnv = shell ? yield* loadShellEnv(shell) : null + yield* Effect.sync(() => { + if (!shellEnv?.XDG_STATE_HOME) delete process.env.XDG_STATE_HOME + Object.assign(process.env, { + ...shellEnv, + OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true", + OPENCODE_EXPERIMENTAL_FILEWATCHER: "true", + OPENCODE_CLIENT: "desktop", + }) }) -} +}) -export function prepareDesktop(logger: DesktopLogger) { - return Effect.gen(function* () { - yield* Effect.promise(() => cleanupStoreFiles(app.getPath("userData"))).pipe( - Effect.tap((result) => - Effect.sync(() => { - if (result.deleted.length === 0) return - logger.log("cleaned scoped store files", { count: result.deleted.length, scanned: result.scanned }) - }), - ), - Effect.catch((error) => Effect.sync(() => logger.warn("failed to clean scoped store files", error))), - ) - if (app.isPackaged || process.env.OPENCODE_DESKTOP_DISABLE_PROTOCOL_REGISTRATION !== "1") - app.setAsDefaultProtocolClient("opencode") - registerRendererProtocol() - setDockIcon() - }) -} +export const prepareDesktop = Effect.gen(function* () { + const path = yield* Path.Path + const paths = yield* DesktopPaths.resolve + yield* cleanupStoreFiles(app.getPath("userData")).pipe( + Effect.tap((result) => + result.deleted.length === 0 + ? Effect.void + : Effect.logInfo("cleaned scoped store files", { count: result.deleted.length, scanned: result.scanned }), + ), + Effect.catch((error) => Effect.logWarning("failed to clean scoped store files", { error })), + ) + if (app.isPackaged || process.env.OPENCODE_DESKTOP_DISABLE_PROTOCOL_REGISTRATION !== "1") + app.setAsDefaultProtocolClient("opencode") + yield* registerRendererProtocol() + setDockIcon(path, paths) +}) -export function loadProxyEnvironment(logger: DesktopLogger) { - ensureLoopbackNoProxy() - try { +export const loadProxyEnvironment = Effect.gen(function* () { + yield* Effect.try(() => { + ensureLoopbackNoProxy() // Electron 41.2 has a newer Node API than the current @types/node package. const proxyAwareHttp = http as typeof http & { setGlobalProxyFromEnv(): void } proxyAwareHttp.setGlobalProxyFromEnv() - } catch (error) { - logger.warn("failed to load proxy environment", error) - } -} + }).pipe(Effect.catch((error) => Effect.logWarning("failed to load proxy environment", { error }))) +}) -function createTestRoot() { +const createTestRoot = Effect.fn("Application.createTestRoot")(function* () { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path const root = testOnboarding - ? join(tmpdir(), `opencode-onboarding-${randomUUID()}`) + ? path.join(tmpdir(), `opencode-onboarding-${randomUUID()}`) : app.isPackaged ? undefined : process.env.OPENCODE_DESKTOP_TEST_ROOT if (!root) return undefined - if (testOnboarding) rmSync(root, { recursive: true, force: true }) - ;["data", "config", "cache", "state", "desktop", "session"].forEach((dir) => - mkdirSync(join(root, dir), { recursive: true }), + if (testOnboarding) yield* fs.remove(root, { recursive: true, force: true }) + yield* Effect.forEach( + ["data", "config", "cache", "state", "desktop", "session"], + (dir) => fs.makeDirectory(path.join(root, dir), { recursive: true }), + { discard: true }, ) if (testOnboarding) process.env.OPENCODE_DB = ":memory:" - process.env.XDG_DATA_HOME = join(root, "data") - process.env.XDG_CONFIG_HOME = join(root, "config") - process.env.XDG_CACHE_HOME = join(root, "cache") - process.env.XDG_STATE_HOME = join(root, "state") + process.env.XDG_DATA_HOME = path.join(root, "data") + process.env.XDG_CONFIG_HOME = path.join(root, "config") + process.env.XDG_CACHE_HOME = path.join(root, "cache") + process.env.XDG_STATE_HOME = path.join(root, "state") return root -} +}) -function loadSystemCertificates(logger: DesktopLogger) { - try { +const loadSystemCertificates = Effect.try({ + try: () => { setDefaultCACertificates([...new Set([...getCACertificates("default"), ...getCACertificates("system")])]) - } catch (error) { - logger.warn("failed to load system certificates", error) - } -} + }, + catch: (error) => error, +}).pipe(Effect.catch((error) => Effect.logWarning("failed to load system certificates", { error }))) function ensureLoopbackNoProxy() { const loopback = ["127.0.0.1", "localhost", "::1"] diff --git a/packages/desktop/src/main/lifecycle/index.ts b/packages/desktop/src/main/lifecycle/index.ts index 39fcfe3ec27..68624d2ed38 100644 --- a/packages/desktop/src/main/lifecycle/index.ts +++ b/packages/desktop/src/main/lifecycle/index.ts @@ -1,80 +1,154 @@ +export * as ApplicationLifecycle from "./index" + import { app, BrowserWindow } from "electron" import type { Event } from "electron" -import { Ipc, sendIpcEvent } from "../../shared/ipc-contract" -import { writeLog, type DesktopLogger } from "../native/logging" +import { Context, Effect, Layer } from "effect" +import { DeepLinksOpened } from "../../shared/ipc-rpc/events" +import { emitIpcEvent } from "../ipc-events" +import { DesktopLogging, scoped } from "../native/logging" import { safeWebContentsURL } from "../windows/state" -import { getLastFocusedWindow, restoreMainWindows, setAppQuitting, setRelaunchHandler } from "../windows" +import { getLastFocusedWindow, makeMainWindows, setAppQuitting, setRelaunchHandler } from "../windows" +import { acquireApplicationLock, configureApplication } from "./environment" +import { Shutdown } from "./shutdown" -export function createApplicationLifecycle(logger: DesktopLogger) { - const pendingDeepLinks: string[] = [] - const wsl = { stop: async () => {} } - const emitDeepLinks = (urls: string[]) => { - if (!urls.length) return - pendingDeepLinks.push(...urls) - const win = getLastFocusedWindow() - if (win) sendIpcEvent(win.webContents, Ipc.app.deepLink, urls) - } - const relaunch = () => { - setAppQuitting() - void wsl.stop().finally(() => { - app.relaunch() - app.quit() - }) - } - - app.on("second-instance", (_event: Event, argv: string[]) => { - const urls = argv.filter((arg) => arg.startsWith("opencode://")) - if (urls.length) { - logger.log("deep link received via second-instance", { urls }) - emitDeepLinks(urls) - } - const win = getLastFocusedWindow() - if (!win) return - win.show() - win.focus() - }) - app.on("open-url", (event: Event, url: string) => { - event.preventDefault() - logger.log("deep link received via open-url", { url }) - emitDeepLinks([url]) - }) - app.on("before-quit", () => { - setAppQuitting() - void wsl.stop() - }) - app.on("will-quit", () => { - setAppQuitting() - void wsl.stop() - }) - app.on("child-process-gone", (_event, details) => { - writeLog("utility", "child process gone", { details }, "error") - }) - app.on("render-process-gone", (_event, webContents, details) => { - writeLog("window", "app render process gone", { url: safeWebContentsURL(webContents), details }, "error") - }) - setRelaunchHandler(relaunch) - ;(["SIGINT", "SIGTERM"] as const).forEach((signal) => { - process.on(signal, () => { - setAppQuitting() - void wsl.stop().finally(() => app.quit()) - }) - }) - - return { - relaunch, - prepareToRestart: () => wsl.stop(), - setWslShutdown(stop: () => Promise) { - wsl.stop = stop - }, - consumeInitialDeepLinks: () => pendingDeepLinks.splice(0), - restoreWindows() { - app.on("window-all-closed", () => { - if (process.platform !== "darwin") app.quit() - }) - app.on("activate", () => { - if (BrowserWindow.getAllWindows().length === 0) restoreMainWindows() - }) - return restoreMainWindows() - }, - } +export interface Interface { + readonly relaunch: () => void + readonly prepareToRestart: Effect.Effect + readonly consumeInitialDeepLinks: () => string[] + readonly createWindow: () => BrowserWindow + readonly restoreWindows: () => BrowserWindow[] } + +export class Service extends Context.Service()("opencode/desktop/ApplicationLifecycle") {} + +const runtime = Layer.effect( + Service, + Effect.gen(function* () { + const shutdown = yield* Shutdown.Service + const runFork = Effect.runForkWith(yield* Effect.context()) + const windows = yield* makeMainWindows() + const createWindow = windows.create + const restoreWindows = windows.restore + const pendingDeepLinks: string[] = [] + let shutdownReady = false + const prepareToRestart = shutdown.run.pipe(Effect.ensuring(Effect.sync(() => (shutdownReady = true)))) + const emitDeepLinks = (urls: string[]) => { + if (!urls.length) return + pendingDeepLinks.push(...urls) + const win = getLastFocusedWindow() + if (win) emitIpcEvent(win.webContents, new DeepLinksOpened({ urls })) + } + const relaunch = () => { + setAppQuitting() + runFork( + prepareToRestart.pipe( + Effect.ensuring( + Effect.sync(() => { + app.relaunch() + app.quit() + }), + ), + ), + ) + } + const secondInstance = (_event: Event, argv: string[]) => { + const urls = argv.filter((arg) => arg.startsWith("opencode://")) + if (urls.length) { + runFork(Effect.logInfo("deep link received via second-instance", { urls })) + emitDeepLinks(urls) + } + const win = getLastFocusedWindow() + if (!win) return + win.show() + win.focus() + } + const openUrl = (event: Event, url: string) => { + event.preventDefault() + runFork(Effect.logInfo("deep link received via open-url", { url })) + emitDeepLinks([url]) + } + const beforeQuit = (event: Event) => { + setAppQuitting() + if (shutdownReady) return + event.preventDefault() + runFork(prepareToRestart.pipe(Effect.ensuring(Effect.sync(() => app.quit())))) + } + const willQuit = () => { + setAppQuitting() + runFork(shutdown.run) + } + const childProcessGone = (_event: Event, details: Electron.Details) => { + runFork(scoped("utility", Effect.logError("child process gone", { details }))) + } + const renderProcessGone = ( + _event: Event, + webContents: Electron.WebContents, + details: Electron.RenderProcessGoneDetails, + ) => { + runFork( + scoped("window", Effect.logError("app render process gone", { url: safeWebContentsURL(webContents), details })), + ) + } + const signal = () => { + setAppQuitting() + runFork(prepareToRestart.pipe(Effect.ensuring(Effect.sync(() => app.quit())))) + } + const windowAllClosed = () => { + if (process.platform !== "darwin") app.quit() + } + const activate = () => { + if (BrowserWindow.getAllWindows().length === 0) restoreWindows() + } + const resetRelaunchHandler = setRelaunchHandler(relaunch) + let windowsWired = false + + app.on("second-instance", secondInstance) + app.on("open-url", openUrl) + app.on("before-quit", beforeQuit) + app.on("will-quit", willQuit) + app.on("child-process-gone", childProcessGone) + app.on("render-process-gone", renderProcessGone) + process.on("SIGINT", signal) + process.on("SIGTERM", signal) + yield* Effect.addFinalizer(() => + Effect.sync(() => { + app.off("second-instance", secondInstance) + app.off("open-url", openUrl) + app.off("before-quit", beforeQuit) + app.off("will-quit", willQuit) + app.off("child-process-gone", childProcessGone) + app.off("render-process-gone", renderProcessGone) + app.off("window-all-closed", windowAllClosed) + app.off("activate", activate) + process.off("SIGINT", signal) + process.off("SIGTERM", signal) + resetRelaunchHandler() + }), + ) + + return Service.of({ + relaunch, + prepareToRestart, + consumeInitialDeepLinks: () => pendingDeepLinks.splice(0), + createWindow, + restoreWindows: () => { + if (!windowsWired) { + windowsWired = true + app.on("window-all-closed", windowAllClosed) + app.on("activate", activate) + } + return restoreWindows() + }, + }) + }), +) + +const platform = Layer.merge(DesktopLogging.layer, Shutdown.layer) + +export const layer = Layer.unwrap( + Effect.gen(function* () { + if (!acquireApplicationLock()) return yield* Effect.interrupt + yield* configureApplication() + return runtime.pipe(Layer.provideMerge(platform)) + }), +) diff --git a/packages/desktop/src/main/lifecycle/onboarding.ts b/packages/desktop/src/main/lifecycle/onboarding.ts index 4ba8358391c..39e5437b30c 100644 --- a/packages/desktop/src/main/lifecycle/onboarding.ts +++ b/packages/desktop/src/main/lifecycle/onboarding.ts @@ -1,16 +1,23 @@ -import { existsSync, readdirSync } from "node:fs" -import { mkdir } from "node:fs/promises" -import { join } from "node:path" import { app } from "electron" -import { writeLog } from "../native/logging" +import { Effect, FileSystem, Option, Path } from "effect" +import { scoped } from "../native/logging" import { hasExistingAppState } from "../storage/install-state" import { FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY } from "../storage/keys" import { getStore } from "../storage/store" const DEFAULT_PROJECT_DIR = "Default Project" -export function initializeFirstLaunchOnboarding(userDataPath: string) { - const entries = existsSync(userDataPath) ? readdirSync(userDataPath, { withFileTypes: true }) : [] +export const initializeFirstLaunchOnboarding = Effect.fn("Onboarding.initialize")(function* (userDataPath: string) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const names = (yield* fs.exists(userDataPath)) ? yield* fs.readDirectory(userDataPath) : [] + const entries = yield* Effect.forEach( + names, + Effect.fnUntraced(function* (name) { + const info = yield* fs.stat(path.join(userDataPath, name)).pipe(Effect.option) + return { name, directory: Option.isSome(info) && info.value.type === "Directory" } + }), + ) const store = getStore() const current = store.get(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY) if (typeof current === "boolean") return current @@ -18,24 +25,29 @@ export function initializeFirstLaunchOnboarding(userDataPath: string) { const complete = hasExistingAppState(entries) store.set(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY, complete) return complete -} +}) -export function isFirstLaunchOnboardingPending() { +export const isFirstLaunchOnboardingPending = Effect.fn("Onboarding.isPending")(function* () { const pending = getStore().get(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY) !== true - writeLog("onboarding", "first launch onboarding pending checked", { pending }) + yield* scoped("onboarding", Effect.logInfo("first launch onboarding pending checked", { pending })) return pending -} +}) -export async function finishFirstLaunchOnboarding(createDefaultProject: boolean) { - if (!isFirstLaunchOnboardingPending()) { - writeLog("onboarding", "first launch onboarding already completed") +export const finishFirstLaunchOnboarding = Effect.fn("Onboarding.finish")(function* (createDefaultProject: boolean) { + if (!(yield* isFirstLaunchOnboardingPending())) { + yield* scoped("onboarding", Effect.logInfo("first launch onboarding already completed")) return null } - const defaultProject = createDefaultProject ? join(app.getPath("documents"), DEFAULT_PROJECT_DIR) : null - if (defaultProject) await mkdir(defaultProject, { recursive: true }) + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const defaultProject = createDefaultProject ? path.join(app.getPath("documents"), DEFAULT_PROJECT_DIR) : null + if (defaultProject) yield* fs.makeDirectory(defaultProject, { recursive: true }) getStore().set(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY, true) - writeLog("onboarding", "first launch onboarding completed", { createDefaultProject, defaultProject }) + yield* scoped( + "onboarding", + Effect.logInfo("first launch onboarding completed", { createDefaultProject, defaultProject }), + ) return defaultProject -} +}) diff --git a/packages/desktop/src/main/lifecycle/shutdown.ts b/packages/desktop/src/main/lifecycle/shutdown.ts new file mode 100644 index 00000000000..a70d590a7e0 --- /dev/null +++ b/packages/desktop/src/main/lifecycle/shutdown.ts @@ -0,0 +1,28 @@ +export * as Shutdown from "./shutdown" + +import { Context, Effect, Layer } from "effect" + +export interface Interface { + readonly add: (effect: Effect.Effect) => Effect.Effect<() => void> + readonly run: Effect.Effect +} + +export class Service extends Context.Service()("opencode/desktop/Shutdown") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const effects = new Set>() + const run = yield* Effect.cached( + Effect.suspend(() => Effect.forEach(effects, (effect) => effect, { concurrency: "unbounded", discard: true })), + ) + return Service.of({ + add: (effect) => + Effect.sync(() => { + effects.add(effect) + return () => effects.delete(effect) + }), + run, + }) + }), +) diff --git a/packages/desktop/src/main/native/logging.ts b/packages/desktop/src/main/native/logging.ts index 1f705fde0ce..963da1546bd 100644 --- a/packages/desktop/src/main/native/logging.ts +++ b/packages/desktop/src/main/native/logging.ts @@ -1,8 +1,8 @@ -import { MainLogger } from "electron-log" +export * as DesktopLogging from "./logging" + import log from "electron-log/main.js" import { app, crashReporter, netLog, shell } from "electron" -import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs" -import { dirname, join } from "node:path" +import { Context, Effect, FileSystem, Layer, Logger, Option, Path, References } from "effect" import { homedir } from "node:os" import { VERSION } from "../constants" @@ -16,97 +16,160 @@ let root = "" let run = "" let netLogPath: string | undefined -let logger: MainLogger -export const getLogger = () => logger -export type DesktopLogger = ReturnType - -export function initLogging() { - initRunDirectory() - log.transports.file.maxSize = 5 * 1024 * 1024 - log.transports.file.resolvePathFn = (_vars, message) => - join( - run, - `${safeLogName(message?.scope ?? (message?.variables?.processType === "renderer" ? "renderer" : "main"))}.log`, - ) - log.initialize({ preload: false, spyRendererConsole: true }) - initConsoleTransport() - cleanup() - return (logger = log) +export interface Interface { + readonly startNetwork: Effect.Effect + readonly exportDebug: Effect.Effect } -export function initCrashReporter() { - const dir = join(app.getPath("userData"), "Crashpad") - mkdirSync(dir, { recursive: true }) - app.setPath("crashDumps", dir) - crashReporter.start({ uploadToServer: false, compress: true }) - writeLog("crash", "crash reporter started", { path: dir }) -} +export class Service extends Context.Service()("opencode/desktop/DesktopLogging") {} -async function startNetLog() { - if (netLog.currentlyLogging) return - netLogPath = join(run, "network.netlog") - await netLog.startLogging(netLogPath, { captureMode: "default", maxFileSize: NET_LOG_SIZE }) - writeLog("network", "net log started", { path: netLogPath }) -} +const serviceLayer = Layer.effect( + Service, + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + yield* initLogging(fs, path).pipe(Effect.orDie) + yield* initCrashReporter(fs, path).pipe(Effect.orDie) + yield* Effect.logInfo("app starting", { + version: VERSION, + packaged: app.isPackaged, + onboardingTest: process.env.OPENCODE_TEST_ONBOARDING === "1", + }) + const exportDebug = exportDebugLogsEffect(fs, path).pipe(Effect.orDie) + return Service.of({ + startNetwork: startNetLog(path).pipe( + Effect.catch((error) => Effect.logWarning("failed to start net log", { error })), + ), + exportDebug, + }) + }), +) -export function startNetworkLogging() { - return startNetLog().catch((error) => logger.warn("failed to start net log", error)) -} - -export async function exportDebugLogs() { - const restartNetLog = netLog.currentlyLogging - if (restartNetLog) { - await netLog.stopLogging().catch((error) => writeLog("network", "failed to stop net log", { error })) - } - - const output = join(app.getPath("downloads"), `opencode-debug-${stamp()}.zip`) +const nativeLogger = Logger.make((options) => { try { - writeLog("main", "exporting debug logs", { output }) - await writeZip(output, [ - { name: "manifest.json", data: Buffer.from(JSON.stringify(manifest(), null, 2)) }, - ...collect(root, "desktop"), - ...serverLogRoots().flatMap((dir, i) => collect(dir, `server-${i + 1}`)), - ...collect(app.getPath("crashDumps"), "crashpad"), - ]) - shell.showItemInFolder(output) - return output - } finally { - if (restartNetLog) { - await startNetLog().catch((error) => writeLog("network", "failed to restart net log", { error })) + if (!run) return + const entry = Logger.formatStructured.log(options) + const scope = typeof entry.annotations.scope === "string" ? entry.annotations.scope : "main" + const annotations = Object.fromEntries(Object.entries(entry.annotations).filter(([key]) => key !== "scope")) + const context = { + ...(Object.keys(annotations).length === 0 ? {} : { annotations }), + ...(Object.keys(entry.spans).length === 0 ? {} : { spans: entry.spans }), + ...(entry.cause === undefined ? {} : { cause: entry.cause }), } + const messages = Array.isArray(options.message) ? options.message : [options.message] + log.scope(safeLogName(scope))[methods[options.logLevel]]( + ...messages, + ...(Object.keys(context).length === 0 ? [] : [context]), + ) + } catch { + // Logging must not interrupt application work. } +}) + +const methods = { + All: "silly", + Trace: "silly", + Debug: "debug", + Info: "info", + Warn: "warn", + Error: "error", + Fatal: "error", + None: "silly", +} as const + +const nativeLoggerLayer = Layer.merge( + Logger.layer([nativeLogger], { mergeWithExisting: false }), + Layer.succeed(References.MinimumLogLevel, "All"), +) + +export const layer = serviceLayer.pipe(Layer.provideMerge(nativeLoggerLayer)) + +function initLogging(fs: FileSystem.FileSystem, path: Path.Path) { + return Effect.gen(function* () { + yield* initRunDirectory(fs, path) + yield* Effect.sync(() => { + log.transports.file.maxSize = 5 * 1024 * 1024 + log.transports.file.resolvePathFn = (_vars, message) => + path.join( + run, + `${safeLogName(message?.scope ?? (message?.variables?.processType === "renderer" ? "renderer" : "main"))}.log`, + ) + log.initialize({ preload: false, spyRendererConsole: true }) + initConsoleTransport() + }) + yield* cleanup(fs, path) + }) } -export function writeLog( - name: string, - message: string, - extra?: Record, - level: "info" | "warn" | "error" = "info", -) { - if (!run) return - const scoped = log.scope(safeLogName(name)) - if (extra !== undefined) { - scoped[level](message, extra) - return - } - scoped[level](message) +function initCrashReporter(fs: FileSystem.FileSystem, path: Path.Path) { + return Effect.gen(function* () { + const dir = path.join(app.getPath("userData"), "Crashpad") + yield* fs.makeDirectory(dir, { recursive: true }) + yield* Effect.sync(() => { + app.setPath("crashDumps", dir) + crashReporter.start({ uploadToServer: false, compress: true }) + }) + yield* scoped("crash", Effect.logInfo("crash reporter started", { path: dir })) + }) } -export function tail(): string { - try { +function startNetLog(path: Path.Path) { + if (netLog.currentlyLogging) return Effect.void + const target = path.join(run, "network.netlog") + netLogPath = target + return Effect.tryPromise(() => netLog.startLogging(target, { captureMode: "default", maxFileSize: NET_LOG_SIZE })).pipe( + Effect.tap(() => scoped("network", Effect.logInfo("net log started", { path: target }))), + ) +} + +function exportDebugLogsEffect(fs: FileSystem.FileSystem, path: Path.Path) { + return Effect.gen(function* () { + const restartNetLog = netLog.currentlyLogging + if (restartNetLog) { + yield* Effect.tryPromise(() => netLog.stopLogging()).pipe( + Effect.catch((error) => scoped("network", Effect.logWarning("failed to stop net log", { error }))), + ) + } + + const output = path.join(app.getPath("downloads"), `opencode-debug-${stamp()}.zip`) + return yield* Effect.gen(function* () { + yield* Effect.logInfo("exporting debug logs", { output }) + yield* writeZip(fs, output, [ + { name: "manifest.json", data: Buffer.from(JSON.stringify(manifest(path), null, 2)) }, + ...(yield* collect(fs, path, root, "desktop")), + ...(yield* Effect.forEach(serverLogRoots(path), (dir, i) => collect(fs, path, dir, `server-${i + 1}`))).flat(), + ...(yield* collect(fs, path, app.getPath("crashDumps"), "crashpad")), + ]) + yield* Effect.sync(() => shell.showItemInFolder(output)) + return output + }).pipe( + Effect.ensuring( + restartNetLog + ? startNetLog(path).pipe( + Effect.catch((error) => + scoped("network", Effect.logWarning("failed to restart net log", { error })), + ), + ) + : Effect.void, + ), + ) + }) +} + +export const tail = Effect.fn("DesktopLogging.tail")(function* () { + const fs = yield* FileSystem.FileSystem + return yield* Effect.gen(function* () { const path = log.transports.file.getFile().path - const contents = readFileSync(path, "utf8") + const contents = yield* fs.readFileString(path) const lines = contents.split("\n") return lines.slice(Math.max(0, lines.length - TAIL_LINES)).join("\n") - } catch { - return "" - } -} + }).pipe(Effect.catch(() => Effect.succeed(""))) +}) -function initRunDirectory() { - root = join(app.getPath("userData"), "logs") - run = join(root, stamp()) - mkdirSync(run, { recursive: true }) +function initRunDirectory(fs: FileSystem.FileSystem, path: Path.Path) { + root = path.join(app.getPath("userData"), "logs") + run = path.join(root, stamp()) + return fs.makeDirectory(run, { recursive: true }) } function stamp() { @@ -120,22 +183,27 @@ function safeLogName(name: string) { return name.replace(/[^a-z0-9_.-]/gi, "_") || "main" } -function cleanup() { - const dir = root || dirname(log.transports.file.getFile().path) - const cutoff = Date.now() - MAX_LOG_AGE_DAYS * 24 * 60 * 60 * 1000 - - for (const entry of readdirSync(dir)) { - const file = join(dir, entry) - try { - const info = statSync(file) - if (info.mtimeMs < cutoff) rmSync(file, { recursive: true, force: true }) - } catch { - continue - } - } +function cleanup(fs: FileSystem.FileSystem, path: Path.Path) { + return Effect.gen(function* () { + const dir = root || path.dirname(log.transports.file.getFile().path) + const cutoff = Date.now() - MAX_LOG_AGE_DAYS * 24 * 60 * 60 * 1000 + const entries = yield* fs.readDirectory(dir) + yield* Effect.forEach( + entries, + (entry) => + Effect.gen(function* () { + const file = path.join(dir, entry) + const info = yield* fs.stat(file) + if (Option.getOrElse(info.mtime, () => new Date(0)).getTime() < cutoff) { + yield* fs.remove(file, { recursive: true, force: true }) + } + }).pipe(Effect.catch(() => Effect.void)), + { discard: true }, + ) + }) } -function manifest() { +function manifest(path: Path.Path) { return { generated: new Date().toISOString(), version: VERSION, @@ -149,49 +217,55 @@ function manifest() { logs: root, currentRun: run, crashDumps: app.getPath("crashDumps"), - serverLogs: serverLogRoots(), + serverLogs: serverLogRoots(path), netLog: netLogPath, } } -function serverLogRoots() { - const xdgData = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share") - return [...new Set([join(xdgData, "opencode", "log"), join(app.getPath("userData"), "opencode", "log")])] +function serverLogRoots(path: Path.Path) { + const xdgData = process.env.XDG_DATA_HOME || path.join(homedir(), ".local", "share") + return [ + ...new Set([path.join(xdgData, "opencode", "log"), path.join(app.getPath("userData"), "opencode", "log")]), + ] } -type Entry = { name: string; path?: string; data?: Buffer } +type Entry = { name: string; path: string } | { name: string; data: Uint8Array } -function collect(dir: string, prefix: string): Entry[] { - if (!existsSync(dir)) return [] - const cutoff = Date.now() - EXPORT_WINDOW - const result: Entry[] = [] - const walk = (current: string) => { - for (const entry of readdirSync(current)) { - const file = join(current, entry) - const info = statSync(file) - if (info.isDirectory()) { - walk(file) - continue - } - if (info.mtimeMs < cutoff) continue - if (info.size > MAX_EXPORT_FILE_SIZE) continue - if (file.endsWith(".heapsnapshot")) continue - result.push({ name: join(prefix, file.slice(dir.length + 1)).replace(/\\/g, "/"), path: file }) - } - } - walk(dir) - return result +function collect(fs: FileSystem.FileSystem, path: Path.Path, dir: string, prefix: string) { + return Effect.gen(function* () { + if (!(yield* fs.exists(dir).pipe(Effect.orElseSucceed(() => false)))) return [] + const cutoff = Date.now() - EXPORT_WINDOW + const entries = yield* fs.readDirectory(dir, { recursive: true }) + return (yield* Effect.forEach(entries, (entry) => + Effect.gen(function* () { + const file = path.join(dir, entry) + const info = yield* fs.stat(file) + if (info.type === "Directory") return null + if (Option.getOrElse(info.mtime, () => new Date(0)).getTime() < cutoff) return null + if (info.size > FileSystem.Size(MAX_EXPORT_FILE_SIZE)) return null + if (file.endsWith(".heapsnapshot")) return null + return { name: path.join(prefix, entry).replace(/\\/g, "/"), path: file } + }), + )).filter((entry) => entry !== null) + }) } -async function writeZip(output: string, entries: Entry[]) { - const { BlobReader, BlobWriter, ZipWriter } = await import("@zip.js/zip.js") - const writer = new ZipWriter(new BlobWriter("application/zip")) - for (const entry of entries) { - const data = entry.data ?? readFileSync(entry.path!) - await writer.add(entry.name, new BlobReader(new Blob([new Uint8Array(data)]))) - } - const zip = await writer.close() - writeFileSync(output, Buffer.from(await zip.arrayBuffer())) +function writeZip(fs: FileSystem.FileSystem, output: string, entries: Entry[]) { + return Effect.gen(function* () { + const { BlobReader, BlobWriter, ZipWriter } = yield* Effect.promise(() => import("@zip.js/zip.js")) + const writer = new ZipWriter(new BlobWriter("application/zip")) + yield* Effect.forEach( + entries, + (entry) => + Effect.gen(function* () { + const data = "data" in entry ? entry.data : yield* fs.readFile(entry.path) + yield* Effect.tryPromise(() => writer.add(entry.name, new BlobReader(new Blob([new Uint8Array(data)])))) + }), + { concurrency: 1, discard: true }, + ) + const zip = yield* Effect.tryPromise(() => writer.close()) + yield* fs.writeFile(output, new Uint8Array(yield* Effect.tryPromise(() => zip.arrayBuffer()))) + }) } function initConsoleTransport() { @@ -214,3 +288,7 @@ function initConsoleTransport() { function isBrokenPipe(err: unknown) { return typeof err === "object" && err !== null && "code" in err && err.code === "EPIPE" } + +export function scoped(name: string, effect: Effect.Effect) { + return effect.pipe(Effect.annotateLogs("scope", name)) +} diff --git a/packages/desktop/src/main/native/menu-actions.ts b/packages/desktop/src/main/native/menu-actions.ts index d4eebbd5559..831b9487823 100644 --- a/packages/desktop/src/main/native/menu-actions.ts +++ b/packages/desktop/src/main/native/menu-actions.ts @@ -1,9 +1,10 @@ import { BrowserWindow } from "electron" import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu" -import { createMainWindow, updateTitlebar } from "../windows" +import { updateTitlebar } from "../windows" export type DesktopMenuActionHandlers = Partial<{ checkForUpdates: () => void + createWindow: () => void relaunch: () => void }> @@ -20,7 +21,7 @@ export function runDesktopMenuAction( handlers.relaunch?.() return case "window.new": - createMainWindow() + handlers.createWindow?.() return case "window.close": win?.close() diff --git a/packages/desktop/src/main/native/menu.ts b/packages/desktop/src/main/native/menu.ts index a661a31e349..535eb8c7b09 100644 --- a/packages/desktop/src/main/native/menu.ts +++ b/packages/desktop/src/main/native/menu.ts @@ -6,16 +6,18 @@ import { type DesktopMenuEntry, type DesktopMenuRole, } from "@opencode-ai/app/desktop-menu" -import { Ipc, sendIpcEvent } from "../../shared/ipc-contract" +import { MenuCommandTriggered } from "../../shared/ipc-rpc/events" +import { emitIpcEvent } from "../ipc-events" import { UPDATER_ENABLED } from "../constants" -import { openExternalURL } from "../files" import { runDesktopMenuAction } from "./menu-actions" import { nativeT } from "./translations" type Deps = { trigger: (id: string) => void checkForUpdates: () => void + createWindow: () => void + openExternal: (url: string) => void relaunch: () => void } @@ -36,7 +38,7 @@ export function createMenu(deps: Deps) { } export function sendMenuCommand(win: BrowserWindow, id: string) { - sendIpcEvent(win.webContents, Ipc.menu.command, id) + emitIpcEvent(win.webContents, new MenuCommandTriggered({ id })) } function nativeItem(entry: DesktopMenuEntry, deps: Deps): MenuItemConstructorOptions { @@ -58,12 +60,13 @@ function nativeItem(entry: DesktopMenuEntry, deps: Deps): MenuItemConstructorOpt item.click = () => runDesktopMenuAction(BrowserWindow.getFocusedWindow(), action, { checkForUpdates: deps.checkForUpdates, + createWindow: deps.createWindow, relaunch: deps.relaunch, }) } if (entry.href) { const href = entry.href - item.click = () => openExternalURL(href) + item.click = () => deps.openExternal(href) } return item diff --git a/packages/desktop/src/main/paths.ts b/packages/desktop/src/main/paths.ts index fb82348b883..e0b74193293 100644 --- a/packages/desktop/src/main/paths.ts +++ b/packages/desktop/src/main/paths.ts @@ -1,7 +1,19 @@ -import { dirname, join } from "node:path" -import { fileURLToPath } from "node:url" +export * as DesktopPaths from "./paths" -export const mainBundleRoot = dirname(fileURLToPath(import.meta.url)) -export const developmentResourcesRoot = join(mainBundleRoot, "../../resources") -export const preloadPath = join(mainBundleRoot, "../preload/index.js") -export const rendererRoot = join(mainBundleRoot, "../renderer") +import { Effect, Path } from "effect" + +export interface Resolved { + readonly developmentResourcesRoot: string + readonly preloadPath: string + readonly rendererRoot: string +} + +export const resolve = Effect.gen(function* () { + const path = yield* Path.Path + const root = path.dirname(yield* path.fromFileUrl(new URL(import.meta.url))) + return { + developmentResourcesRoot: path.join(root, "../../resources"), + preloadPath: path.join(root, "../preload/index.js"), + rendererRoot: path.join(root, "../renderer"), + } satisfies Resolved +}).pipe(Effect.orDie) diff --git a/packages/desktop/src/main/service/background-service.ts b/packages/desktop/src/main/service/background-service.ts index c7f36e427bb..a7b41cdfd61 100644 --- a/packages/desktop/src/main/service/background-service.ts +++ b/packages/desktop/src/main/service/background-service.ts @@ -1,147 +1,67 @@ -import { Service } from "@opencode-ai/client/service" -import { execFile } from "node:child_process" -import { existsSync } from "node:fs" -import { chmod, copyFile, mkdir, readdir, rename, rm } from "node:fs/promises" -import { dirname, join } from "node:path" -import { promisify } from "node:util" import { app } from "electron" -import { parseCliVersion } from "./cli-version" -import { developmentResourcesRoot } from "../paths" +import { Context, Effect, Exit, Layer, Path } from "effect" +import type { ServerReadyData } from "../../shared/ipc-contract" +import { cleanStages, DesktopCli } from "./desktop-cli" -const execFileAsync = promisify(execFile) -type Logger = { - log(message: string, meta?: Record): void - error(message: string, meta?: Record): void +export * as BackgroundService from "./background-service" + +export interface Interface { + readonly connection: Effect.Effect } -export async function startBackgroundCli(logger: Logger) { +export class Service extends Context.Service()("opencode/desktop/BackgroundService") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const result = yield* start().pipe(Effect.exit) + return Service.of({ + connection: Exit.isSuccess(result) + ? Effect.succeed(result.value) + : Effect.failCause(result.cause).pipe(Effect.orDie), + }) + }), +) + +const start = Effect.fn("BackgroundService.start")(function* () { + yield* Effect.logInfo("starting v2 background service") + const path = yield* Path.Path + const desktopCli = yield* DesktopCli.Service + const runFork = Effect.runForkWith(yield* Effect.context()) const isolated = !app.isPackaged && process.env.OPENCODE_DESKTOP_ISOLATED_SERVER === "1" - const development = !app.isPackaged && process.env.OPENCODE_DESKTOP_CLI_DEV - const developmentVersion = process.env.OPENCODE_VERSION ?? "local" - const cli = development - ? { - version: developmentVersion, - command: [ - "bun", - "run", - "--cwd", - development, - `--define=OPENCODE_VERSION=${JSON.stringify(developmentVersion)}`, - "src/index.ts", - ], - binary: undefined, - } - : await resolveBundledCli(isolated, logger) + const cli = yield* desktopCli.resolve if (isolated) process.env.XDG_STATE_HOME = app.getPath("userData") - const service = await Service.ensure({ - file: - isolated && process.env.OPENCODE_DESKTOP_SERVER_CHANNEL === "local" - ? join(app.getPath("userData"), "opencode", "service-local.json") - : undefined, - version: cli.version, - command: [...cli.command, "serve", "--service", ...(isolated ? ["--port", "0"] : [])], - onStart: (reason, previousVersion) => logger.log("v2 CLI background service starting", { reason, previousVersion }), - }) + const client = yield* Effect.promise(() => import("@opencode-ai/client/service")) + const service = yield* Effect.tryPromise(() => + client.Service.ensure({ + file: + isolated && process.env.OPENCODE_DESKTOP_SERVER_CHANNEL === "local" + ? path.join(app.getPath("userData"), "opencode", "service-local.json") + : undefined, + version: cli.version, + command: [...cli.command, "serve", "--service", ...(isolated ? ["--port", "0"] : [])], + onStart: (reason, previousVersion) => + runFork(Effect.logInfo("v2 CLI background service starting", { reason, previousVersion })), + }), + ) if (service.auth?.type !== "basic") throw new Error("V2 CLI background service did not provide authentication") const url = new URL(service.url) if (url.hostname === "0.0.0.0") url.hostname = "127.0.0.1" - logger.log("v2 CLI background service ready", { + yield* Effect.logInfo("v2 CLI background service ready", { username: service.auth.username, version: cli.version, ...endpoint(url.origin), }) - if (isolated && cli.binary) await cleanCliStages(cli.binary, logger) + if (isolated && cli.binary) yield* cleanStages(cli.binary).pipe(Effect.orDie) return { url: url.origin, username: service.auth.username, password: service.auth.password, - version: cli.version, - wslBuild: - app.isPackaged || !process.env.OPENCODE_DESKTOP_WSL_CLI_BUILD || !process.env.OPENCODE_DESKTOP_WSL_CLI_OUTPUT - ? undefined - : { - script: process.env.OPENCODE_DESKTOP_WSL_CLI_BUILD, - output: process.env.OPENCODE_DESKTOP_WSL_CLI_OUTPUT, - }, - } -} - -async function resolveBundledCli(isolated: boolean, logger: Logger) { - const bundled = app.isPackaged - ? join(process.resourcesPath, executableName()) - : join(developmentResourcesRoot, isolated ? developmentExecutableName() : executableName()) - logger.log("v2 CLI executable resolved", { bundled, packaged: app.isPackaged }) - const version = parseCliVersion(await run(bundled, ["--version"], logger)) - const binary = app.isPackaged || isolated ? await installCli(bundled, version, logger) : bundled - return { version, binary, command: [binary] } -} - -async function cleanCliStages(binary: string, logger: Logger) { - const current = dirname(binary) - const root = dirname(current) - await Promise.all( - (await readdir(root, { withFileTypes: true })) - .filter((entry) => entry.isDirectory() && join(root, entry.name) !== current) - .map((entry) => - rm(join(root, entry.name), { recursive: true, force: true }).catch((error) => - logger.error("failed to clean staged v2 CLI", { path: join(root, entry.name), error }), - ), - ), - ) -} - -async function installCli(source: string, version: string, logger: Logger) { - const directory = join(app.getPath("userData"), "cli", version.replace(/[^a-zA-Z0-9._-]/g, "-")) - const destination = join(directory, executableName()) - if (existsSync(destination)) { - logger.log("v2 CLI staged executable reused", { path: destination, version }) - return destination - } - - const temp = destination + `.${process.pid}.tmp` - await mkdir(directory, { recursive: true }) - await copyFile(source, temp) - if (process.platform !== "win32") await chmod(temp, 0o755) - await rename(temp, destination).catch(async (error) => { - await rm(temp, { force: true }) - throw error - }) - logger.log("v2 CLI executable staged", { source, path: destination, version }) - return destination -} - -async function run(binary: string, args: string[], logger: Logger) { - logger.log("v2 CLI command started", { binary, args }) - return execFileAsync(binary, args, { windowsHide: true }).then( - (result) => { - const stdout = result.stdout.trim() - const stderr = result.stderr.trim() - logger.log("v2 CLI command completed", { args, stdout, stderr }) - return stdout - }, - (error: unknown) => { - const output = error as { stdout?: string; stderr?: string } - logger.error("v2 CLI command failed", { - args, - error: error instanceof Error ? error.message : String(error), - stdout: output.stdout?.trim() ?? "", - stderr: output.stderr?.trim() ?? "", - }) - throw error - }, - ) -} + } satisfies ServerReadyData +}) function endpoint(url: string | undefined) { if (!url || !URL.canParse(url)) return {} const parsed = new URL(url) return { url, hostname: parsed.hostname, port: parsed.port } } - -function executableName() { - return process.platform === "win32" ? "opencode-cli.exe" : "opencode-cli" -} - -function developmentExecutableName() { - return process.platform === "win32" ? "opencode-cli-dev.exe" : "opencode-cli-dev" -} diff --git a/packages/desktop/src/main/service/desktop-cli.ts b/packages/desktop/src/main/service/desktop-cli.ts new file mode 100644 index 00000000000..1602730b41a --- /dev/null +++ b/packages/desktop/src/main/service/desktop-cli.ts @@ -0,0 +1,143 @@ +export * as DesktopCli from "./desktop-cli" + +import { execFile } from "node:child_process" +import { promisify } from "node:util" +import { app } from "electron" +import { Context, Effect, FileSystem, Layer, Path } from "effect" +import { DesktopPaths } from "../paths" +import { parseCliVersion } from "./cli-version" + +const execFileAsync = promisify(execFile) + +export interface Resolved { + readonly version: string + readonly command: readonly string[] + readonly binary?: string + readonly wslBuild?: { readonly script: string; readonly output: string } +} + +export interface Interface { + readonly resolve: Effect.Effect +} + +export class Service extends Context.Service()("opencode/desktop/DesktopCli") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const resolve = yield* Effect.cached( + make().pipe(Effect.provide(yield* Effect.context()), Effect.orDie), + ) + return Service.of({ resolve }) + }), +) + +const make = Effect.fn("DesktopCli.resolve")(function* () { + const development = !app.isPackaged && process.env.OPENCODE_DESKTOP_CLI_DEV + const version = process.env.OPENCODE_VERSION ?? "local" + const cli = development + ? { + version, + command: [ + "bun", + "run", + "--cwd", + development, + `--define=OPENCODE_VERSION=${JSON.stringify(version)}`, + "src/index.ts", + ], + binary: undefined, + } + : yield* resolveBundledCli(!app.isPackaged && process.env.OPENCODE_DESKTOP_ISOLATED_SERVER === "1") + return { + ...cli, + wslBuild: + app.isPackaged || !process.env.OPENCODE_DESKTOP_WSL_CLI_BUILD || !process.env.OPENCODE_DESKTOP_WSL_CLI_OUTPUT + ? undefined + : { + script: process.env.OPENCODE_DESKTOP_WSL_CLI_BUILD, + output: process.env.OPENCODE_DESKTOP_WSL_CLI_OUTPUT, + }, + } satisfies Resolved +}) + +const resolveBundledCli = Effect.fn("DesktopCli.resolveBundled")(function* (isolated: boolean) { + const path = yield* Path.Path + const paths = yield* DesktopPaths.resolve + const bundled = app.isPackaged + ? path.join(process.resourcesPath, executableName()) + : path.join(paths.developmentResourcesRoot, isolated ? developmentExecutableName() : executableName()) + yield* Effect.logInfo("v2 CLI executable resolved", { bundled, packaged: app.isPackaged }) + const version = parseCliVersion(yield* run(bundled, ["--version"])) + const binary = app.isPackaged || isolated ? yield* installCli(bundled, version) : bundled + return { version, binary, command: [binary] } +}) + +export const cleanStages = Effect.fn("DesktopCli.cleanStages")(function* (binary: string) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const current = path.dirname(binary) + const root = path.dirname(current) + const entries = yield* fs.readDirectory(root) + yield* Effect.forEach( + entries, + Effect.fnUntraced(function* (entry) { + const target = path.join(root, entry) + if (target === current) return + const stat = yield* fs.stat(target).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (stat?.type !== "Directory") return + yield* fs + .remove(target, { recursive: true, force: true }) + .pipe(Effect.catch((error) => Effect.logError("failed to clean staged v2 CLI", { path: target, error }))) + }), + { concurrency: "unbounded" }, + ) +}) + +const installCli = Effect.fn("DesktopCli.install")(function* (source: string, version: string) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const directory = path.join(app.getPath("userData"), "cli", version.replace(/[^a-zA-Z0-9._-]/g, "-")) + const destination = path.join(directory, executableName()) + if (yield* fs.exists(destination)) { + yield* Effect.logInfo("v2 CLI staged executable reused", { path: destination, version }) + return destination + } + + const temp = destination + `.${process.pid}.tmp` + yield* fs.makeDirectory(directory, { recursive: true }) + yield* fs.copyFile(source, temp) + if (process.platform !== "win32") yield* fs.chmod(temp, 0o755) + yield* fs + .rename(temp, destination) + .pipe(Effect.catch((error) => fs.remove(temp, { force: true }).pipe(Effect.andThen(Effect.fail(error))))) + yield* Effect.logInfo("v2 CLI executable staged", { source, path: destination, version }) + return destination +}) + +const run = Effect.fn("DesktopCli.run")(function* (binary: string, args: string[]) { + yield* Effect.logInfo("v2 CLI command started", { binary, args }) + const result = yield* Effect.tryPromise(() => execFileAsync(binary, args, { windowsHide: true })).pipe( + Effect.tapError((error) => { + const output = error as { stdout?: string; stderr?: string } + return Effect.logError("v2 CLI command failed", { + args, + error: error instanceof Error ? error.message : String(error), + stdout: output.stdout?.trim() ?? "", + stderr: output.stderr?.trim() ?? "", + }) + }), + ) + const stdout = result.stdout.trim() + const stderr = result.stderr.trim() + yield* Effect.logInfo("v2 CLI command completed", { args, stdout, stderr }) + return stdout +}) + +function executableName() { + return process.platform === "win32" ? "opencode-cli.exe" : "opencode-cli" +} + +function developmentExecutableName() { + return process.platform === "win32" ? "opencode-cli-dev.exe" : "opencode-cli-dev" +} diff --git a/packages/desktop/src/main/service/initialization.test.ts b/packages/desktop/src/main/service/initialization.test.ts deleted file mode 100644 index 338c7fd26a2..00000000000 --- a/packages/desktop/src/main/service/initialization.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, test } from "bun:test" -import { Cause, Deferred, Effect, Exit, Fiber } from "effect" -import { forwardInitializationFailure } from "./initialization" - -describe("desktop initialization", () => { - const failure = new Error("sidecar startup failed") - const expectFailure = (exit: Exit.Exit) => { - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isSuccess(exit)) return - expect(Cause.squash(exit.cause)).toBe(failure) - } - - test("forwards loading task failures before renderer initialization", () => { - const exit = Effect.runSync( - Effect.gen(function* () { - const initialization = yield* Deferred.make() - yield* forwardInitializationFailure(initialization)(Effect.die(failure)).pipe(Effect.exit) - return yield* Deferred.await(initialization).pipe(Effect.exit) - }), - ) - - expectFailure(exit) - }) - - test("forwards loading task failures while renderer initialization waits", () => { - const exit = Effect.runSync( - Effect.gen(function* () { - const initialization = yield* Deferred.make() - const waiting = yield* Deferred.await(initialization).pipe(Effect.exit, Effect.forkChild) - yield* forwardInitializationFailure(initialization)(Effect.die(failure)).pipe(Effect.exit) - return yield* Fiber.join(waiting) - }), - ) - - expectFailure(exit) - }) -}) diff --git a/packages/desktop/src/main/service/initialization.ts b/packages/desktop/src/main/service/initialization.ts deleted file mode 100644 index 476abac7f59..00000000000 --- a/packages/desktop/src/main/service/initialization.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Deferred, Effect } from "effect" - -export function forwardInitializationFailure(initialization: Deferred.Deferred) { - return (effect: Effect.Effect) => - effect.pipe(Effect.tapCause((cause) => Deferred.failCause(initialization, cause))) -} diff --git a/packages/desktop/src/main/service/shell-env.test.ts b/packages/desktop/src/main/service/shell-env.test.ts index e71708ad049..c3a265c22e8 100644 --- a/packages/desktop/src/main/service/shell-env.test.ts +++ b/packages/desktop/src/main/service/shell-env.test.ts @@ -1,4 +1,6 @@ import { describe, expect, test } from "bun:test" +import { NodePath } from "@effect/platform-node" +import { Effect } from "effect" import { isNushell, mergeShellEnv, parseShellEnv, resolveUserShell } from "./shell-env" @@ -42,9 +44,10 @@ describe("shell env", () => { }) test("isNushell handles path and binary name", () => { - expect(isNushell("nu")).toBe(true) - expect(isNushell("/opt/homebrew/bin/nu")).toBe(true) - expect(isNushell("C:\\Program Files\\nu.exe")).toBe(true) - expect(isNushell("/bin/zsh")).toBe(false) + const check = (shell: string) => Effect.runSync(isNushell(shell).pipe(Effect.provide(NodePath.layer))) + expect(check("nu")).toBe(true) + expect(check("/opt/homebrew/bin/nu")).toBe(true) + expect(check("C:\\Program Files\\nu.exe")).toBe(true) + expect(check("/bin/zsh")).toBe(false) }) }) diff --git a/packages/desktop/src/main/service/shell-env.ts b/packages/desktop/src/main/service/shell-env.ts index 082ed5e930d..ef54cb53889 100644 --- a/packages/desktop/src/main/service/shell-env.ts +++ b/packages/desktop/src/main/service/shell-env.ts @@ -1,14 +1,10 @@ import { spawnSync } from "node:child_process" import { userInfo } from "node:os" -import { basename } from "node:path" +import { Effect, Path } from "effect" const TIMEOUT = 5_000 type Probe = { type: "Loaded"; value: Record } | { type: "Timeout" } | { type: "Unavailable" } -type ShellEnvLogger = { - log: (message: string) => void -} - export function resolveUserShell(envShell: string | undefined, loginShell: string | null | undefined) { const resolvedLoginShell = loginShell && loginShell !== "unknown" ? loginShell : undefined return envShell || resolvedLoginShell || "/bin/sh" @@ -33,7 +29,7 @@ export function parseShellEnv(out: Buffer) { return env } -function probe(shell: string, mode: "-il" | "-l"): Probe { +const probe = Effect.fn("ShellEnv.probe")(function* (shell: string, mode: "-il" | "-l") { const out = spawnSync(shell, [mode, "-c", "env -0"], { stdio: ["ignore", "pipe", "ignore"], timeout: TIMEOUT, @@ -42,56 +38,57 @@ function probe(shell: string, mode: "-il" | "-l"): Probe { const err = out.error as NodeJS.ErrnoException | undefined if (err) { - if (err.code === "ETIMEDOUT") return { type: "Timeout" } - console.log(`[server] Shell env probe failed for ${shell} ${mode}: ${err.message}`) - return { type: "Unavailable" } + if (err.code === "ETIMEDOUT") return { type: "Timeout" } satisfies Probe + yield* Effect.logWarning(`[server] Shell env probe failed for ${shell} ${mode}: ${err.message}`) + return { type: "Unavailable" } satisfies Probe } if (out.status !== 0) { - console.log(`[server] Shell env probe exited with non-zero status for ${shell} ${mode}`) - return { type: "Unavailable" } + yield* Effect.logWarning(`[server] Shell env probe exited with non-zero status for ${shell} ${mode}`) + return { type: "Unavailable" } satisfies Probe } const env = parseShellEnv(out.stdout) if (Object.keys(env).length === 0) { - console.log(`[server] Shell env probe returned empty env for ${shell} ${mode}`) - return { type: "Unavailable" } + yield* Effect.logWarning(`[server] Shell env probe returned empty env for ${shell} ${mode}`) + return { type: "Unavailable" } satisfies Probe } - return { type: "Loaded", value: env } -} + return { type: "Loaded", value: env } satisfies Probe +}) -export function isNushell(shell: string) { - const name = basename(shell).toLowerCase() +export const isNushell = Effect.fn("ShellEnv.isNushell")(function* (shell: string) { + const path = yield* Path.Path + const name = path.basename(shell).toLowerCase() const raw = shell.toLowerCase() return name === "nu" || name === "nu.exe" || raw.endsWith("\\nu.exe") -} +}) -export function loadShellEnv(shell: string, logger: ShellEnvLogger) { - if (isNushell(shell)) { - logger.log(`[server] Skipping shell env probe for nushell: ${shell}`) +export const loadShellEnv = Effect.fn("ShellEnv.load")(function* (shell: string) { + if (yield* isNushell(shell)) { + yield* Effect.logInfo(`[server] Skipping shell env probe for nushell: ${shell}`) return null } - const interactive = probe(shell, "-il") + const interactive = yield* probe(shell, "-il") if (interactive.type === "Loaded") { - logger.log(`[server] Loaded shell environment with -il (${Object.keys(interactive.value).length} vars)`) + yield* Effect.logInfo(`[server] Loaded shell environment with -il (${Object.keys(interactive.value).length} vars)`) return interactive.value } if (interactive.type === "Timeout") { - logger.log(`[server] Interactive shell env probe timed out: ${shell}`) + yield* Effect.logInfo(`[server] Interactive shell env probe timed out: ${shell}`) return null } - const login = probe(shell, "-l") + const login = yield* probe(shell, "-l") if (login.type === "Loaded") { - logger.log(`[server] Loaded shell environment with -l (${Object.keys(login.value).length} vars)`) + yield* Effect.logInfo(`[server] Loaded shell environment with -l (${Object.keys(login.value).length} vars)`) return login.value } - logger.log(`[server] Falling back to app environment: ${shell}`) + yield* Effect.logInfo(`[server] Falling back to app environment: ${shell}`) return null -} +}) export function mergeShellEnv(shell: Record | null, env: Record) { return { diff --git a/packages/desktop/src/main/storage/cleanup.test.ts b/packages/desktop/src/main/storage/cleanup.test.ts index d818057498b..ef19612fb68 100644 --- a/packages/desktop/src/main/storage/cleanup.test.ts +++ b/packages/desktop/src/main/storage/cleanup.test.ts @@ -1,93 +1,140 @@ +import { NodeFileSystem, NodePath } from "@effect/platform-node" import { afterEach, describe, expect, test } from "bun:test" -import { mkdtemp, readdir, rm, utimes, writeFile } from "node:fs/promises" import { tmpdir } from "node:os" -import { join } from "node:path" +import { Effect, FileSystem, Layer, Path } from "effect" import { cleanupStoreFiles, deleteStoreFileIfEmpty } from "./cleanup" const roots: string[] = [] +const platform = Layer.merge(NodeFileSystem.layer, NodePath.layer) +const run = (effect: Effect.Effect) => + Effect.runPromise(effect.pipe(Effect.provide(platform))) -async function tempRoot() { - const root = await mkdtemp(join(tmpdir(), "opencode-store-cleanup-")) +const tempRoot = Effect.fn("StorageTest.tempRoot")(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* fs.makeTempDirectory({ directory: tmpdir(), prefix: "opencode-store-cleanup-" }) roots.push(root) return root -} - -async function writeStore(root: string, name: string, value: string, modified: Date) { - await writeFile(join(root, name), value) - await utimes(join(root, name), modified, modified) -} - -afterEach(async () => { - await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) }) +const writeStore = Effect.fn("StorageTest.writeStore")(function* ( + root: string, + name: string, + value: string, + modified: Date, +) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + yield* fs.writeFileString(path.join(root, name), value) + yield* fs.utimes(path.join(root, name), modified, modified) +}) + +afterEach(() => + run( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + yield* Effect.forEach(roots.splice(0), (root) => fs.remove(root, { recursive: true, force: true }), { + concurrency: "unbounded", + discard: true, + }) + }), + ), +) + describe("store cleanup", () => { - test("removes empty scoped stores and leaves global stores alone", async () => { - const root = await tempRoot() - const now = new Date("2026-07-01T00:00:00.000Z") - await writeStore(root, "opencode.draft.empty.dat", "{}", now) - await writeStore(root, "opencode.workspace.empty.dat", "{\n}", now) - await writeStore(root, "opencode.global.dat", "{}", now) - await writeStore(root, "opencode.workspace.empty.dat.json", "{}", now) + test("removes empty scoped stores and leaves global stores alone", () => + run( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* tempRoot() + const now = new Date("2026-07-01T00:00:00.000Z") + yield* writeStore(root, "opencode.draft.empty.dat", "{}", now) + yield* writeStore(root, "opencode.workspace.empty.dat", "{\n}", now) + yield* writeStore(root, "opencode.global.dat", "{}", now) + yield* writeStore(root, "opencode.workspace.empty.dat.json", "{}", now) - const result = await cleanupStoreFiles(root, now.getTime()) + const result = yield* cleanupStoreFiles(root, now.getTime()) - expect(result.deleted.sort()).toEqual(["opencode.draft.empty.dat", "opencode.workspace.empty.dat"]) - expect((await readdir(root)).sort()).toEqual(["opencode.global.dat", "opencode.workspace.empty.dat.json"]) - }) + expect(result.deleted.sort()).toEqual(["opencode.draft.empty.dat", "opencode.workspace.empty.dat"]) + expect((yield* fs.readDirectory(root)).sort()).toEqual([ + "opencode.global.dat", + "opencode.workspace.empty.dat.json", + ]) + }), + ), + ) - test("removes stale drafts by age without removing non-empty workspace stores", async () => { - const root = await tempRoot() - const now = new Date("2026-07-01T00:00:00.000Z") - await writeStore(root, "opencode.draft.old.dat", '{"draft:prompt":"hello"}', new Date("2026-05-01T00:00:00.000Z")) - await writeStore(root, "opencode.draft.recent.dat", '{"draft:prompt":"hello"}', now) - await writeStore( - root, - "opencode.workspace.old.dat", - '{"workspace:layout":"wide"}', - new Date("2025-01-01T00:00:00.000Z"), - ) - await writeStore(root, "opencode.workspace.recent.dat", '{"workspace:layout":"wide"}', now) - - const result = await cleanupStoreFiles(root, now.getTime()) - - expect(result.deleted).toEqual(["opencode.draft.old.dat"]) - expect((await readdir(root)).sort()).toEqual([ - "opencode.draft.recent.dat", - "opencode.workspace.old.dat", - "opencode.workspace.recent.dat", - ]) - }) - - test("caps scoped stores by recency", async () => { - const root = await tempRoot() - const now = new Date("2026-07-01T00:00:00.000Z") - await Promise.all( - Array.from({ length: 102 }, (_, index) => - writeStore( + test("removes stale drafts by age without removing non-empty workspace stores", () => + run( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* tempRoot() + const now = new Date("2026-07-01T00:00:00.000Z") + yield* writeStore( root, - `opencode.draft.${index}.dat`, + "opencode.draft.old.dat", '{"draft:prompt":"hello"}', - new Date(now.getTime() - index * 1000), - ), - ), - ) + new Date("2026-05-01T00:00:00.000Z"), + ) + yield* writeStore(root, "opencode.draft.recent.dat", '{"draft:prompt":"hello"}', now) + yield* writeStore( + root, + "opencode.workspace.old.dat", + '{"workspace:layout":"wide"}', + new Date("2025-01-01T00:00:00.000Z"), + ) + yield* writeStore(root, "opencode.workspace.recent.dat", '{"workspace:layout":"wide"}', now) - const result = await cleanupStoreFiles(root, now.getTime()) + const result = yield* cleanupStoreFiles(root, now.getTime()) - const remaining = await readdir(root) + expect(result.deleted).toEqual(["opencode.draft.old.dat"]) + expect((yield* fs.readDirectory(root)).sort()).toEqual([ + "opencode.draft.recent.dat", + "opencode.workspace.old.dat", + "opencode.workspace.recent.dat", + ]) + }), + ), + ) - expect(result.deleted.sort()).toEqual(["opencode.draft.100.dat", "opencode.draft.101.dat"]) - expect(remaining).toHaveLength(100) - }) + test("caps scoped stores by recency", () => + run( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* tempRoot() + const now = new Date("2026-07-01T00:00:00.000Z") + yield* Effect.forEach( + Array.from({ length: 102 }, (_, index) => index), + (index) => + writeStore( + root, + `opencode.draft.${index}.dat`, + '{"draft:prompt":"hello"}', + new Date(now.getTime() - index * 1000), + ), + { concurrency: "unbounded", discard: true }, + ) - test("removes a scoped store immediately when it becomes empty", async () => { - const root = await tempRoot() - await writeStore(root, "opencode.draft.empty.dat", "{}", new Date("2026-07-01T00:00:00.000Z")) - await writeStore(root, "opencode.global.dat", "{}", new Date("2026-07-01T00:00:00.000Z")) + const result = yield* cleanupStoreFiles(root, now.getTime()) + const remaining = yield* fs.readDirectory(root) - expect(await deleteStoreFileIfEmpty(root, "opencode.draft.empty.dat")).toBe(true) - expect(await deleteStoreFileIfEmpty(root, "opencode.global.dat")).toBe(false) - expect(await readdir(root)).toEqual(["opencode.global.dat"]) - }) + expect(result.deleted.sort()).toEqual(["opencode.draft.100.dat", "opencode.draft.101.dat"]) + expect(remaining).toHaveLength(100) + }), + ), + ) + + test("removes a scoped store immediately when it becomes empty", () => + run( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* tempRoot() + yield* writeStore(root, "opencode.draft.empty.dat", "{}", new Date("2026-07-01T00:00:00.000Z")) + yield* writeStore(root, "opencode.global.dat", "{}", new Date("2026-07-01T00:00:00.000Z")) + + expect(yield* deleteStoreFileIfEmpty(root, "opencode.draft.empty.dat")).toBe(true) + expect(yield* deleteStoreFileIfEmpty(root, "opencode.global.dat")).toBe(false) + expect(yield* fs.readDirectory(root)).toEqual(["opencode.global.dat"]) + }), + ), + ) }) diff --git a/packages/desktop/src/main/storage/cleanup.ts b/packages/desktop/src/main/storage/cleanup.ts index 4d592ebc4b6..64fa091cfa4 100644 --- a/packages/desktop/src/main/storage/cleanup.ts +++ b/packages/desktop/src/main/storage/cleanup.ts @@ -1,5 +1,4 @@ -import { readdir, readFile, rm, stat } from "node:fs/promises" -import { join } from "node:path" +import { Effect, FileSystem, Option, Path } from "effect" const EMPTY_STORE_MAX_BYTES = 128 const DRAFT_RETENTION_MS = 30 * 24 * 60 * 60 * 1000 @@ -14,30 +13,33 @@ type StoreCandidate = { empty: boolean } -export async function cleanupStoreFiles(userDataPath: string, now = Date.now()) { - const entries = await readdir(userDataPath, { withFileTypes: true }).catch(() => []) - const candidates = ( - await Promise.all( - entries - .filter((entry) => entry.isFile()) - .map(async (entry) => { - const kind = storeKind(entry.name) - if (!kind) return +export const cleanupStoreFiles = Effect.fn("Storage.cleanupStoreFiles")(function* ( + userDataPath: string, + now = Date.now(), +) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const entries = yield* fs.readDirectory(userDataPath).pipe(Effect.catch(() => Effect.succeed([]))) + const candidates = (yield* Effect.forEach( + entries, + Effect.fnUntraced(function* (entry) { + const kind = storeKind(entry) + if (!kind) return - const file = join(userDataPath, entry.name) - const stats = await stat(file).catch(() => undefined) - if (!stats?.isFile()) return + const file = path.join(userDataPath, entry) + const stats = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (stats?.type !== "File") return - return { - name: entry.name, - path: file, - kind, - modified: stats.mtimeMs, - empty: await isEmptyStore(file, stats.size), - } - }), - ) - ).filter((candidate) => !!candidate) + return { + name: entry, + path: file, + kind, + modified: Option.getOrElse(stats.mtime, () => new Date(0)).getTime(), + empty: yield* isEmptyStore(file, stats.size), + } + }), + { concurrency: 5 }, + )).filter((candidate) => !!candidate) const stale = new Set() for (const candidate of candidates) { @@ -51,37 +53,45 @@ export async function cleanupStoreFiles(userDataPath: string, now = Date.now()) .slice(DRAFT_KEEP_RECENT) .forEach((candidate) => stale.add(candidate)) - const deleted = await Promise.all( - [...stale].map(async (candidate) => { - await rm(candidate.path, { force: true }) + const deleted = yield* Effect.forEach( + stale, + Effect.fnUntraced(function* (candidate) { + yield* fs.remove(candidate.path, { force: true }) return candidate.name }), + { concurrency: "unbounded" }, ) return { scanned: candidates.length, deleted } -} +}) -export async function deleteStoreFileIfEmpty(userDataPath: string, name: string) { +export const deleteStoreFileIfEmpty = Effect.fn("Storage.deleteStoreFileIfEmpty")(function* ( + userDataPath: string, + name: string, +) { if (!storeKind(name)) return false - const file = join(userDataPath, name) - const stats = await stat(file).catch(() => undefined) - if (!stats?.isFile()) return false - if (!(await isEmptyStore(file, stats.size))) return false + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const file = path.join(userDataPath, name) + const stats = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (stats?.type !== "File") return false + if (!(yield* isEmptyStore(file, stats.size))) return false - await rm(file, { force: true }) + yield* fs.remove(file, { force: true }) return true -} +}) function storeKind(name: string): StoreKind | undefined { if (/^opencode\.draft\..+\.dat$/.test(name)) return "draft" if (/^opencode\.workspace\..+\.dat$/.test(name)) return "workspace" } -async function isEmptyStore(file: string, size: number) { - if (size > EMPTY_STORE_MAX_BYTES) return false +const isEmptyStore = Effect.fn("Storage.isEmptyStore")(function* (file: string, size: FileSystem.Size) { + if (size > FileSystem.Size(EMPTY_STORE_MAX_BYTES)) return false - const raw = await readFile(file, "utf8").catch(() => undefined) + const fs = yield* FileSystem.FileSystem + const raw = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(undefined))) if (raw === undefined) return false if (raw.trim() === "") return true @@ -91,4 +101,4 @@ async function isEmptyStore(file: string, size: number) { } catch { return false } -} +}) diff --git a/packages/desktop/src/main/storage/index.ts b/packages/desktop/src/main/storage/index.ts index 458611feef5..8f86b5ff03b 100644 --- a/packages/desktop/src/main/storage/index.ts +++ b/packages/desktop/src/main/storage/index.ts @@ -1,13 +1,46 @@ -import { join } from "node:path" -import { app } from "electron" +export * as DesktopStorage from "./index" + +import { app, BrowserWindow } from "electron" +import { Context, Effect, Layer, Path } from "effect" import { createDesktopDraftStore } from "./drafts" import { getStore, removeStoreFileIfEmpty } from "./store" -export function createDesktopStorage() { - const drafts = createDesktopDraftStore(join(app.getPath("userData"), "drafts.sqlite")) - app.on("before-quit", () => drafts.flush()) - app.once("will-quit", () => drafts.close()) - app.on("browser-window-created", (_event, win) => win.on("session-end", () => drafts.flush())) +export type Interface = ReturnType + +export class Service extends Context.Service()("opencode/desktop/DesktopStorage") {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const path = yield* Path.Path + const storage = make(path.join(app.getPath("userData"), "drafts.sqlite")) + const flush = () => storage.drafts.flush() + const wire = (_event: Electron.Event, win: BrowserWindow) => win.on("session-end", flush) + app.on("before-quit", flush) + app.on("browser-window-created", wire) + BrowserWindow.getAllWindows().forEach((win) => wire({} as Electron.Event, win)) + yield* Effect.addFinalizer(() => + Effect.sync(() => { + app.off("before-quit", flush) + app.off("browser-window-created", wire) + BrowserWindow.getAllWindows().forEach((win) => win.off("session-end", flush)) + storage.drafts.close() + }), + ) + return Service.of(storage) + }), +) + +function make(draftFile: string) { + const drafts = createDesktopDraftStore(draftFile) + const deleteValue = Effect.fn("DesktopStorage.delete")(function* (name: string, key: string) { + getStore(name).delete(key) + yield* removeStoreFileIfEmpty(name).pipe(Effect.ignore) + }) + const clear = Effect.fn("DesktopStorage.clear")(function* (name: string) { + getStore(name).clear() + yield* removeStoreFileIfEmpty(name).pipe(Effect.ignore) + }) return { get(name: string, key: string) { @@ -20,14 +53,8 @@ export function createDesktopStorage() { } }, set: (name: string, key: string, value: string) => getStore(name).set(key, value), - deleteValue(name: string, key: string) { - getStore(name).delete(key) - void removeStoreFileIfEmpty(name) - }, - clear(name: string) { - getStore(name).clear() - void removeStoreFileIfEmpty(name) - }, + deleteValue, + clear, keys: (name: string) => Object.keys(getStore(name).store), length: (name: string) => Object.keys(getStore(name).store).length, drafts: { @@ -38,6 +65,8 @@ export function createDesktopStorage() { const data = drafts.getBlob(id) return data ? new Uint8Array(data).buffer : null }, + flush: drafts.flush, + close: drafts.close, }, } } diff --git a/packages/desktop/src/main/storage/install-state.test.ts b/packages/desktop/src/main/storage/install-state.test.ts index e85d11815e5..3400446d2bb 100644 --- a/packages/desktop/src/main/storage/install-state.test.ts +++ b/packages/desktop/src/main/storage/install-state.test.ts @@ -1,8 +1,8 @@ import { describe, expect, test } from "bun:test" import { hasExistingAppState } from "./install-state" -const file = (name: string) => ({ name, isDirectory: () => false }) -const directory = (name: string) => ({ name, isDirectory: () => true }) +const file = (name: string) => ({ name, directory: false }) +const directory = (name: string) => ({ name, directory: true }) describe("hasExistingAppState", () => { test("ignores files Electron may create on a fresh install", () => { diff --git a/packages/desktop/src/main/storage/install-state.ts b/packages/desktop/src/main/storage/install-state.ts index 32f1df3640c..4e46582db56 100644 --- a/packages/desktop/src/main/storage/install-state.ts +++ b/packages/desktop/src/main/storage/install-state.ts @@ -1,8 +1,8 @@ -export function hasExistingAppState(entries: Array<{ name: string; isDirectory: () => boolean }>) { +export function hasExistingAppState(entries: Array<{ name: string; directory: boolean }>) { return entries.some((entry) => { if (entry.name === "opencode.settings") return true if (entry.name.endsWith(".dat")) return true if (/^window-state-.+\.json$/.test(entry.name)) return true - return entry.isDirectory() && entry.name === "opencode" + return entry.directory && entry.name === "opencode" }) } diff --git a/packages/desktop/src/main/storage/store.ts b/packages/desktop/src/main/storage/store.ts index 4216dec341c..e853b34235e 100644 --- a/packages/desktop/src/main/storage/store.ts +++ b/packages/desktop/src/main/storage/store.ts @@ -1,7 +1,6 @@ import Store from "electron-store" import electron from "electron" -import { rmSync } from "node:fs" -import { join } from "node:path" +import { Effect } from "effect" import { deleteStoreFileIfEmpty } from "./cleanup" import { SETTINGS_STORE } from "./keys" @@ -25,11 +24,10 @@ export function getStore(name = SETTINGS_STORE) { return next } -export async function removeStoreFileIfEmpty(name: string) { - if (await deleteStoreFileIfEmpty(electron.app.getPath("userData"), name)) cache.delete(name) -} +export const removeStoreFileIfEmpty = Effect.fn("DesktopStorage.removeStoreFileIfEmpty")(function* (name: string) { + if (yield* deleteStoreFileIfEmpty(electron.app.getPath("userData"), name)) cache.delete(name) +}) -export function removeStoreFile(name: string) { - rmSync(join(electron.app.getPath("userData"), name), { force: true }) +export function forgetStore(name: string) { cache.delete(name) } diff --git a/packages/desktop/src/main/updater/controller.ts b/packages/desktop/src/main/updater/controller.ts deleted file mode 100644 index b088fa0890b..00000000000 --- a/packages/desktop/src/main/updater/controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { UpdaterState } from "@opencode-ai/app/updater" - -export type { UpdaterState } from "@opencode-ai/app/updater" - -export type UpdaterReadyRecord = { version: string } - -export type UpdaterPlatform = { - checkForUpdate(): Promise - stageUpdate(): Promise - installAndRestart(): Promise -} - -export type UpdaterLifecycle = { - prepareToRestart(): Promise -} - -type UpdaterPersistence = { - get(): UpdaterReadyRecord | undefined | Promise - set(value: UpdaterReadyRecord): void | Promise - clear(): void | Promise -} - -export function createUpdaterController(input: { - currentVersion: string - platform?: UpdaterPlatform - lifecycle: UpdaterLifecycle - persistence: UpdaterPersistence - log?: (message: string, data?: object) => void -}) { - let state: UpdaterState = input.platform ? { status: "idle" } : { status: "disabled" } - let pending: Promise | undefined - let installing: Promise | undefined - const listeners = new Set<(state: UpdaterState) => void>() - - const transition = (next: UpdaterState) => { - input.log?.("updater state changed", { from: state.status, to: next.status }) - state = next - listeners.forEach((listener) => listener(state)) - return state - } - - const check = () => { - const platform = input.platform - if (!platform) return Promise.resolve(state) - if (state.status === "installing") return Promise.resolve(state) - if (pending) return pending - - pending = (state.status === "ready" ? refreshStaged(platform, state.version) : findAndStage(platform)).finally( - () => { - pending = undefined - }, - ) - return pending - } - - const findAndStage = (platform: UpdaterPlatform) => - (async () => { - transition({ status: "checking" }) - const version = await platform.checkForUpdate() - if (!version || version === input.currentVersion) { - await input.persistence.clear() - return transition({ status: "up-to-date" }) - } - - transition({ status: "downloading", version }) - await platform.stageUpdate() - await input.persistence.set({ version }) - return transition({ status: "ready", version }) - })().catch((error) => - transition({ status: "error", message: error instanceof Error ? error.message : String(error) }), - ) - - // A staged update stays visible and installable throughout: the refresh makes no - // transitions until a newer version is staged, and a failure keeps the current one. - const refreshStaged = (platform: UpdaterPlatform, staged: string) => - (async () => { - const version = await platform.checkForUpdate() - if (!version || version === staged || version === input.currentVersion) return state - - await platform.stageUpdate() - await input.persistence.set({ version }) - // An install may have started while this stage was in flight; keep its status - // and show the newer version instead of flickering back to ready. - return transition({ status: installing ? "installing" : "ready", version }) - })().catch((error) => { - input.log?.("updater refresh failed, keeping staged update", { - staged, - message: error instanceof Error ? error.message : String(error), - }) - return state - }) - - const install = () => { - if (installing) return installing - const platform = input.platform - if (!platform || state.status !== "ready") return Promise.reject(new Error("Update is not ready to install")) - - const staged = state.version - transition({ status: "installing", version: staged }) - installing = (async () => { - // Installation is the commit point: refresh once more so one restart lands - // on the newest release, or keep the known-good staged update if checking fails. - await (pending ?? refreshStaged(platform, staged)) - await input.lifecycle.prepareToRestart() - await platform.installAndRestart() - })().catch((error) => { - installing = undefined - if (state.status === "installing") transition({ status: "ready", version: state.version }) - throw error - }) - return installing - } - - return { - getState: () => state, - subscribe(listener: (state: UpdaterState) => void) { - listeners.add(listener) - listener(state) - return () => listeners.delete(listener) - }, - async start() { - const ready = await input.persistence.get() - if (ready?.version === input.currentVersion) await input.persistence.clear() - return check() - }, - check, - install, - } -} - -export type UpdaterController = ReturnType diff --git a/packages/desktop/src/main/updater/controller.test.ts b/packages/desktop/src/main/updater/index.test.ts similarity index 52% rename from packages/desktop/src/main/updater/controller.test.ts rename to packages/desktop/src/main/updater/index.test.ts index fb5373180eb..d96060ad935 100644 --- a/packages/desktop/src/main/updater/controller.test.ts +++ b/packages/desktop/src/main/updater/index.test.ts @@ -1,71 +1,91 @@ -import { describe, expect, test } from "bun:test" -import { createUpdaterController, type UpdaterReadyRecord } from "./controller" +import { afterEach, describe, expect, test } from "bun:test" +import { Effect, ManagedRuntime } from "effect" +import { type Dependencies, layerWith, Service } from "./index" -// Drives the controller the way the app does: start or check, observe the states -// the renderer sees, then install like a button click. `calls` records the platform +const dispose: Array<() => Promise> = [] + +afterEach(async () => { + await Promise.all(dispose.splice(0).map((run) => run())) +}) + +// Drives the updater the way the app does: start or check, then install like a button click. `calls` records the platform // operations in order; installs record the staged version they would apply. function setup(input?: { currentVersion?: string - ready?: UpdaterReadyRecord + ready?: { version: string } latest?: () => string stage?: () => Promise install?: () => Promise }) { const calls: string[] = [] - const states: string[] = [] let ready = input?.ready - const controller = createUpdaterController({ + const dependencies: Dependencies = { currentVersion: input?.currentVersion ?? "1.0.0", platform: { - async checkForUpdate() { - calls.push("check") - return input?.latest?.() ?? "2.0.0" - }, - async stageUpdate() { + checkForUpdate: Effect.try({ + try: () => { + calls.push("check") + return input?.latest?.() ?? "2.0.0" + }, + catch: (error) => error, + }), + stageUpdate: Effect.tryPromise(async () => { calls.push("download") await input?.stage?.() - }, - installAndRestart() { + }), + installAndRestart: Effect.suspend(() => { calls.push(`install:${ready?.version}`) - return input?.install?.() ?? new Promise(() => {}) - }, - }, - lifecycle: { - async prepareToRestart() { - calls.push("prepare") - }, + return Effect.tryPromise({ + try: () => input?.install?.() ?? new Promise(() => {}), + catch: (error) => error, + }) + }), + dispose: () => {}, }, + prepareToRestart: Effect.sync(() => { + calls.push("prepare") + }), persistence: { - get: () => ready, - set: (value) => { - ready = value - }, - clear: () => { + get: Effect.sync(() => ready), + set: (value) => + Effect.sync(() => { + ready = value + }), + clear: Effect.sync(() => { ready = undefined - }, + }), }, - }) - controller.subscribe((state) => states.push(state.status)) - return { controller, calls, states, getReady: () => ready } + } + const runtime = ManagedRuntime.make(layerWith(dependencies)) + dispose.push(() => runtime.dispose()) + const run = (effect: (updater: Service) => Effect.Effect) => + runtime.runPromise(Service.pipe(Effect.flatMap(effect))) + const updater = { + start: () => run((updater) => updater.started), + check: () => run((updater) => updater.check), + install: () => run((updater) => updater.install), + installFork: () => runtime.runFork(Service.pipe(Effect.flatMap((updater) => updater.install))), + getState: () => run((updater) => updater.state), + } + return { updater, calls, getReady: () => ready } } -describe("updater controller", () => { +describe("updater", () => { test("stages an update found at launch and shows it as ready", async () => { const app = setup() - await app.controller.start() + await app.updater.start() - expect(app.states).toEqual(["idle", "checking", "downloading", "ready"]) - expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "ready", version: "2.0.0" }) expect(app.getReady()).toEqual({ version: "2.0.0" }) }) test("reports up to date and clears the record once the update is installed", async () => { const app = setup({ currentVersion: "2.0.0", ready: { version: "2.0.0" } }) - await app.controller.start() + await app.updater.start() - expect(app.states).toEqual(["idle", "checking", "up-to-date"]) + expect(await app.updater.getState()).toEqual({ status: "up-to-date" }) expect(app.calls).toEqual(["check"]) expect(app.getReady()).toBeUndefined() }) @@ -73,38 +93,38 @@ describe("updater controller", () => { test("revalidates a persisted target through the updater cache on launch", async () => { const app = setup({ ready: { version: "2.0.0" } }) - await app.controller.start() + await app.updater.start() expect(app.calls).toEqual(["check", "download"]) - expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "ready", version: "2.0.0" }) }) test("concurrent checks share one platform check", async () => { const app = setup() - await Promise.all([app.controller.check(), app.controller.check(), app.controller.check()]) + await Promise.all([app.updater.check(), app.updater.check(), app.updater.check()]) expect(app.calls).toEqual(["check", "download"]) }) test("clicking install twice checks once and installs the staged version once", async () => { const app = setup() - await app.controller.start() + await app.updater.start() - void app.controller.install() - void app.controller.install() + app.updater.installFork() + app.updater.installFork() await new Promise((resolve) => setTimeout(resolve, 0)) expect(app.calls).toEqual(["check", "download", "check", "prepare", "install:2.0.0"]) - expect(app.controller.getState()).toEqual({ status: "installing", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "2.0.0" }) }) test("ignores checks while an installation is in progress", async () => { const app = setup() - await app.controller.start() - void app.controller.install() + await app.updater.start() + app.updater.installFork() - await app.controller.check() + await app.updater.check() await new Promise((resolve) => setTimeout(resolve, 0)) expect(app.calls).toEqual(["check", "download", "check", "prepare", "install:2.0.0"]) @@ -113,15 +133,15 @@ describe("updater controller", () => { test("clicking install downloads and installs a newer release", async () => { let latest = "2.0.0" const app = setup({ latest: () => latest }) - await app.controller.start() + await app.updater.start() latest = "3.0.0" - void app.controller.install() + app.updater.installFork() - expect(app.controller.getState()).toEqual({ status: "installing", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "2.0.0" }) await new Promise((resolve) => setTimeout(resolve, 0)) expect(app.calls).toEqual(["check", "download", "check", "download", "prepare", "install:3.0.0"]) - expect(app.controller.getState()).toEqual({ status: "installing", version: "3.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "3.0.0" }) }) test("clicking install uses the staged release when the final check fails", async () => { @@ -132,29 +152,27 @@ describe("updater controller", () => { return "2.0.0" }, }) - await app.controller.start() + await app.updater.start() offline = true - void app.controller.install() + app.updater.installFork() await new Promise((resolve) => setTimeout(resolve, 0)) expect(app.calls).toEqual(["check", "download", "check", "prepare", "install:2.0.0"]) - expect(app.controller.getState()).toEqual({ status: "installing", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "2.0.0" }) }) test("later checks stay silent while ready and pick up newer versions", async () => { let latest = "2.0.0" const app = setup({ latest: () => latest }) - await app.controller.start() + await app.updater.start() - await app.controller.check() + await app.updater.check() // Nothing new was published: the install button never hid. - expect(app.states).toEqual(["idle", "checking", "downloading", "ready"]) latest = "3.0.0" - await app.controller.check() - expect(app.states).toEqual(["idle", "checking", "downloading", "ready", "ready"]) - expect(app.controller.getState()).toEqual({ status: "ready", version: "3.0.0" }) + await app.updater.check() + expect(await app.updater.getState()).toEqual({ status: "ready", version: "3.0.0" }) expect(app.getReady()).toEqual({ version: "3.0.0" }) }) @@ -166,13 +184,12 @@ describe("updater controller", () => { return "2.0.0" }, }) - await app.controller.start() + await app.updater.start() offline = true - await app.controller.check() + await app.updater.check() - expect(app.states).toEqual(["idle", "checking", "downloading", "ready"]) - expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "ready", version: "2.0.0" }) expect(app.getReady()).toEqual({ version: "2.0.0" }) }) @@ -189,19 +206,19 @@ describe("updater controller", () => { }) }, }) - await app.controller.start() + await app.updater.start() latest = "3.0.0" slowStage = true - const refresh = app.controller.check() + const refresh = app.updater.check() await new Promise((resolve) => setTimeout(resolve, 0)) - void app.controller.install() - expect(app.controller.getState()).toEqual({ status: "installing", version: "2.0.0" }) + app.updater.installFork() + expect(await app.updater.getState()).toEqual({ status: "installing", version: "2.0.0" }) releaseStage() await refresh - expect(app.controller.getState()).toEqual({ status: "installing", version: "3.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "3.0.0" }) await new Promise((resolve) => setTimeout(resolve, 0)) expect(app.calls).toEqual(["check", "download", "check", "download", "prepare", "install:3.0.0"]) }) @@ -215,14 +232,14 @@ describe("updater controller", () => { return new Promise(() => {}) }, }) - await app.controller.start() + await app.updater.start() - await expect(app.controller.install()).rejects.toThrow("install failed") - expect(app.controller.getState()).toEqual({ status: "ready", version: "2.0.0" }) + await expect(app.updater.install()).rejects.toThrow("install failed") + expect(await app.updater.getState()).toEqual({ status: "ready", version: "2.0.0" }) - void app.controller.install() + app.updater.installFork() await new Promise((resolve) => setTimeout(resolve, 0)) expect(attempts).toBe(2) - expect(app.controller.getState()).toEqual({ status: "installing", version: "2.0.0" }) + expect(await app.updater.getState()).toEqual({ status: "installing", version: "2.0.0" }) }) }) diff --git a/packages/desktop/src/main/updater/index.ts b/packages/desktop/src/main/updater/index.ts index bb2f93c4c45..516c3835dc3 100644 --- a/packages/desktop/src/main/updater/index.ts +++ b/packages/desktop/src/main/updater/index.ts @@ -1,99 +1,185 @@ -import { app, dialog } from "electron" +export * as Updater from "./index" + import type { WebContents } from "electron" -import { Ipc, sendIpcEvent } from "../../shared/ipc-contract" -import { UPDATER_ENABLED } from "../constants" -import { getLogger } from "../native/logging" -import { nativeT } from "../native/translations" -import { getStore } from "../storage/store" -import { createUpdaterController, type UpdaterController, type UpdaterReadyRecord } from "./controller" +import { Context, Deferred, Effect, Exit, Fiber, Layer } from "effect" +import type { UpdaterState } from "@opencode-ai/app/updater" +import { UpdaterStateChanged } from "../../shared/ipc-rpc/events" +import { emitIpcEvent } from "../ipc-events" -const key = "ready" - -export async function setupAutoUpdater(prepareToRestart: () => Promise) { - const logger = getLogger() - const store = getStore("opencode.updater") - const platform = UPDATER_ENABLED ? (await import("./platform")).createUpdaterPlatform(logger) : undefined - return createUpdaterController({ - currentVersion: app.getVersion(), - platform, - lifecycle: { prepareToRestart }, - persistence: { - get() { - const value = store.get(key) - if (!value || typeof value !== "object" || !("version" in value) || typeof value.version !== "string") - return undefined - return { version: value.version } satisfies UpdaterReadyRecord - }, - set: (value) => store.set(key, value), - clear: () => store.delete(key), - }, - log: (message, data) => logger.log(message, data), - }) +export type Platform = { + readonly checkForUpdate: Effect.Effect + readonly stageUpdate: Effect.Effect + readonly installAndRestart: Effect.Effect + readonly dispose: () => void } -export function startAutoUpdater(controller: UpdaterController) { - void controller.start() - const timer = setInterval(() => void controller.check(), 10 * 60 * 1000) - timer.unref() - app.once("will-quit", () => clearInterval(timer)) +export type Dependencies = { + readonly currentVersion: string + readonly platform?: Platform + readonly prepareToRestart: Effect.Effect + readonly persistence: { + readonly get: Effect.Effect<{ version: string } | undefined, unknown> + readonly set: (value: { version: string }) => Effect.Effect + readonly clear: Effect.Effect + } + readonly show?: ( + check: Effect.Effect, + install: Effect.Effect, + ) => Effect.Effect } -export function createUpdaterIpc(controller: UpdaterController) { +export interface Interface { + readonly subscribe: (sender: WebContents) => Effect.Effect + readonly unsubscribe: (id: number) => Effect.Effect + readonly check: Effect.Effect + readonly install: Effect.Effect + readonly show: Effect.Effect + readonly state: Effect.Effect + readonly started: Effect.Effect +} + +export class Service extends Context.Service()("opencode/desktop/Updater") {} + +export const layerWith = (dependencies: Dependencies) => Layer.effect(Service, make(dependencies)) + +export const make = Effect.fn("Updater.make")(function* (dependencies: Dependencies) { + const runFork = Effect.runForkWith(yield* Effect.context()) + let state: UpdaterState = dependencies.platform ? { status: "idle" } : { status: "disabled" } + let pending: Deferred.Deferred | undefined + let installing: Deferred.Deferred | undefined + const listeners = new Set<(state: UpdaterState) => void>() const subscriptions = new Map void>() + const transition = (next: UpdaterState) => { + runFork(Effect.logInfo("updater state changed", { from: state.status, to: next.status })) + state = next + listeners.forEach((listener) => listener(state)) + return state + } + const findAndStage = (platform: Platform) => + Effect.gen(function* () { + yield* Effect.sync(() => transition({ status: "checking" })) + const version = yield* platform.checkForUpdate + if (!version || version === dependencies.currentVersion) { + yield* dependencies.persistence.clear + return transition({ status: "up-to-date" }) + } + transition({ status: "downloading", version }) + yield* platform.stageUpdate + yield* dependencies.persistence.set({ version }) + return transition({ status: "ready", version }) + }).pipe( + Effect.catch((error) => + Effect.sync(() => + transition({ status: "error", message: error instanceof Error ? error.message : String(error) }), + ), + ), + ) + const refreshStaged = (platform: Platform, staged: string) => + Effect.gen(function* () { + const version = yield* platform.checkForUpdate + if (!version || version === staged || version === dependencies.currentVersion) return state + yield* platform.stageUpdate + yield* dependencies.persistence.set({ version }) + return transition({ status: installing ? "installing" : "ready", version }) + }).pipe( + Effect.catch((error) => + Effect.sync(() => { + runFork( + Effect.logWarning("updater refresh failed, keeping staged update", { + staged, + message: error instanceof Error ? error.message : String(error), + }), + ) + return state + }), + ), + ) + const check = Effect.suspend(() => { + const platform = dependencies.platform + if (!platform || state.status === "installing") return Effect.succeed(state) + if (pending) return Deferred.await(pending) + const deferred = Deferred.makeUnsafe() + pending = deferred + return (state.status === "ready" ? refreshStaged(platform, state.version) : findAndStage(platform)).pipe( + Effect.tap((result) => Deferred.succeed(deferred, result)), + Effect.ensuring(Effect.sync(() => (pending = undefined))), + ) + }) + const install = Effect.suspend(() => { + if (installing) return Deferred.await(installing) + const platform = dependencies.platform + if (!platform || state.status !== "ready") return Effect.fail(new Error("Update is not ready to install")) + const staged = state.version + transition({ status: "installing", version: staged }) + const deferred = Deferred.makeUnsafe() + installing = deferred + return Effect.gen(function* () { + yield* pending ? Deferred.await(pending) : refreshStaged(platform, staged) + yield* dependencies.prepareToRestart + return yield* platform.installAndRestart + }).pipe( + Effect.exit, + Effect.flatMap((exit) => + Deferred.done(deferred, exit).pipe( + Effect.andThen( + Effect.sync(() => { + installing = undefined + if (Exit.isFailure(exit) && state.status === "installing") { + transition({ status: "ready", version: state.version }) + } + }), + ), + Effect.andThen(Deferred.await(deferred)), + ), + ), + ) + }) + const start = Effect.gen(function* () { + const ready = yield* dependencies.persistence.get + if (ready?.version === dependencies.currentVersion) yield* dependencies.persistence.clear + yield* check + }) const unsubscribe = (id: number) => { subscriptions.get(id)?.() subscriptions.delete(id) } - app.once("will-quit", () => subscriptions.forEach((dispose) => dispose())) + const starting = yield* start.pipe(Effect.forkScoped) + yield* Effect.gen(function* () { + yield* Effect.sleep("10 minutes") + yield* check + }).pipe(Effect.forever, Effect.forkScoped) + yield* Effect.addFinalizer(() => + Effect.sync(() => { + dependencies.platform?.dispose() + subscriptions.forEach((dispose) => dispose()) + subscriptions.clear() + }), + ) - return { - subscribe(sender: WebContents) { - const id = sender.id - subscriptions.get(id)?.() // a reloaded renderer replaces its previous subscription - subscriptions.set( - id, - controller.subscribe((state) => { - if (sender.isDestroyed()) return unsubscribe(id) - sendIpcEvent(sender, Ipc.updater.state, state) - }), - ) - sender.once("destroyed", () => unsubscribe(id)) - }, - unsubscribe, - check: () => controller.check(), - install: () => controller.install(), - } -} - -export type UpdaterIpc = ReturnType - -export async function showUpdaterDialog(controller: UpdaterController) { - const state = await controller.check() - if (state.status === "error") { - await dialog.showMessageBox({ - type: "error", - message: nativeT("desktop.updater.dialog.checkFailed.message"), - title: nativeT("desktop.updater.dialog.checkFailed.title"), - }) - return - } - if (state.status === "up-to-date") { - await dialog.showMessageBox({ - type: "info", - message: nativeT("desktop.updater.dialog.upToDate.message"), - title: nativeT("desktop.updater.dialog.upToDate.title"), - }) - return - } - if (state.status !== "ready") return - - const response = await dialog.showMessageBox({ - type: "info", - message: nativeT("desktop.updater.dialog.ready.message", { version: state.version }), - title: nativeT("desktop.updater.dialog.ready.title"), - buttons: [nativeT("desktop.updater.dialog.restart"), nativeT("desktop.updater.dialog.later")], - defaultId: 0, - cancelId: 1, + return Service.of({ + subscribe: (sender) => + Effect.sync(() => { + const id = sender.id + subscriptions.get(id)?.() + subscriptions.set( + id, + (() => { + const listener = (state: UpdaterState) => { + if (sender.isDestroyed()) return unsubscribe(id) + emitIpcEvent(sender, new UpdaterStateChanged({ state })) + } + listeners.add(listener) + listener(state) + return () => listeners.delete(listener) + })(), + ) + sender.once("destroyed", () => unsubscribe(id)) + }), + unsubscribe: (id) => Effect.sync(() => unsubscribe(id)), + check, + install: install.pipe(Effect.orDie), + show: dependencies.show ? dependencies.show(check, install).pipe(Effect.orDie) : Effect.void, + state: Effect.sync(() => state), + started: Fiber.join(starting).pipe(Effect.orDie), }) - if (response.response === 0) await controller.install() -} +}) diff --git a/packages/desktop/src/main/updater/live.ts b/packages/desktop/src/main/updater/live.ts new file mode 100644 index 00000000000..b5f1a0dc2a2 --- /dev/null +++ b/packages/desktop/src/main/updater/live.ts @@ -0,0 +1,85 @@ +export * as UpdaterLive from "./live" + +import { dialog } from "electron" +import { Effect, Layer } from "effect" +import type { UpdaterState } from "@opencode-ai/app/updater" +import { UPDATER_ENABLED } from "../constants" +import { DesktopInitialization } from "../lifecycle/desktop-initialization" +import { ApplicationLifecycle } from "../lifecycle" +import { nativeT } from "../native/translations" +import { make, Service } from "./index" + +const key = "ready" + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const lifecycle = yield* ApplicationLifecycle.Service + const desktop = yield* DesktopInitialization.Service + const platform = UPDATER_ENABLED + ? yield* Effect.gen(function* () { + const { make } = yield* Effect.promise(() => import("./platform")) + return yield* make + }) + : undefined + return yield* make({ + currentVersion: desktop.version, + platform, + prepareToRestart: lifecycle.prepareToRestart, + persistence: { + get: Effect.sync(() => { + const value = desktop.updaterStore.get(key) + if (!value || typeof value !== "object" || !("version" in value) || typeof value.version !== "string") return + return { version: value.version } + }), + set: (value) => Effect.sync(() => desktop.updaterStore.set(key, value)), + clear: Effect.sync(() => desktop.updaterStore.delete(key)), + }, + show, + }) + }), +) + +const show = Effect.fn("Updater.show")(function* ( + check: Effect.Effect, + install: Effect.Effect, +) { + const state = yield* check + if (state.status === "error") { + yield* promise(() => + dialog.showMessageBox({ + type: "error", + message: nativeT("desktop.updater.dialog.checkFailed.message"), + title: nativeT("desktop.updater.dialog.checkFailed.title"), + }), + ) + return + } + if (state.status === "up-to-date") { + yield* promise(() => + dialog.showMessageBox({ + type: "info", + message: nativeT("desktop.updater.dialog.upToDate.message"), + title: nativeT("desktop.updater.dialog.upToDate.title"), + }), + ) + return + } + if (state.status !== "ready") return + + const response = yield* promise(() => + dialog.showMessageBox({ + type: "info", + message: nativeT("desktop.updater.dialog.ready.message", { version: state.version }), + title: nativeT("desktop.updater.dialog.ready.title"), + buttons: [nativeT("desktop.updater.dialog.restart"), nativeT("desktop.updater.dialog.later")], + defaultId: 0, + cancelId: 1, + }), + ) + if (response.response === 0) yield* install +}) + +function promise(evaluate: () => Promise) { + return Effect.tryPromise(evaluate).pipe(Effect.orDie) +} diff --git a/packages/desktop/src/main/updater/platform.ts b/packages/desktop/src/main/updater/platform.ts index 3a34b05a3d7..8033870365d 100644 --- a/packages/desktop/src/main/updater/platform.ts +++ b/packages/desktop/src/main/updater/platform.ts @@ -1,89 +1,106 @@ import { app, autoUpdater } from "electron" import pkg from "electron-updater" -import { getLogger } from "../native/logging" +import { Effect } from "effect" import { setAppQuitting } from "../windows" -import type { UpdaterPlatform } from "./controller" +import type { Platform } from "./index" const updateClient = pkg.autoUpdater const restartTimeout = 10_000 -export function createUpdaterPlatform(logger: ReturnType): UpdaterPlatform { - configureUpdater(logger) - autoUpdater.on("before-quit-for-update", () => setAppQuitting()) - - return { - async checkForUpdate() { - const result = await updateClient.checkForUpdates() - if (!result?.isUpdateAvailable) return - return result.updateInfo.version - }, - stageUpdate, - installAndRestart: () => installAndRestart(logger), +export const make = Effect.gen(function* () { + const runFork = Effect.runForkWith(yield* Effect.context()) + updateClient.logger = { + info: (...args) => runFork(Effect.logInfo(...args)), + warn: (...args) => runFork(Effect.logWarning(...args)), + error: (...args) => runFork(Effect.logError(...args)), + debug: (...args) => runFork(Effect.logDebug(...args)), } -} - -function configureUpdater(logger: ReturnType) { - updateClient.logger = logger updateClient.channel = "latest" updateClient.allowPrerelease = false updateClient.allowDowngrade = true updateClient.autoDownload = false updateClient.autoInstallOnAppQuit = process.platform === "darwin" - logger.log("auto updater configured", { + yield* Effect.logInfo("auto updater configured", { channel: updateClient.channel, allowPrerelease: updateClient.allowPrerelease, allowDowngrade: updateClient.allowDowngrade, currentVersion: app.getVersion(), }) -} + const beforeQuit = () => setAppQuitting() + autoUpdater.on("before-quit-for-update", beforeQuit) + + return { + checkForUpdate: Effect.tryPromise({ + try: async () => { + const result = await updateClient.checkForUpdates() + return result?.isUpdateAvailable ? result.updateInfo.version : undefined + }, + catch: (error) => error, + }), + stageUpdate: stageUpdate(), + installAndRestart, + dispose: () => autoUpdater.off("before-quit-for-update", beforeQuit), + } satisfies Platform +}) function stageUpdate() { - if (process.platform !== "darwin") return updateClient.downloadUpdate() + if (process.platform !== "darwin") + return Effect.tryPromise({ + try: () => updateClient.downloadUpdate(), + catch: (error) => error, + }).pipe(Effect.asVoid) - return new Promise((resolve, reject) => { + return Effect.callback((resume) => { const cleanup = () => { autoUpdater.removeListener("update-downloaded", complete) updateClient.removeListener("error", fail) } const complete = () => { cleanup() - resolve() + resume(Effect.void) } const fail = (error: Error) => { cleanup() - reject(error) + resume(Effect.fail(error)) } autoUpdater.once("update-downloaded", complete) updateClient.once("error", fail) void updateClient.downloadUpdate().catch(fail) + return Effect.sync(cleanup) }) } -function installAndRestart(logger: ReturnType) { - return new Promise((_resolve, reject) => { - const timeout = setTimeout(() => { - logger.error("update restart did not start") - fail(new Error()) - }, restartTimeout) - const started = () => { - clearTimeout(timeout) - autoUpdater.removeListener("before-quit-for-update", started) - } - const fail = (error: Error) => { - clearTimeout(timeout) - autoUpdater.removeListener("before-quit-for-update", started) - updateClient.removeListener("error", fail) - setAppQuitting(false) - reject(error) - } +const installAndRestart = Effect.callback((resume) => { + const cleanup = () => { + autoUpdater.removeListener("before-quit-for-update", started) + updateClient.removeListener("error", fail) + } + const started = () => { + cleanup() + resume(Effect.void) + } + const fail = (error: Error) => { + cleanup() + resume(Effect.fail(error)) + } - autoUpdater.once("before-quit-for-update", started) - updateClient.once("error", fail) - try { - updateClient.quitAndInstall() - } catch (error) { - fail(error instanceof Error ? error : new Error(String(error))) - } - }) -} + autoUpdater.once("before-quit-for-update", started) + updateClient.once("error", fail) + try { + updateClient.quitAndInstall() + } catch (error) { + fail(error instanceof Error ? error : new Error(String(error))) + } + return Effect.sync(cleanup) +}).pipe( + Effect.timeoutOrElse({ + duration: restartTimeout, + orElse: () => + Effect.logError("update restart did not start").pipe( + Effect.andThen(Effect.fail(new Error("Update restart did not start"))), + ), + }), + Effect.tapError(() => Effect.sync(() => setAppQuitting(false))), + Effect.andThen(Effect.never), +) diff --git a/packages/desktop/src/main/windows/appearance.ts b/packages/desktop/src/main/windows/appearance.ts index 449a294be4d..5dd3e95e636 100644 --- a/packages/desktop/src/main/windows/appearance.ts +++ b/packages/desktop/src/main/windows/appearance.ts @@ -2,9 +2,11 @@ import { resolveThemeVariant } from "@opencode-ai/ui/theme/resolve" import type { DesktopTheme } from "@opencode-ai/ui/theme/types" import oc2ThemeJson from "../../../../ui/src/theme/themes/oc-2.json" import { app, BrowserWindow, nativeImage, nativeTheme } from "electron" -import { join } from "node:path" -import { Ipc, sendIpcEvent, type TitlebarTheme } from "../../shared/ipc-contract" -import { developmentResourcesRoot, preloadPath } from "../paths" +import type { Path } from "effect" +import { type TitlebarTheme } from "../../shared/ipc-contract" +import { WindowFullscreenChanged, WindowPinchZoomChanged, WindowZoomChanged } from "../../shared/ipc-rpc/events" +import { emitIpcEvent } from "../ipc-events" +import type { DesktopPaths } from "../paths" import { BACKGROUND_COLOR_KEY, PINCH_ZOOM_ENABLED_KEY } from "../storage/keys" import { getStore } from "../storage/store" @@ -20,12 +22,12 @@ const maxZoomLevel = 10 const minZoomLevel = 0.2 let backgroundColor: string | undefined -export function windowAppearance() { +export function windowAppearance(path: Path.Path, paths: DesktopPaths.Resolved) { const mode = tone() const storedBackground = getStore().get(BACKGROUND_COLOR_KEY) return { title: "OpenCode", - icon: iconPath(), + icon: iconPath(path, paths), backgroundColor: backgroundColor ?? (typeof storedBackground === "string" ? storedBackground : undefined) ?? oc2Background[mode], ...(process.platform === "darwin" @@ -42,7 +44,7 @@ export function windowAppearance() { } : {}), webPreferences: { - preload: preloadPath, + preload: paths.preloadPath, contextIsolation: true, nodeIntegration: false, sandbox: true, @@ -50,9 +52,9 @@ export function windowAppearance() { } } -export function setDockIcon() { +export function setDockIcon(path: Path.Path, paths: DesktopPaths.Resolved) { if (process.platform !== "darwin") return - const icon = nativeImage.createFromPath(join(iconsDir(), "dock.png")) + const icon = nativeImage.createFromPath(path.join(iconsDir(path, paths), "dock.png")) if (!icon.isEmpty()) app.dock?.setIcon(icon) } @@ -86,7 +88,7 @@ export function setPinchZoomEnabled(enabled: boolean) { getStore().set(PINCH_ZOOM_ENABLED_KEY, enabled) BrowserWindow.getAllWindows().forEach((win) => { pinchZoomEnabled.set(win, enabled) - sendIpcEvent(win.webContents, Ipc.window.pinchZoomEnabledChanged, enabled) + emitIpcEvent(win.webContents, new WindowPinchZoomChanged({ enabled })) if (!enabled && win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1) updateZoom(win) }) @@ -115,18 +117,20 @@ export function wireZoom(win: BrowserWindow) { export function wireFullscreen(win: BrowserWindow) { const send = (fullscreen: boolean) => { if (win.isDestroyed() || win.webContents.isDestroyed()) return - sendIpcEvent(win.webContents, Ipc.window.fullscreenChanged, fullscreen) + emitIpcEvent(win.webContents, new WindowFullscreenChanged({ fullscreen })) } win.on("enter-full-screen", () => send(true)) win.on("leave-full-screen", () => send(false)) } -function iconsDir() { - return app.isPackaged ? join(process.resourcesPath, "icons") : join(developmentResourcesRoot, "icons") +function iconsDir(path: Path.Path, paths: DesktopPaths.Resolved) { + return app.isPackaged + ? path.join(process.resourcesPath, "icons") + : path.join(paths.developmentResourcesRoot, "icons") } -function iconPath() { - return join(iconsDir(), `icon.${process.platform === "win32" ? "ico" : "png"}`) +function iconPath(path: Path.Path, paths: DesktopPaths.Resolved) { + return path.join(iconsDir(path, paths), `icon.${process.platform === "win32" ? "ico" : "png"}`) } function tone() { @@ -148,5 +152,5 @@ function clampZoom(value: number) { function updateZoom(win: BrowserWindow) { updateTitlebar(win) - sendIpcEvent(win.webContents, Ipc.window.zoomFactorChanged, win.webContents.getZoomFactor()) + emitIpcEvent(win.webContents, new WindowZoomChanged({ factor: win.webContents.getZoomFactor() })) } diff --git a/packages/desktop/src/main/windows/index.ts b/packages/desktop/src/main/windows/index.ts index 12ef8cbaee0..bbff6eaa269 100644 --- a/packages/desktop/src/main/windows/index.ts +++ b/packages/desktop/src/main/windows/index.ts @@ -1,10 +1,11 @@ import windowState from "electron-window-state" import { randomUUID } from "node:crypto" -import { rmSync } from "node:fs" -import { join } from "node:path" import { app, BrowserWindow } from "electron" -import { writeLog } from "../native/logging" -import { removeStoreFile, getStore } from "../storage/store" +import { Effect, FileSystem, Path } from "effect" +import { openExternalURL } from "../files" +import { scoped } from "../native/logging" +import { DesktopPaths } from "../paths" +import { forgetStore, getStore } from "../storage/store" import { WINDOW_IDS_KEY } from "../storage/keys" import { getBackgroundColor, @@ -20,7 +21,7 @@ import { } from "./appearance" import { loadWindow, registerRendererProtocol } from "./protocol" import { createWindowRegistry } from "./registry" -import { wireWindowRecovery } from "./recovery" +import { makeWindowRecovery } from "./recovery" import { allowRendererPermissions, wireNavigationPolicy, wireRendererHeaders } from "./security" const windowIDs = new WeakMap() @@ -28,10 +29,6 @@ const themeReady = new WeakMap void>() const registry = createWindowRegistry({ read: () => getStore().get(WINDOW_IDS_KEY), write: (ids) => getStore().set(WINDOW_IDS_KEY, ids), - cleanup: (id) => { - rmSync(join(app.getPath("userData"), windowStateFile(id)), { force: true }) - removeStoreFile(windowDataFile(id)) - }, }) let relaunchHandler = () => { setAppQuitting() @@ -51,7 +48,11 @@ export { } export function setRelaunchHandler(handler: () => void) { + const previous = relaunchHandler relaunchHandler = handler + return () => { + if (relaunchHandler === handler) relaunchHandler = previous + } } export function setAppQuitting(quitting = true) { @@ -74,63 +75,85 @@ export function setWindowThemeReady(win: BrowserWindow) { themeReady.get(win)?.() } -export function restoreMainWindows() { - const ids = registry.persisted() - return (ids.length ? ids : [randomUUID()]).map((id) => createMainWindow(id)) -} +export const makeMainWindows = Effect.fn("Window.make")(function* () { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const paths = yield* DesktopPaths.resolve + const runFork = Effect.runForkWith(yield* Effect.context()) + const wireWindowRecovery = yield* makeWindowRecovery -export function createMainWindow(id: string = randomUUID()) { - const state = windowState({ file: windowStateFile(id), defaultWidth: 1280, defaultHeight: 800 }) - const win = new BrowserWindow({ - x: state.x, - y: state.y, - width: state.width, - height: state.height, - show: false, - autoHideMenuBar: true, - ...windowAppearance(), - }) - - allowRendererPermissions(win) - wireWindowRecovery(win, id, () => relaunchHandler()) - wireNavigationPolicy(win) - wireRendererHeaders(win) - state.manage(win) - registerWindow(win, id) - wireFullscreen(win) - wireZoom(win) - let contentReady = false - let appliedTheme = false - let revealed = false - const reveal = () => { - if (!contentReady || !appliedTheme || revealed || win.isDestroyed()) return - revealed = true - win.show() - writeLog("window", "main window visible", { window: id }) + const restore = () => { + const ids = registry.persisted() + return (ids.length ? ids : [randomUUID()]).map((id) => create(id)) } - const ready = () => { - contentReady = true - reveal() - } - themeReady.set(win, () => { - appliedTheme = true - reveal() - }) - win.once("ready-to-show", ready) - if (process.platform === "linux") win.webContents.once("did-finish-load", ready) - win.once("closed", () => themeReady.delete(win)) - loadWindow(win, "index.html") - return win -} -function registerWindow(win: BrowserWindow, id: string) { - windowIDs.set(win, id) - registry.register(id, win) - win.on("focus", () => registry.focused(id)) - // Windows emits session-end, but not before-quit, during shutdown and logoff. - win.on("session-end", () => registry.setQuitting()) - win.on("closed", () => registry.closed(id)) -} + const create = (id: string = randomUUID()) => { + const state = windowState({ file: windowStateFile(id), defaultWidth: 1280, defaultHeight: 800 }) + const win = new BrowserWindow({ + x: state.x, + y: state.y, + width: state.width, + height: state.height, + show: false, + autoHideMenuBar: true, + ...windowAppearance(path, paths), + }) + + allowRendererPermissions(win) + wireWindowRecovery(win, id, () => relaunchHandler()) + wireNavigationPolicy(win, (url) => runFork(openExternalURL(url))) + wireRendererHeaders(win) + state.manage(win) + register(win, id) + wireFullscreen(win) + loadWindow(win, "index.html") + wireZoom(win) + let contentReady = false + let appliedTheme = false + let revealed = false + const reveal = () => { + if (!contentReady || !appliedTheme || revealed || win.isDestroyed()) return + revealed = true + win.show() + runFork(Effect.logInfo("main window visible", { window: id })) + } + const ready = () => { + contentReady = true + reveal() + } + themeReady.set(win, () => { + appliedTheme = true + reveal() + }) + win.once("ready-to-show", ready) + if (process.platform === "linux") win.webContents.once("did-finish-load", ready) + win.once("closed", () => themeReady.delete(win)) + return win + } + + const register = (win: BrowserWindow, id: string) => { + windowIDs.set(win, id) + registry.register(id, win) + win.on("focus", () => registry.focused(id)) + // Windows emits session-end, but not before-quit, during shutdown and logoff. + win.on("session-end", () => registry.setQuitting()) + win.on("closed", () => { + if (!registry.closed(id)) return + const data = windowDataFile(id) + runFork( + Effect.gen(function* () { + yield* fs.remove(path.join(app.getPath("userData"), windowStateFile(id)), { force: true }) + yield* fs.remove(path.join(app.getPath("userData"), data), { force: true }) + }).pipe( + Effect.tap(() => Effect.sync(() => forgetStore(data))), + Effect.catch((error) => scoped("window", Effect.logError("failed to clean window files", { id, error }))), + ), + ) + }) + } + + return { create, restore } +}) function windowStateFile(id: string) { return `window-state-${safeWindowID(id)}.json` diff --git a/packages/desktop/src/main/windows/protocol.ts b/packages/desktop/src/main/windows/protocol.ts index 346afa86619..ec94d836964 100644 --- a/packages/desktop/src/main/windows/protocol.ts +++ b/packages/desktop/src/main/windows/protocol.ts @@ -1,9 +1,9 @@ import { net, protocol } from "electron" import type { BrowserWindow } from "electron" -import { isAbsolute, relative, resolve } from "node:path" import { pathToFileURL } from "node:url" -import { writeLog } from "../native/logging" -import { rendererRoot } from "../paths" +import { Effect, Path } from "effect" +import { scoped } from "../native/logging" +import { DesktopPaths } from "../paths" const rendererProtocol = "oc" const rendererHost = "renderer" @@ -22,20 +22,23 @@ protocol.registerSchemesAsPrivileged([ }, ]) -export function registerRendererProtocol() { +export const registerRendererProtocol = Effect.fn("Window.registerRendererProtocol")(function* () { + const path = yield* Path.Path + const paths = yield* DesktopPaths.resolve + const runFork = Effect.runForkWith(yield* Effect.context()) if (protocol.isProtocolHandled(rendererProtocol)) return protocol.handle(rendererProtocol, async (request) => { const url = new URL(request.url) if (url.host !== rendererHost) { - writeLog("protocol", "rejected host", { url: request.url }, "warn") + runFork(scoped("protocol", Effect.logWarning("rejected host", { url: request.url }))) return new Response("Not found", { status: 404 }) } - const file = resolve(rendererRoot, `.${decodeURIComponent(url.pathname)}`) - const rel = relative(rendererRoot, file) - if (rel.startsWith("..") || isAbsolute(rel)) { - writeLog("protocol", "rejected path", { url: request.url, file }, "warn") + const file = path.resolve(paths.rendererRoot, `.${decodeURIComponent(url.pathname)}`) + const rel = path.relative(paths.rendererRoot, file) + if (rel.startsWith("..") || path.isAbsolute(rel)) { + runFork(scoped("protocol", Effect.logWarning("rejected path", { url: request.url, file }))) return new Response("Not found", { status: 404 }) } @@ -43,20 +46,25 @@ export function registerRendererProtocol() { const range = request.headers.get("range") const response = await net.fetch(pathToFileURL(file).toString(), { headers: range ? { range } : undefined }) if (response.status >= 400) { - writeLog( - "protocol", - "fetch failed", - { url: request.url, file, status: response.status, statusText: response.statusText }, - "error", + runFork( + scoped( + "protocol", + Effect.logError("fetch failed", { + url: request.url, + file, + status: response.status, + statusText: response.statusText, + }), + ), ) } return addDocumentPolicy(response, file) } catch (error) { - writeLog("protocol", "fetch error", { url: request.url, file, error }, "error") + runFork(scoped("protocol", Effect.logError("fetch error", { url: request.url, file, error }))) return new Response("Not found", { status: 404 }) } }) -} +}) export function loadWindow(win: BrowserWindow, html: string) { const devUrl = process.env.ELECTRON_RENDERER_URL diff --git a/packages/desktop/src/main/windows/recovery.ts b/packages/desktop/src/main/windows/recovery.ts index 0c1d08475ef..2e2a00a1c69 100644 --- a/packages/desktop/src/main/windows/recovery.ts +++ b/packages/desktop/src/main/windows/recovery.ts @@ -1,126 +1,156 @@ import { app, dialog } from "electron" import type { BrowserWindow } from "electron" -import { exportDebugLogs, writeLog } from "../native/logging" +import { Effect } from "effect" +import { DesktopLogging, scoped } from "../native/logging" import { nativeT } from "../native/translations" import { safeWindowURL } from "./state" -import { createUnresponsiveSampler } from "./unresponsive" +import { makeUnresponsiveSampler } from "./unresponsive" -export function wireWindowRecovery(win: BrowserWindow, name: string, relaunch: () => void) { - let showing = false - const sampler = createUnresponsiveSampler(win, name) +export const makeWindowRecovery = Effect.gen(function* () { + const context = yield* Effect.context() + const runFork = Effect.runForkWith(context) + const runPromise = Effect.runPromiseWith(context) + const logging = yield* DesktopLogging.Service + const createUnresponsiveSampler = yield* makeUnresponsiveSampler - type RecoveryAction = "relaunch" | "export-logs" | "keep-waiting" | "quit" - const handle = async (action: RecoveryAction | undefined, wait: boolean) => { - if (action === "export-logs") { - const sampling = sampler.stopAndFlush() - await exportDebugLogs().catch((error) => writeLog("main", "failed to export debug logs", { error }, "error")) - if (wait && sampling) sampler.start() - return true - } - if (action === "relaunch") { - sampler.stopAndFlush() - relaunch() + function wireWindowRecovery(win: BrowserWindow, name: string, relaunch: () => void) { + let showing = false + const sampler = createUnresponsiveSampler(win, name) + + type RecoveryAction = "relaunch" | "export-logs" | "keep-waiting" | "quit" + const handle = async (action: RecoveryAction | undefined, wait: boolean) => { + if (action === "export-logs") { + const sampling = sampler.stopAndFlush() + await runPromise(logging.exportDebug).catch((error) => + runFork(Effect.logError("failed to export debug logs", { error })), + ) + if (wait && sampling) sampler.start() + return true + } + if (action === "relaunch") { + sampler.stopAndFlush() + relaunch() + return false + } + if (action === "quit") { + sampler.stopAndFlush() + app.quit() + } return false } - if (action === "quit") { - sampler.stopAndFlush() - app.quit() - } - return false - } - const show = async (message: string, detail: string, wait: boolean) => { - if (showing || win.isDestroyed()) return - showing = true - try { - while (!win.isDestroyed()) { - const actions: { id: RecoveryAction; label: string }[] = wait - ? [ - { id: "relaunch", label: nativeT("desktop.recovery.action.relaunch") }, - { id: "export-logs", label: nativeT("desktop.recovery.action.exportLogs") }, - { id: "keep-waiting", label: nativeT("desktop.recovery.action.keepWaiting") }, - ] - : [ - { id: "relaunch", label: nativeT("desktop.recovery.action.relaunch") }, - { id: "export-logs", label: nativeT("desktop.recovery.action.exportLogs") }, - { id: "quit", label: nativeT("desktop.recovery.action.quit") }, - ] - const result = await dialog.showMessageBox(win, { - type: "warning", - buttons: actions.map((action) => action.label), - defaultId: 0, - cancelId: 2, - message, - detail, - }) - if (await handle(actions[result.response]?.id, wait)) continue - return + const show = async (message: string, detail: string, wait: boolean) => { + if (showing || win.isDestroyed()) return + showing = true + try { + while (!win.isDestroyed()) { + const actions: { id: RecoveryAction; label: string }[] = wait + ? [ + { id: "relaunch", label: nativeT("desktop.recovery.action.relaunch") }, + { id: "export-logs", label: nativeT("desktop.recovery.action.exportLogs") }, + { id: "keep-waiting", label: nativeT("desktop.recovery.action.keepWaiting") }, + ] + : [ + { id: "relaunch", label: nativeT("desktop.recovery.action.relaunch") }, + { id: "export-logs", label: nativeT("desktop.recovery.action.exportLogs") }, + { id: "quit", label: nativeT("desktop.recovery.action.quit") }, + ] + const result = await dialog.showMessageBox(win, { + type: "warning", + buttons: actions.map((action) => action.label), + defaultId: 0, + cancelId: 2, + message, + detail, + }) + if (await handle(actions[result.response]?.id, wait)) continue + return + } + } finally { + showing = false } - } finally { - showing = false } + + const failed = ( + event: string, + errorCode: number, + errorDescription: string, + validatedURL: string, + isMainFrame: boolean, + ) => { + runFork( + scoped( + "window", + Effect.logError("renderer load failed", { + window: name, + event, + errorCode, + errorDescription, + validatedURL, + currentURL: safeWindowURL(win), + isMainFrame, + }), + ), + ) + if (!isMainFrame || errorCode === -3) return + void show( + nativeT("desktop.recovery.loadFailed"), + nativeT("desktop.recovery.loadFailed.detail", { + window: name, + url: validatedURL, + code: errorCode, + description: errorDescription, + }), + false, + ) + } + + win.webContents.on("did-fail-load", (_event, code, description, url, mainFrame) => { + failed("did-fail-load", code, description, url, mainFrame) + }) + win.webContents.on("did-fail-provisional-load", (_event, code, description, url, mainFrame) => { + failed("did-fail-provisional-load", code, description, url, mainFrame) + }) + win.webContents.on("render-process-gone", (_event, details) => { + sampler.stopAndFlush() + runFork( + scoped( + "window", + Effect.logError("renderer process gone", { window: name, currentURL: safeWindowURL(win), details }), + ), + ) + void show( + nativeT("desktop.recovery.terminated"), + nativeT("desktop.recovery.terminated.detail", { + window: name, + reason: details.reason, + code: details.exitCode ?? nativeT("desktop.recovery.unknown"), + }), + false, + ) + }) + win.on("unresponsive", () => { + runFork( + scoped("window", Effect.logError("renderer unresponsive", { window: name, currentURL: safeWindowURL(win) })), + ) + sampler.start() + void show(nativeT("desktop.recovery.unresponsive"), nativeT("desktop.recovery.unresponsive.detail"), true) + }) + win.on("responsive", () => { + runFork( + scoped("window", Effect.logError("renderer responsive", { window: name, currentURL: safeWindowURL(win) })), + ) + sampler.stopAndFlush() + }) + win.webContents.on("console-message", (_event, level, message, line, sourceId) => { + if (message.toLowerCase().includes("terminal") || sourceId.toLowerCase().includes("terminal")) { + runFork(scoped("pty", Effect.logInfo("console", { window: name, level, message, line, sourceId }))) + } + }) + win.webContents.on("preload-error", (_event, path, error) => { + runFork(scoped("preload", Effect.logError("preload error", { window: name, preloadPath: path, error }))) + }) } - const failed = ( - event: string, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - ) => { - writeLog( - "window", - "renderer load failed", - { window: name, event, errorCode, errorDescription, validatedURL, currentURL: safeWindowURL(win), isMainFrame }, - "error", - ) - if (!isMainFrame || errorCode === -3) return - void show( - nativeT("desktop.recovery.loadFailed"), - nativeT("desktop.recovery.loadFailed.detail", { - window: name, - url: validatedURL, - code: errorCode, - description: errorDescription, - }), - false, - ) - } - - win.webContents.on("did-fail-load", (_event, code, description, url, mainFrame) => { - failed("did-fail-load", code, description, url, mainFrame) - }) - win.webContents.on("did-fail-provisional-load", (_event, code, description, url, mainFrame) => { - failed("did-fail-provisional-load", code, description, url, mainFrame) - }) - win.webContents.on("render-process-gone", (_event, details) => { - sampler.stopAndFlush() - writeLog("window", "renderer process gone", { window: name, currentURL: safeWindowURL(win), details }, "error") - void show( - nativeT("desktop.recovery.terminated"), - nativeT("desktop.recovery.terminated.detail", { - window: name, - reason: details.reason, - code: details.exitCode ?? nativeT("desktop.recovery.unknown"), - }), - false, - ) - }) - win.on("unresponsive", () => { - writeLog("window", "renderer unresponsive", { window: name, currentURL: safeWindowURL(win) }, "error") - sampler.start() - void show(nativeT("desktop.recovery.unresponsive"), nativeT("desktop.recovery.unresponsive.detail"), true) - }) - win.on("responsive", () => { - writeLog("window", "renderer responsive", { window: name, currentURL: safeWindowURL(win) }, "error") - sampler.stopAndFlush() - }) - win.webContents.on("console-message", (_event, level, message, line, sourceId) => { - if (message.toLowerCase().includes("terminal") || sourceId.toLowerCase().includes("terminal")) { - writeLog("pty", "console", { window: name, level, message, line, sourceId }) - } - }) - win.webContents.on("preload-error", (_event, path, error) => { - writeLog("preload", "preload error", { window: name, preloadPath: path, error }, "error") - }) -} + return wireWindowRecovery +}) diff --git a/packages/desktop/src/main/windows/registry.test.ts b/packages/desktop/src/main/windows/registry.test.ts index f404bf60ee8..cad9f63eca9 100644 --- a/packages/desktop/src/main/windows/registry.test.ts +++ b/packages/desktop/src/main/windows/registry.test.ts @@ -3,15 +3,13 @@ import { createWindowRegistry } from "./registry" function setup(initial: unknown = []) { const state = { stored: initial } - const cleaned: string[] = [] const registry = createWindowRegistry<{ name: string }>({ read: () => state.stored, write: (ids) => { state.stored = ids }, - cleanup: (id) => cleaned.push(id), }) - return { registry, state, cleaned } + return { registry, state } } describe("window registry", () => { @@ -33,24 +31,21 @@ describe("window registry", () => { const app = setup() app.registry.register("a", { name: "a" }) app.registry.register("b", { name: "b" }) - app.registry.closed("a") + expect(app.registry.closed("a")).toBe(true) expect(app.state.stored).toEqual(["b"]) - expect(app.cleaned).toEqual(["a"]) }) test("keeps the id when the last window closes so relaunch restores it", () => { const app = setup() app.registry.register("a", { name: "a" }) - app.registry.closed("a") + expect(app.registry.closed("a")).toBe(false) expect(app.state.stored).toEqual(["a"]) - expect(app.cleaned).toEqual([]) const restarted = createWindowRegistry<{ name: string }>({ read: () => app.state.stored, write: (ids) => { app.state.stored = ids }, - cleanup: () => {}, }) expect(restarted.persisted()).toEqual(["a"]) }) @@ -60,10 +55,9 @@ describe("window registry", () => { app.registry.register("a", { name: "a" }) app.registry.register("b", { name: "b" }) app.registry.setQuitting() - app.registry.closed("a") - app.registry.closed("b") + expect(app.registry.closed("a")).toBe(false) + expect(app.registry.closed("b")).toBe(false) expect(app.state.stored).toEqual(["a", "b"]) - expect(app.cleaned).toEqual([]) }) test("tracks the last focused window and falls back on close", () => { @@ -84,8 +78,7 @@ describe("window registry", () => { app.registry.register("b", { name: "b" }) app.registry.setQuitting() app.registry.setQuitting(false) - app.registry.closed("a") + expect(app.registry.closed("a")).toBe(true) expect(app.state.stored).toEqual(["b"]) - expect(app.cleaned).toEqual(["a"]) }) }) diff --git a/packages/desktop/src/main/windows/registry.ts b/packages/desktop/src/main/windows/registry.ts index f40902ef78d..d5862daa3c1 100644 --- a/packages/desktop/src/main/windows/registry.ts +++ b/packages/desktop/src/main/windows/registry.ts @@ -3,7 +3,6 @@ export function createWindowRegistry(persistence: { read: () => unknown write: (ids: string[]) => void - cleanup: (id: string) => void }) { const windows = new Map() let quitting = false @@ -39,9 +38,9 @@ export function createWindowRegistry(persistence: { // forgets a window. Closing the last window quits the app and fires // `closed` before `before-quit`, so treat it as a quit and keep the id // for restore on next launch. - if (quitting || windows.size === 0) return + if (quitting || windows.size === 0) return false persistence.write(persisted().filter((item) => item !== id)) - persistence.cleanup(id) + return true }, } } diff --git a/packages/desktop/src/main/windows/security.ts b/packages/desktop/src/main/windows/security.ts index 3afff900712..52ba46a7e96 100644 --- a/packages/desktop/src/main/windows/security.ts +++ b/packages/desktop/src/main/windows/security.ts @@ -1,5 +1,4 @@ import type { BrowserWindow } from "electron" -import { openExternalURL } from "../files" import { addRendererHeaders, isRendererUrl, upsertHeader } from "./protocol" const rendererPermissions = new Set(["clipboard-sanitized-write", "notifications"]) @@ -18,7 +17,7 @@ export function allowRendererPermissions(win: BrowserWindow) { }) } -export function wireNavigationPolicy(win: BrowserWindow) { +export function wireNavigationPolicy(win: BrowserWindow, openExternalURL: (url: string) => unknown) { win.webContents.setWindowOpenHandler(({ url }) => { if (!isRendererUrl(url)) openExternalURL(url) return { action: "deny" } diff --git a/packages/desktop/src/main/windows/unresponsive.ts b/packages/desktop/src/main/windows/unresponsive.ts index 77848e03a9f..e2d7c2f6355 100644 --- a/packages/desktop/src/main/windows/unresponsive.ts +++ b/packages/desktop/src/main/windows/unresponsive.ts @@ -1,70 +1,77 @@ import type { BrowserWindow } from "electron" -import { writeLog } from "../native/logging" +import { Effect } from "effect" +import { scoped } from "../native/logging" import { safeWindowURL } from "./state" const sampleInterval = 1000 const samplePeriod = 15000 -export function createUnresponsiveSampler(win: BrowserWindow, name: string) { - let sampleTimer: ReturnType | undefined - let stopTimer: ReturnType | undefined - let sampling = false - const samples = new Map() +export const makeUnresponsiveSampler = Effect.gen(function* () { + const runFork = Effect.runForkWith(yield* Effect.context()) - const active = () => sampling && !win.isDestroyed() && !win.webContents.isDestroyed() - const clearTimers = () => { - if (sampleTimer) clearTimeout(sampleTimer) - if (stopTimer) clearTimeout(stopTimer) - sampleTimer = undefined - stopTimer = undefined + function createUnresponsiveSampler(win: BrowserWindow, name: string) { + let sampleTimer: ReturnType | undefined + let stopTimer: ReturnType | undefined + let sampling = false + const samples = new Map() + + const active = () => sampling && !win.isDestroyed() && !win.webContents.isDestroyed() + const clearTimers = () => { + if (sampleTimer) clearTimeout(sampleTimer) + if (stopTimer) clearTimeout(stopTimer) + sampleTimer = undefined + stopTimer = undefined + } + + const schedule = () => { + sampleTimer = setTimeout(() => { + void collect() + }, sampleInterval) + } + + const collect = async () => { + if (!active()) return + const stack = await win.webContents.mainFrame.collectJavaScriptCallStack().catch((error) => { + runFork(scoped("window", Effect.logError("failed to collect unresponsive sample", { window: name, error }))) + return undefined + }) + if (!active()) return + if (stack) samples.set(stack, (samples.get(stack) ?? 0) + 1) + schedule() + } + + const stopAndFlush = () => { + const wasSampling = sampling + sampling = false + clearTimers() + if (samples.size === 0) return wasSampling + + const entries = [...samples.entries()].sort((a, b) => b[1] - a[1]) + const total = entries.reduce((sum, entry) => sum + entry[1], 0) + const message = [ + "renderer unresponsive samples", + `Window: ${name}`, + `URL: ${safeWindowURL(win)}`, + ...entries.map((entry) => `<${entry[1]}> ${entry[0]}`), + `Total Samples: ${total}`, + ].join("\n") + runFork(scoped("window", Effect.logError(message))) + samples.clear() + return wasSampling + } + + const start = () => { + if (sampling || win.isDestroyed() || win.webContents.isDestroyed() || win.webContents.isDevToolsOpened()) return + sampling = true + samples.clear() + schedule() + stopTimer = setTimeout(stopAndFlush, samplePeriod) + } + + win.on("closed", stopAndFlush) + + return { start, stopAndFlush } } - const schedule = () => { - sampleTimer = setTimeout(() => { - void collect() - }, sampleInterval) - } - - const collect = async () => { - if (!active()) return - const stack = await win.webContents.mainFrame.collectJavaScriptCallStack().catch((error) => { - writeLog("window", "failed to collect unresponsive sample", { window: name, error }, "error") - return undefined - }) - if (!active()) return - if (stack) samples.set(stack, (samples.get(stack) ?? 0) + 1) - schedule() - } - - const stopAndFlush = () => { - const wasSampling = sampling - sampling = false - clearTimers() - if (samples.size === 0) return wasSampling - - const entries = [...samples.entries()].sort((a, b) => b[1] - a[1]) - const total = entries.reduce((sum, entry) => sum + entry[1], 0) - const message = [ - "renderer unresponsive samples", - `Window: ${name}`, - `URL: ${safeWindowURL(win)}`, - ...entries.map((entry) => `<${entry[1]}> ${entry[0]}`), - `Total Samples: ${total}`, - ].join("\n") - writeLog("window", message, undefined, "error") - samples.clear() - return wasSampling - } - - const start = () => { - if (sampling || win.isDestroyed() || win.webContents.isDestroyed() || win.webContents.isDevToolsOpened()) return - sampling = true - samples.clear() - schedule() - stopTimer = setTimeout(stopAndFlush, samplePeriod) - } - - win.on("closed", stopAndFlush) - - return { start, stopAndFlush } -} + return createUnresponsiveSampler +}) diff --git a/packages/desktop/src/main/wsl/ipc.ts b/packages/desktop/src/main/wsl/ipc.ts index 59fd4a58e57..c0765027863 100644 --- a/packages/desktop/src/main/wsl/ipc.ts +++ b/packages/desktop/src/main/wsl/ipc.ts @@ -1,27 +1,31 @@ +export * as WslIpc from "./ipc" + import { app } from "electron" import type { WebContents } from "electron" import type { WslServerConfig, WslServersState } from "@opencode-ai/app/wsl/types" -import { Ipc, sendIpcEvent } from "../../shared/ipc-contract" +import { Effect } from "effect" +import { WslServersChanged } from "../../shared/ipc-rpc/events" +import { emitIpcEvent } from "../ipc-events" import type { WslServersController } from "./servers" import { nativeT } from "../native/translations" -export type WslIpc = { - subscribe(sender: WebContents): void - unsubscribe(id: number): void - getState(): WslServersState - probeRuntime(): Promise - refreshDistros(): Promise - installWsl(): Promise - installDistro(value: string): Promise - probeAddable(value: string[]): Promise - installOpencode(value: string): Promise - openTerminal(value: string): Promise - addServer(value: string): Promise - removeServer(value: string): Promise - startServer(value: string): Promise +export interface Interface { + readonly subscribe: (sender: WebContents) => Effect.Effect + readonly unsubscribe: (id: number) => Effect.Effect + readonly getState: () => Effect.Effect + readonly probeRuntime: () => Effect.Effect + readonly refreshDistros: () => Effect.Effect + readonly installWsl: () => Effect.Effect + readonly installDistro: (value: string) => Effect.Effect + readonly probeAddable: (value: string[]) => Effect.Effect + readonly installOpencode: (value: string) => Effect.Effect + readonly openTerminal: (value: string) => Effect.Effect + readonly addServer: (value: string) => Effect.Effect + readonly removeServer: (value: string) => Effect.Effect + readonly startServer: (value: string) => Effect.Effect } -export function createWslIpc(controller?: WslServersController): WslIpc { +export function create(controller?: WslServersController): Interface { if (!controller) return createUnavailableWslIpc() const subscriptions = new Map void>() @@ -38,45 +42,53 @@ export function createWslIpc(controller?: WslServersController): WslIpc { }) return { - subscribe(sender) { - const id = sender.id - if (subscriptions.has(id)) return - subscriptions.set( - id, - controller.subscribe((payload) => { - if (sender.isDestroyed()) { - unsubscribe(id) - return - } - sendIpcEvent(sender, Ipc.wsl.event, payload) - }), - ) - sender.once("destroyed", () => unsubscribe(id)) - }, - unsubscribe, - getState: () => controller.getState(), - probeRuntime: () => controller.probeRuntime(), - refreshDistros: () => controller.refreshDistros(), - installWsl: () => controller.installWsl(), - installDistro: (value) => controller.installDistro(requireWslIpcString("distro", value)), - probeAddable: (value) => controller.probeAddable(requireWslIpcStrings("distro", value)), - installOpencode: (value) => controller.installOpencode(requireWslIpcString("distro", value)), - openTerminal: (value) => controller.openTerminal(requireWslIpcString("distro", value)), - addServer: (value) => controller.addServer(requireWslIpcString("distro", value)), - removeServer: (value) => controller.removeServer(requireWslIpcString("server id", value)), - startServer: (value) => controller.startServer(requireWslIpcString("server id", value)), + subscribe: (sender) => + Effect.sync(() => { + const id = sender.id + if (subscriptions.has(id)) return + subscriptions.set( + id, + controller.subscribe((payload) => { + if (sender.isDestroyed()) { + unsubscribe(id) + return + } + emitIpcEvent(sender, new WslServersChanged({ event: payload })) + }), + ) + sender.once("destroyed", () => unsubscribe(id)) + }), + unsubscribe: (id) => Effect.sync(() => unsubscribe(id)), + getState: () => Effect.sync(() => controller.getState()), + probeRuntime: () => promise(() => controller.probeRuntime()), + refreshDistros: () => promise(() => controller.refreshDistros()), + installWsl: () => promise(() => controller.installWsl()), + installDistro: (value) => promise(() => controller.installDistro(requireWslIpcString("distro", value))), + probeAddable: (value) => promise(() => controller.probeAddable(requireWslIpcStrings("distro", value))), + installOpencode: (value) => promise(() => controller.installOpencode(requireWslIpcString("distro", value))), + openTerminal: (value) => promise(() => controller.openTerminal(requireWslIpcString("distro", value))), + addServer: (value) => promise(() => controller.addServer(requireWslIpcString("distro", value))), + removeServer: (value) => promise(() => controller.removeServer(requireWslIpcString("server id", value))), + startServer: (value) => promise(() => controller.startServer(requireWslIpcString("server id", value))), } } -function createUnavailableWslIpc(): WslIpc { +function promise(evaluate: () => Promise) { + return Effect.tryPromise(evaluate).pipe(Effect.orDie) +} + +function createUnavailableWslIpc(): Interface { + const message = nativeT( + process.platform === "win32" ? "desktop.wsl.error.unavailable" : "desktop.wsl.error.windowsOnly", + ) const unavailable = () => { - throw new Error(nativeT("desktop.wsl.error.windowsOnly")) + throw new Error(message) } const state = (): WslServersState => ({ runtime: { available: false, version: null, - error: nativeT("desktop.wsl.error.windowsOnly"), + error: message, }, installed: [], online: [], @@ -88,19 +100,20 @@ function createUnavailableWslIpc(): WslIpc { }) return { - subscribe: (sender) => sendIpcEvent(sender, Ipc.wsl.event, { type: "state", state: state() }), - unsubscribe: () => undefined, - getState: state, - probeRuntime: unavailable, - refreshDistros: unavailable, - installWsl: unavailable, - installDistro: unavailable, - probeAddable: unavailable, - installOpencode: unavailable, - openTerminal: unavailable, - addServer: unavailable, - removeServer: unavailable, - startServer: unavailable, + subscribe: (sender) => + Effect.sync(() => emitIpcEvent(sender, new WslServersChanged({ event: { type: "state", state: state() } }))), + unsubscribe: () => Effect.void, + getState: () => Effect.sync(state), + probeRuntime: () => Effect.sync(unavailable), + refreshDistros: () => Effect.sync(unavailable), + installWsl: () => Effect.sync(unavailable), + installDistro: () => Effect.sync(unavailable), + probeAddable: () => Effect.sync(unavailable), + installOpencode: () => Effect.sync(unavailable), + openTerminal: () => Effect.sync(unavailable), + addServer: () => Effect.sync(unavailable), + removeServer: () => Effect.sync(unavailable), + startServer: () => Effect.sync(unavailable), } } diff --git a/packages/desktop/src/main/wsl/local.ts b/packages/desktop/src/main/wsl/local.ts index e1ec23eb0d7..1456cb91621 100644 --- a/packages/desktop/src/main/wsl/local.ts +++ b/packages/desktop/src/main/wsl/local.ts @@ -1,38 +1,48 @@ import { execFile } from "node:child_process" -import { copyFile, mkdtemp, readFile, rm } from "node:fs/promises" -import { tmpdir } from "node:os" -import { dirname, join } from "node:path" import { promisify } from "node:util" +import { Effect, FileSystem, Path } from "effect" const execFileAsync = promisify(execFile) -export async function buildLocalWslCli(input: { version: string; script: string; output: string }) { - const directory = await mkdtemp(join(tmpdir(), "opencode-wsl-cli-")) - const root = join(dirname(input.script), "../../..") - const packageManager = (JSON.parse(await readFile(join(root, "package.json"), "utf8")) as { packageManager: string }) - .packageManager - const target = `linux-${process.arch}` - try { - await execFileAsync("bunx", [packageManager, "install", "--os=*", "--cpu=*", "--frozen-lockfile"], { - cwd: root, - env: process.env, - windowsHide: true, - }) - await execFileAsync( - "bunx", - [ - packageManager, - input.script, - `--target=opencode2-${target}`, - "--skip-install", - "--skip-web-ui", - `--outdir=${directory}`, - ], - { cwd: root, env: { ...process.env, OPENCODE_VERSION: input.version }, windowsHide: true }, +export const buildLocalWslCli = Effect.fn("Wsl.buildLocalCli")(function* (input: { + version: string + script: string + output: string +}) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const directory = yield* fs.makeTempDirectory({ prefix: "opencode-wsl-cli-" }) + const build = Effect.gen(function* () { + const root = path.join(path.dirname(input.script), "../../..") + const packageManager = ( + JSON.parse(yield* fs.readFileString(path.join(root, "package.json"))) as { + packageManager: string + } + ).packageManager + const target = `linux-${process.arch}` + yield* Effect.tryPromise(() => + execFileAsync("bunx", [packageManager, "install", "--os=*", "--cpu=*", "--frozen-lockfile"], { + cwd: root, + env: process.env, + windowsHide: true, + }), ) - await copyFile(join(directory, `cli-${target}`, "bin", "opencode2"), input.output) + yield* Effect.tryPromise(() => + execFileAsync( + "bunx", + [ + packageManager, + input.script, + `--target=opencode2-${target}`, + "--skip-install", + "--skip-web-ui", + `--outdir=${directory}`, + ], + { cwd: root, env: { ...process.env, OPENCODE_VERSION: input.version }, windowsHide: true }, + ), + ) + yield* fs.copyFile(path.join(directory, `cli-${target}`, "bin", "opencode2"), input.output) return input.output - } finally { - await rm(directory, { recursive: true, force: true }) - } -} + }) + return yield* build.pipe(Effect.ensuring(fs.remove(directory, { recursive: true, force: true }).pipe(Effect.orDie))) +}) diff --git a/packages/desktop/src/main/wsl/runtime.ts b/packages/desktop/src/main/wsl/runtime.ts index f0d00bbe905..662ad260ec9 100644 --- a/packages/desktop/src/main/wsl/runtime.ts +++ b/packages/desktop/src/main/wsl/runtime.ts @@ -1,8 +1,7 @@ import { spawn } from "node:child_process" -import { existsSync } from "node:fs" -import { join } from "node:path" import * as pty from "@lydell/node-pty" import type { WslDistroProbe, WslInstalledDistro, WslOnlineDistro, WslRuntimeCheck } from "@opencode-ai/app/wsl/types" +import { Effect, FileSystem, Path } from "effect" import { nativeT } from "../native/translations" import { parseCliVersion } from "../service/cli-version" @@ -261,25 +260,35 @@ export async function installWslRuntimeElevated(opts?: RunWslOptions) { requireSuccess(result, nativeT("desktop.wsl.error.installWsl")) } -export async function installWslDistro(distro: string, opts?: RunWslOptions) { - const result = await runInteractiveCommand( - resolveSystem32Command("wsl.exe"), - ["--install", "-d", distro, "--web-download", "--no-launch"], - withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), - DEFAULT_WSL_INSTALL_TIMEOUT_MS, +export const installWslDistro = Effect.fn("Wsl.installDistro")(function* (distro: string, opts?: RunWslOptions) { + const command = yield* resolveSystem32Command("wsl.exe") + const result = yield* Effect.tryPromise(() => + runInteractiveCommand( + command, + ["--install", "-d", distro, "--web-download", "--no-launch"], + withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), + DEFAULT_WSL_INSTALL_TIMEOUT_MS, + ), ) requireSuccess(result, nativeT("desktop.wsl.error.installDistro", { distro })) -} +}) -export async function installWslCli(distro: string, cli: WslCliBuild, opts?: RunWslOptions) { - const result = await runInteractiveCommand( - resolveSystem32Command("wsl.exe"), - wslArgs(["bash", "-lc", wslCliInstallCommand(cli)], distro), - withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), - DEFAULT_WSL_INSTALL_TIMEOUT_MS, +export const installWslCli = Effect.fn("Wsl.installCli")(function* ( + distro: string, + cli: WslCliBuild, + opts?: RunWslOptions, +) { + const command = yield* resolveSystem32Command("wsl.exe") + const result = yield* Effect.tryPromise(() => + runInteractiveCommand( + command, + wslArgs(["bash", "-lc", wslCliInstallCommand(cli)], distro), + withTimeout(opts, DEFAULT_WSL_INSTALL_TIMEOUT_MS), + DEFAULT_WSL_INSTALL_TIMEOUT_MS, + ), ) requireSuccess(result, nativeT("desktop.wsl.error.installOpencode")) -} +}) export function wslCliInstallCommand(cli: WslCliBuild) { const installer = "curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash -s --" @@ -407,12 +416,14 @@ export function shellEscape(value: string) { return `'${value.replace(/'/g, `'"'"'`)}'` } -function resolveSystem32Command(command: string) { +const resolveSystem32Command = Effect.fn("Wsl.resolveSystem32Command")(function* (command: string) { const root = process.env.SystemRoot ?? process.env.windir if (!root) return command - const resolved = join(root, "System32", command) - return existsSync(resolved) ? resolved : command -} + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const resolved = path.join(root, "System32", command) + return (yield* fs.exists(resolved).pipe(Effect.orElseSucceed(() => false))) ? resolved : command +}) function withTimeout(opts: RunWslOptions | undefined, timeoutMs: number): RunWslOptions { return { diff --git a/packages/desktop/src/main/wsl/servers.test.ts b/packages/desktop/src/main/wsl/servers.test.ts index 93593d1598c..476068dc46b 100644 --- a/packages/desktop/src/main/wsl/servers.test.ts +++ b/packages/desktop/src/main/wsl/servers.test.ts @@ -1,5 +1,6 @@ import { expect, test } from "bun:test" import type { WslServerConfig } from "@opencode-ai/app/wsl/types" +import { Effect } from "effect" import { wslCliInstallCommand } from "./runtime" import { createWslServersController } from "./servers" @@ -16,13 +17,15 @@ test("passes a local CLI path directly to the V2 installer", () => { test("installs and verifies the bundled CLI version", async () => { persistedServers = [] const installs: string[][] = [] - const controller = createWslServersController( - testControllerOptions({ - installCli: async (distro, cli) => { - installs.push([distro, cli.version]) - }, - resolveCli: async () => "/home/me/.opencode/bin/opencode2", - }), + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + installCli: async (distro, cli) => { + installs.push([distro, cli.version]) + }, + resolveCli: async () => "/home/me/.opencode/bin/opencode2", + }), + ), ) await controller.installOpencode("Debian") @@ -33,12 +36,14 @@ test("installs and verifies the bundled CLI version", async () => { test("rejects a WSL CLI version that differs from the bundled version", async () => { persistedServers = [] - const controller = createWslServersController( - testControllerOptions({ - installCli: async () => undefined, - resolveCli: async () => "/home/me/.opencode/bin/opencode2", - readCliVersion: async () => "0.0.0-dev-older", - }), + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + installCli: async () => undefined, + resolveCli: async () => "/home/me/.opencode/bin/opencode2", + readCliVersion: async () => "0.0.0-dev-older", + }), + ), ) await expect(controller.installOpencode("Debian")).rejects.toThrow( @@ -49,24 +54,26 @@ test("rejects a WSL CLI version that differs from the bundled version", async () test("stops a running WSL server before replacing its CLI", async () => { persistedServers = [{ id: "wsl:Debian", distro: "Debian" }] const events: string[] = [] - const controller = createWslServersController( - testControllerOptions({ - spawnSidecar: async () => { - events.push("start") - return { - stop: async () => { - events.push("stop") - }, - onExit: () => undefined, - url: "http://127.0.0.1:4096", - username: "opencode", - password: "secret", - } - }, - installCli: async () => { - events.push("install") - }, - }), + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + spawnSidecar: async () => { + events.push("start") + return { + stop: async () => { + events.push("stop") + }, + onExit: () => undefined, + url: "http://127.0.0.1:4096", + username: "opencode", + password: "secret", + } + }, + installCli: async () => { + events.push("install") + }, + }), + ), ) controller.startConfiguredServers() await waitFor(() => controller.getState().servers[0]?.runtime.kind === "ready") @@ -77,24 +84,55 @@ test("stops a running WSL server before replacing its CLI", async () => { await controller.stopServers() }) +test("stops a sidecar that finishes starting after shutdown", async () => { + persistedServers = [{ id: "wsl:Debian", distro: "Debian" }] + const stopped: string[] = [] + let resolveSidecar: ((sidecar: Awaited>) => void) | undefined + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + spawnSidecar: () => new Promise((resolve) => (resolveSidecar = resolve)), + }), + ), + ) + controller.startConfiguredServers() + await waitFor(() => controller.getState().servers[0]?.runtime.kind === "starting") + + await controller.stopServers() + resolveSidecar?.({ + stop: async () => { + stopped.push("stop") + }, + onExit: () => undefined, + url: "http://127.0.0.1:4096", + username: "opencode", + password: "secret", + }) + await waitFor(() => stopped.length === 1) + + expect(stopped).toEqual(["stop"]) +}) + test("probes addable distros in parallel before checking OpenCode", async () => { persistedServers = [] const started: string[] = [] const release = new Map void>() const opencode: string[] = [] - const controller = createWslServersController( - testControllerOptions({ - spawnSidecar: pendingSidecar, - probeDistro: async (distro) => { - started.push(distro) - await new Promise((resolve) => release.set(distro, resolve)) - return { name: distro, canExecute: true, hasBash: true, hasCurl: true, error: null } - }, - resolveCli: async (distro) => { - opencode.push(distro) - return "/home/me/.opencode/bin/opencode2" - }, - }), + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + spawnSidecar: pendingSidecar, + probeDistro: async (distro) => { + started.push(distro) + await new Promise((resolve) => release.set(distro, resolve)) + return { name: distro, canExecute: true, hasBash: true, hasCurl: true, error: null } + }, + resolveCli: async (distro) => { + opencode.push(distro) + return "/home/me/.opencode/bin/opencode2" + }, + }), + ), ) const task = controller.probeAddable(["Debian", "Ubuntu"]) @@ -113,21 +151,23 @@ test("probes addable distros in parallel before checking OpenCode", async () => test("does not check OpenCode in addable distros that cannot execute commands", async () => { persistedServers = [] const opencode: string[] = [] - const controller = createWslServersController( - testControllerOptions({ - spawnSidecar: pendingSidecar, - probeDistro: async (distro) => ({ - name: distro, - canExecute: distro === "Debian", - hasBash: distro === "Debian", - hasCurl: distro === "Debian", - error: distro === "Debian" ? null : "Open Ubuntu once to finish setup", + const controller = await Effect.runPromise( + createWslServersController( + testControllerOptions({ + spawnSidecar: pendingSidecar, + probeDistro: async (distro) => ({ + name: distro, + canExecute: distro === "Debian", + hasBash: distro === "Debian", + hasCurl: distro === "Debian", + error: distro === "Debian" ? null : "Open Ubuntu once to finish setup", + }), + resolveCli: async (distro) => { + opencode.push(distro) + return "/home/me/.opencode/bin/opencode2" + }, }), - resolveCli: async (distro) => { - opencode.push(distro) - return "/home/me/.opencode/bin/opencode2" - }, - }), + ), ) await controller.probeAddable(["Debian", "Ubuntu"]) @@ -148,6 +188,8 @@ async function waitFor(check: () => boolean) { function testControllerOptions(overrides: Partial = {}): ControllerOptions { return { cli: { version: "0.0.0-dev-16365" }, + installCli: async () => undefined, + installDistro: async () => undefined, spawnSidecar: async () => ({ stop: async () => undefined, onExit: () => undefined, diff --git a/packages/desktop/src/main/wsl/servers.ts b/packages/desktop/src/main/wsl/servers.ts index 957237b89a8..da2550314d3 100644 --- a/packages/desktop/src/main/wsl/servers.ts +++ b/packages/desktop/src/main/wsl/servers.ts @@ -8,12 +8,11 @@ import type { WslServersEvent, WslServersState, } from "@opencode-ai/app/wsl/types" +import { Effect } from "effect" import { nativeT } from "../native/translations" import { WSL_SERVERS_KEY } from "../storage/keys" import { getStore } from "../storage/store" import { - installWslCli, - installWslDistro, installWslRuntimeElevated, listInstalledWslDistros, listOnlineWslDistros, @@ -35,33 +34,33 @@ type RunningSidecar = { type SpawnSidecar = (distro: string) => Promise -type ControllerLogger = { - log: (message: string, meta?: unknown) => void - error: (message: string, meta?: unknown) => void -} - type WslServersControllerOptions = { cli: WslCliBuild spawnSidecar: SpawnSidecar - logger?: ControllerLogger + installCli: (distro: string, cli: WslCliBuild) => Promise + installDistro: (distro: string) => Promise readServers?: () => WslServerConfig[] writeServers?: (servers: WslServerConfig[]) => void - installCli?: typeof installWslCli probeDistro?: typeof probeWslDistro resolveCli?: typeof resolveWslCli readCliVersion?: typeof readWslCliVersion } -export type WslServersController = ReturnType +export type WslServersController = Effect.Success> export function wslServerIdForDistro(distro: string) { return `wsl:${distro}` } -export function createWslServersController(options: WslServersControllerOptions) { +export const createWslServersController = Effect.fn("WslServers.make")(function* ( + options: WslServersControllerOptions, +) { + const runFork = Effect.runForkWith(yield* Effect.context()) let state: WslServersState = initialState() const listeners = new Set<(event: WslServersEvent) => void>() const sidecars = new Map() + const starts = new Map() + let closed = false const readServers = options.readServers ?? readPersistedServers const writeServers = options.writeServers ?? writePersistedServers const probeDistro = options.probeDistro ?? probeWslDistro @@ -142,7 +141,7 @@ export function createWslServersController(options: WslServersControllerOptions) const refreshCliCheckSafely = (id: string, distro: string) => { return refreshCliCheck(distro).catch((error) => { const message = error instanceof Error ? error.message : String(error) - options.logger?.error("wsl CLI check failed", { id, distro, message }) + runFork(Effect.logError("wsl CLI check failed", { id, distro, message })) }) } @@ -159,10 +158,18 @@ export function createWslServersController(options: WslServersControllerOptions) const item = state.servers.find((x) => x.config.id === id) if (!item) return await stopServer(id) + if (closed) return + const token = Symbol() + starts.set(id, token) setRuntime(id, { kind: "starting" }) - options.logger?.log("wsl sidecar starting", { id, distro: item.config.distro }) + runFork(Effect.logInfo("wsl sidecar starting", { id, distro: item.config.distro })) try { const sidecar = await options.spawnSidecar(item.config.distro) + if (starts.get(id) !== token) { + await sidecar.stop() + return + } + starts.delete(id) sidecars.set(id, sidecar) setRuntime(id, { kind: "ready", @@ -175,18 +182,21 @@ export function createWslServersController(options: WslServersControllerOptions) sidecars.delete(id) const message = startupFailure(code, signal) setRuntime(id, { kind: "failed", message }) - options.logger?.error("wsl sidecar exited", { id, distro: item.config.distro, code, signal }) + runFork(Effect.logError("wsl sidecar exited", { id, distro: item.config.distro, code, signal })) }) void refreshCliCheckSafely(id, item.config.distro) - options.logger?.log("wsl sidecar ready", { id, distro: item.config.distro, url: sidecar.url }) + runFork(Effect.logInfo("wsl sidecar ready", { id, distro: item.config.distro, url: sidecar.url })) } catch (error) { + if (starts.get(id) !== token) return + starts.delete(id) const message = error instanceof Error ? error.message : String(error) setRuntime(id, { kind: "failed", message }) - options.logger?.error("wsl sidecar failed to start", { id, distro: item.config.distro, message }) + runFork(Effect.logError("wsl sidecar failed to start", { id, distro: item.config.distro, message })) } } const stopServer = async (id: string) => { + starts.delete(id) const existing = sidecars.get(id) if (!existing) return sidecars.delete(id) @@ -213,6 +223,7 @@ export function createWslServersController(options: WslServersControllerOptions) }, startConfiguredServers() { + closed = false refreshFromStore() void refreshCliChecks() state.servers.forEach((item) => void startServer(item.config.id)) @@ -244,7 +255,7 @@ export function createWslServersController(options: WslServersControllerOptions) async installDistro(distro: string) { await runJob({ kind: "install-distro", distro, startedAt: Date.now() }, async () => { - await installWslDistro(distro) + await options.installDistro(distro) const distros = await refreshDistroLists() const probe = await probeDistro(distro) setState({ @@ -263,7 +274,7 @@ export function createWslServersController(options: WslServersControllerOptions) await runJob({ kind: "install-opencode", distro, startedAt: Date.now() }, async () => { const id = state.servers.find((item) => item.config.distro === distro)?.config.id if (id) await stopServer(id) - await (options.installCli ?? installWslCli)(distro, options.cli) + await options.installCli(distro, options.cli) requireMatchingCli(await refreshCliCheck(distro), options.cli.version) if (id) await startServer(id) }) @@ -304,11 +315,13 @@ export function createWslServersController(options: WslServersControllerOptions) startServer, async stopServers() { + closed = true + starts.clear() await Promise.all([...sidecars.values()].map((sidecar) => sidecar.stop())) sidecars.clear() }, } -} +}) function initialState(): WslServersState { return { diff --git a/packages/desktop/src/main/wsl/start.ts b/packages/desktop/src/main/wsl/start.ts index c9eebe88273..d47178960b2 100644 --- a/packages/desktop/src/main/wsl/start.ts +++ b/packages/desktop/src/main/wsl/start.ts @@ -1,47 +1,63 @@ -import { createWslIpc } from "./ipc" +export * as Wsl from "./start" -type Cli = { - version: string - wslBuild?: { script: string; output: string } +import { Context, Effect, Exit, FileSystem, Layer, Path } from "effect" +import { Shutdown } from "../lifecycle/shutdown" +import { DesktopCli } from "../service/desktop-cli" +import { WslIpc } from "./ipc" + +export interface Interface extends WslIpc.Interface { + readonly stop: Effect.Effect } -type Logger = { - log(message: string, meta?: unknown): void - error(message: string, meta?: unknown): void -} +export class Service extends Context.Service()("opencode/desktop/Wsl") {} -export async function startWsl(cli: Cli, logger: Logger) { - if (process.platform !== "win32") return { ipc: createWslIpc(), start: () => {}, stop: async () => {} } +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const desktopCli = yield* DesktopCli.Service + const cli = yield* desktopCli.resolve.pipe(Effect.exit) + const wsl = Exit.isSuccess(cli) ? yield* makeWsl(cli.value) : { ...WslIpc.create(), stop: Effect.void } + const shutdown = yield* Shutdown.Service + const removeShutdown = yield* shutdown.add(wsl.stop) + yield* Effect.addFinalizer(() => Effect.sync(removeShutdown).pipe(Effect.andThen(wsl.stop))) + return Service.of(wsl) + }), +) - const { createWslServersController } = await import("./servers") - const { spawnWslSidecar } = await import("./sidecar") +const makeWsl = Effect.fn("Wsl.make")(function* (cli: DesktopCli.Resolved) { + if (process.platform !== "win32") return { ...WslIpc.create(), stop: Effect.void } + + const { createWslServersController } = yield* Effect.promise(() => import("./servers")) + const { spawnWslSidecar } = yield* Effect.promise(() => import("./sidecar")) + const { installWslCli, installWslDistro } = yield* Effect.promise(() => import("./runtime")) + const context = yield* Effect.context() + const run = Effect.runPromiseWith(context) + const runFork = Effect.runForkWith(context) const local = cli.wslBuild - const controller = createWslServersController({ + const controller = yield* createWslServersController({ cli: { version: cli.version }, + installDistro: (distro) => run(installWslDistro(distro)), installCli: local ? async (distro) => { const { buildLocalWslCli } = await import("./local") - const { installWslCli } = await import("./runtime") - await installWslCli(distro, { - version: cli.version, - binary: await buildLocalWslCli({ ...local, version: cli.version }), - }) + await run( + Effect.gen(function* () { + const binary = yield* buildLocalWslCli({ ...local, version: cli.version }) + yield* installWslCli(distro, { version: cli.version, binary }) + }), + ) } - : undefined, - spawnSidecar: async (distro) => { - logger.log("spawning wsl sidecar", { distro }) + : (distro, build) => run(installWslCli(distro, build)), + spawnSidecar: (distro) => { + runFork(Effect.logInfo("spawning wsl sidecar", { distro })) return spawnWslSidecar(distro, { - onLine: (line) => logger.log("wsl sidecar", { distro, stream: line.stream, text: line.text }), + onLine: (line) => runFork(Effect.logInfo("wsl sidecar", { distro, stream: line.stream, text: line.text })), }) }, - logger: { - log: (message, meta) => logger.log(message, meta), - error: (message, meta) => logger.error(message, meta), - }, }) + controller.startConfiguredServers() return { - ipc: createWslIpc(controller), - start: () => controller.startConfiguredServers(), - stop: () => controller.stopServers(), - } -} + ...WslIpc.create(controller), + stop: Effect.tryPromise(() => controller.stopServers()).pipe(Effect.orDie), + } satisfies Interface +}) diff --git a/packages/desktop/src/preload/index.ts b/packages/desktop/src/preload/index.ts index 772a1ed4a04..ecf427caf9b 100644 --- a/packages/desktop/src/preload/index.ts +++ b/packages/desktop/src/preload/index.ts @@ -1,146 +1,11 @@ import { contextBridge, ipcRenderer, webUtils } from "electron" -import type { IpcRendererEvent } from "electron" -import type { ElectronAPI } from "./types" -import type { UpdaterState } from "@opencode-ai/app/updater" -import { - Ipc, - type IpcEvent, - type IpcEventListener, - type IpcInvoke, - type IpcInvokeArgs, - type IpcInvokeResult, - type IpcSend, -} from "../shared/ipc-contract" +import { IpcTransportPort } from "../shared/ipc-transport" -function invoke(channel: Channel, ...args: IpcInvokeArgs) { - return ipcRenderer.invoke(channel, ...args) as Promise> -} +ipcRenderer.on(IpcTransportPort, (event) => { + const port = event.ports[0] + if (port) window.postMessage(IpcTransportPort, "*", [port]) +}) -function send(channel: Channel, ...args: IpcSend[Channel]) { - ipcRenderer.send(channel, ...args) -} - -function listen(channel: Channel, listener: IpcEventListener) { - const handler = (_event: IpcRendererEvent, ...args: IpcEvent[Channel]) => listener(...args) - ipcRenderer.on(channel, handler) - return () => ipcRenderer.removeListener(channel, handler) -} - -const updaterCallbacks = new Set<(state: UpdaterState) => void>() -let updaterState: UpdaterState | undefined -let updaterSubscription: Promise | undefined -let updaterListener: (() => void) | undefined -const updaterHandler = (state: UpdaterState) => { - updaterState = state - updaterCallbacks.forEach((callback) => callback(state)) -} -type WslInvoke = Exclude< - (typeof Ipc.wsl)[keyof typeof Ipc.wsl], - typeof Ipc.wsl.awaitInitialization | typeof Ipc.wsl.event -> -function invokeWsl(channel: Channel, ...args: IpcInvokeArgs) { - return invoke(Ipc.wsl.awaitInitialization).then(() => invoke(channel, ...args)) -} - -const api: ElectronAPI = { - awaitInitialization: () => invoke(Ipc.app.awaitInitialization), - wslServers: { - getState: () => invokeWsl(Ipc.wsl.getState), - subscribe: (cb) => { - const dispose = listen(Ipc.wsl.event, cb) - const subscribed = invokeWsl(Ipc.wsl.subscribe) - return () => { - dispose() - void subscribed.then(() => invokeWsl(Ipc.wsl.unsubscribe)) - } - }, - probeRuntime: () => invokeWsl(Ipc.wsl.probeRuntime), - refreshDistros: () => invokeWsl(Ipc.wsl.refreshDistros), - installWsl: () => invokeWsl(Ipc.wsl.installWsl), - installDistro: (name) => invokeWsl(Ipc.wsl.installDistro, name), - probeAddable: (distros) => invokeWsl(Ipc.wsl.probeAddable, distros), - installOpencode: (name) => invokeWsl(Ipc.wsl.installOpencode, name), - openTerminal: (name) => invokeWsl(Ipc.wsl.openTerminal, name), - addServer: (distro) => invokeWsl(Ipc.wsl.addServer, distro), - removeServer: (id) => invokeWsl(Ipc.wsl.removeServer, id), - startServer: (id) => invokeWsl(Ipc.wsl.startServer, id), - }, - updater: { - subscribe: async (cb) => { - updaterCallbacks.add(cb) - if (updaterState) cb(updaterState) - if (!updaterSubscription) { - updaterListener = listen(Ipc.updater.state, updaterHandler) - updaterSubscription = invoke(Ipc.updater.subscribe) - } - await updaterSubscription - return () => { - updaterCallbacks.delete(cb) - if (updaterCallbacks.size > 0) return - updaterListener?.() - updaterListener = undefined - updaterSubscription = undefined - void invoke(Ipc.updater.unsubscribe) - } - }, - check: () => invoke(Ipc.updater.check), - install: () => invoke(Ipc.updater.install), - }, - consumeInitialDeepLinks: () => invoke(Ipc.app.consumeInitialDeepLinks), - getDefaultServerUrl: () => invoke(Ipc.app.getDefaultServerUrl), - setDefaultServerUrl: (url) => invoke(Ipc.app.setDefaultServerUrl, url), - isFirstLaunchOnboardingPending: () => invoke(Ipc.app.isFirstLaunchOnboardingPending), - finishFirstLaunchOnboarding: (createDefaultProject) => - invoke(Ipc.app.finishFirstLaunchOnboarding, createDefaultProject), - checkAppExists: (appName) => invoke(Ipc.app.checkAppExists, appName), - resolveAppPath: (appName) => invoke(Ipc.app.resolveAppPath, appName), - storeGet: (name, key) => invoke(Ipc.storage.get, name, key), - storeSet: (name, key, value) => invoke(Ipc.storage.set, name, key, value), - storeDelete: (name, key) => invoke(Ipc.storage.delete, name, key), - storeClear: (name) => invoke(Ipc.storage.clear, name), - storeKeys: (name) => invoke(Ipc.storage.keys, name), - storeLength: (name) => invoke(Ipc.storage.length, name), - draftGet: (key) => invoke(Ipc.drafts.get, key), - draftSet: (key, value) => invoke(Ipc.drafts.set, key, value), - draftDelete: (key) => invoke(Ipc.drafts.delete, key), - draftBlobPut: (data) => invoke(Ipc.drafts.putBlob, data), - draftBlobGet: (id) => invoke(Ipc.drafts.getBlob, id), - - getWindowID: () => invoke(Ipc.window.getId), - themeReady: () => invoke(Ipc.window.themeReady), - onMenuCommand: (cb) => listen(Ipc.menu.command, cb), - onDeepLink: (cb) => listen(Ipc.app.deepLink, cb), - - openDirectoryPicker: (opts) => invoke(Ipc.files.openDirectoryPicker, opts), - openFilePicker: (opts) => invoke(Ipc.files.openFilePicker, opts), - readPickedFile: (token, path) => invoke(Ipc.files.readPickedFile, token, path), - releasePickedFiles: (token) => invoke(Ipc.files.releasePickedFiles, token), - getPathForFile: (file) => webUtils.getPathForFile(file), - saveFilePicker: (opts) => invoke(Ipc.files.saveFilePicker, opts), - openExternal: (url) => send(Ipc.files.openExternal, url), - openLocalFile: (url) => send(Ipc.files.openLocalFile, url), - openPath: (path, app) => invoke(Ipc.files.openPath, path, app), - revealPath: (path) => invoke(Ipc.files.revealPath, path), - readClipboardImage: () => invoke(Ipc.files.readClipboardImage), - getWindowFocused: () => invoke(Ipc.window.getFocused), - getWindowFullscreen: () => invoke(Ipc.window.getFullscreen), - onWindowFullscreenChanged: (cb) => listen(Ipc.window.fullscreenChanged, cb), - setWindowFocus: () => invoke(Ipc.window.setFocus), - showWindow: () => invoke(Ipc.window.show), - relaunch: () => send(Ipc.app.relaunch), - getZoomFactor: () => invoke(Ipc.window.getZoomFactor), - setZoomFactor: (factor) => invoke(Ipc.window.setZoomFactor, factor), - getPinchZoomEnabled: () => invoke(Ipc.window.getPinchZoomEnabled), - setPinchZoomEnabled: (enabled) => invoke(Ipc.window.setPinchZoomEnabled, enabled), - onPinchZoomEnabledChanged: (cb) => listen(Ipc.window.pinchZoomEnabledChanged, cb), - onZoomFactorChanged: (cb) => listen(Ipc.window.zoomFactorChanged, cb), - setTitlebar: (theme) => invoke(Ipc.window.setTitlebar, theme), - runDesktopMenuAction: (action) => invoke(Ipc.menu.runAction, action), - setBackgroundColor: (color) => invoke(Ipc.app.setBackgroundColor, color), - exportDebugLogs: () => invoke(Ipc.app.exportDebugLogs), - setForceFocus: (enabled) => invoke(Ipc.app.setForceFocus, enabled), - recordFatalRendererError: (error) => invoke(Ipc.app.recordFatalRendererError, error), - setNativeTranslations: (bundle) => invoke(Ipc.app.setNativeTranslations, bundle), -} - -contextBridge.exposeInMainWorld("api", api) +contextBridge.exposeInMainWorld("electron", { + getPathForFile: (file: File) => webUtils.getPathForFile(file), +}) diff --git a/packages/desktop/src/preload/types.ts b/packages/desktop/src/preload/types.ts index a8856f70d51..9fb67648c39 100644 --- a/packages/desktop/src/preload/types.ts +++ b/packages/desktop/src/preload/types.ts @@ -1,75 +1,3 @@ -import type { WslServersPlatform } from "@opencode-ai/app/wsl/types" -import { - Ipc, - type IpcEventListener, - type IpcEventSubscription, - type IpcInvokeMethod, - type IpcSendMethod, -} from "../shared/ipc-contract" - -export type WslServersAPI = WslServersPlatform -export type UpdaterAPI = { - subscribe: (cb: IpcEventListener) => Promise<() => void> - check: IpcInvokeMethod - install: IpcInvokeMethod -} - -export type ElectronAPI = { - awaitInitialization: IpcInvokeMethod - wslServers: WslServersAPI - updater: UpdaterAPI - consumeInitialDeepLinks: IpcInvokeMethod - getDefaultServerUrl: IpcInvokeMethod - setDefaultServerUrl: IpcInvokeMethod - isFirstLaunchOnboardingPending: IpcInvokeMethod - finishFirstLaunchOnboarding: IpcInvokeMethod - checkAppExists: IpcInvokeMethod - resolveAppPath: IpcInvokeMethod - storeGet: IpcInvokeMethod - storeSet: IpcInvokeMethod - storeDelete: IpcInvokeMethod - storeClear: IpcInvokeMethod - storeKeys: IpcInvokeMethod - storeLength: IpcInvokeMethod - draftGet: IpcInvokeMethod - draftSet: IpcInvokeMethod - draftDelete: IpcInvokeMethod - draftBlobPut: IpcInvokeMethod - draftBlobGet: IpcInvokeMethod - - getWindowID: IpcInvokeMethod - themeReady: IpcInvokeMethod - onMenuCommand: IpcEventSubscription - onDeepLink: IpcEventSubscription - - openDirectoryPicker: IpcInvokeMethod - openFilePicker: IpcInvokeMethod - readPickedFile: IpcInvokeMethod - releasePickedFiles: IpcInvokeMethod - getPathForFile: (file: File) => string - saveFilePicker: IpcInvokeMethod - openExternal: IpcSendMethod - openLocalFile: IpcSendMethod - openPath: IpcInvokeMethod - revealPath: IpcInvokeMethod - readClipboardImage: IpcInvokeMethod - getWindowFocused: IpcInvokeMethod - getWindowFullscreen: IpcInvokeMethod - onWindowFullscreenChanged: IpcEventSubscription - setWindowFocus: IpcInvokeMethod - showWindow: IpcInvokeMethod - relaunch: IpcSendMethod - getZoomFactor: IpcInvokeMethod - setZoomFactor: IpcInvokeMethod - getPinchZoomEnabled: IpcInvokeMethod - setPinchZoomEnabled: IpcInvokeMethod - onPinchZoomEnabledChanged: IpcEventSubscription - onZoomFactorChanged: IpcEventSubscription - setTitlebar: IpcInvokeMethod - runDesktopMenuAction: IpcInvokeMethod - setBackgroundColor: IpcInvokeMethod - exportDebugLogs: IpcInvokeMethod - setForceFocus: IpcInvokeMethod - recordFatalRendererError: IpcInvokeMethod - setNativeTranslations: IpcInvokeMethod +export type ElectronNative = { + getPathForFile(file: File): string } diff --git a/packages/desktop/src/renderer/api-types.ts b/packages/desktop/src/renderer/api-types.ts new file mode 100644 index 00000000000..53c0f21c25f --- /dev/null +++ b/packages/desktop/src/renderer/api-types.ts @@ -0,0 +1,79 @@ +import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu" +import type { DesktopNativeBundle } from "@opencode-ai/app/i18n/desktop-native" +import type { UpdaterState } from "@opencode-ai/app/updater" +import type { WslServersPlatform } from "@opencode-ai/app/wsl/types" +import type { + ClipboardImage, + DirectoryPickerOptions, + FatalRendererError, + FilePickerOptions, + PickedFiles, + SaveFilePickerOptions, + ServerReadyData, + TitlebarTheme, +} from "../shared/ipc-contract" + +export type WslServersAPI = WslServersPlatform +export type UpdaterAPI = { + subscribe(cb: (state: UpdaterState) => void): Promise<() => void> + check(): Promise + install(): Promise +} + +export type ElectronAPI = { + awaitInitialization(): Promise + wslServers: WslServersAPI + updater: UpdaterAPI + consumeInitialDeepLinks(): Promise + getDefaultServerUrl(): Promise + setDefaultServerUrl(url: string | null): Promise + isFirstLaunchOnboardingPending(): Promise + finishFirstLaunchOnboarding(createDefaultProject: boolean): Promise + checkAppExists(appName: string): Promise + resolveAppPath(appName: string): Promise + storeGet(name: string, key: string): Promise + storeSet(name: string, key: string, value: string): Promise + storeDelete(name: string, key: string): Promise + storeClear(name: string): Promise + storeKeys(name: string): Promise + storeLength(name: string): Promise + draftGet(key: string): Promise + draftSet(key: string, value: string): Promise + draftDelete(key: string): Promise + draftBlobPut(data: ArrayBuffer): Promise + draftBlobGet(id: string): Promise + getWindowID(): Promise + themeReady(): Promise + onMenuCommand(cb: (id: string) => void): () => void + onDeepLink(cb: (urls: string[]) => void): () => void + openDirectoryPicker(opts?: DirectoryPickerOptions): Promise + openFilePicker(opts?: FilePickerOptions): Promise + readPickedFile(token: string, path: string): Promise + releasePickedFiles(token: string): Promise + getPathForFile(file: File): string + saveFilePicker(opts?: SaveFilePickerOptions): Promise + openExternal(url: string): void + openLocalFile(url: string): void + openPath(path: string, app?: string): Promise + revealPath(path: string): Promise + readClipboardImage(): Promise + getWindowFocused(): Promise + getWindowFullscreen(): Promise + onWindowFullscreenChanged(cb: (fullscreen: boolean) => void): () => void + setWindowFocus(): Promise + showWindow(): Promise + relaunch(): void + getZoomFactor(): Promise + setZoomFactor(factor: number): Promise + getPinchZoomEnabled(): Promise + setPinchZoomEnabled(enabled: boolean): Promise + onPinchZoomEnabledChanged(cb: (enabled: boolean) => void): () => void + onZoomFactorChanged(cb: (factor: number) => void): () => void + setTitlebar(theme: TitlebarTheme): Promise + runDesktopMenuAction(action: DesktopMenuAction): Promise + setBackgroundColor(color: string): Promise + exportDebugLogs(): Promise + setForceFocus(enabled: boolean): Promise + recordFatalRendererError(error: FatalRendererError): Promise + setNativeTranslations(bundle: DesktopNativeBundle): Promise +} diff --git a/packages/desktop/src/renderer/api.ts b/packages/desktop/src/renderer/api.ts new file mode 100644 index 00000000000..be52c29dc80 --- /dev/null +++ b/packages/desktop/src/renderer/api.ts @@ -0,0 +1,127 @@ +import type { ElectronAPI } from "./api-types" +import type { UpdaterState } from "@opencode-ai/app/updater" +import { invoke, listen, send } from "./ipc-client" + +type Mutable = + Value extends ReadonlyArray + ? { -readonly [Key in keyof Value]: Mutable } + : Value extends object + ? { -readonly [Key in keyof Value]: Mutable } + : Value + +const mutable = (value: Value) => value as Mutable +const toArrayBuffer = (value: Uint8Array) => + value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength) as ArrayBuffer + +const updaterCallbacks = new Set<(state: UpdaterState) => void>() +let updaterState: UpdaterState | undefined +let updaterSubscription: Promise | undefined +let updaterListener: (() => void) | undefined +const updaterHandler = (state: UpdaterState) => { + updaterState = state + updaterCallbacks.forEach((callback) => callback(state)) +} + +export const api: ElectronAPI = { + awaitInitialization: () => invoke("AppAwaitInitialization"), + wslServers: { + getState: () => invoke("WslGetState").then(mutable), + subscribe: (cb) => { + const dispose = listen("WslServersChanged", (event) => cb(mutable(event.event))) + void invoke("WslSubscribe") + return () => { + dispose() + void invoke("WslUnsubscribe") + } + }, + probeRuntime: () => invoke("WslProbeRuntime"), + refreshDistros: () => invoke("WslRefreshDistros"), + installWsl: () => invoke("WslInstallWsl"), + installDistro: (name) => invoke("WslInstallDistro", { name }), + probeAddable: (distros) => invoke("WslProbeAddable", { distros }), + installOpencode: (name) => invoke("WslInstallOpencode", { name }), + openTerminal: (name) => invoke("WslOpenTerminal", { name }), + addServer: (distro) => invoke("WslAddServer", { distro }), + removeServer: (id) => invoke("WslRemoveServer", { id }), + startServer: (id) => invoke("WslStartServer", { id }), + }, + updater: { + subscribe: async (cb) => { + updaterCallbacks.add(cb) + if (updaterState) cb(updaterState) + if (!updaterSubscription) { + updaterListener = listen("UpdaterStateChanged", (event) => updaterHandler(mutable(event.state))) + updaterSubscription = invoke("UpdaterSubscribe") + } + await updaterSubscription + return () => { + updaterCallbacks.delete(cb) + if (updaterCallbacks.size > 0) return + updaterListener?.() + updaterListener = undefined + updaterSubscription = undefined + void invoke("UpdaterUnsubscribe") + } + }, + check: () => invoke("UpdaterCheck"), + install: () => invoke("UpdaterInstall"), + }, + consumeInitialDeepLinks: () => invoke("AppConsumeInitialDeepLinks").then(mutable), + getDefaultServerUrl: () => invoke("AppGetDefaultServerUrl"), + setDefaultServerUrl: (url) => invoke("AppSetDefaultServerUrl", { url }), + isFirstLaunchOnboardingPending: () => invoke("AppIsFirstLaunchOnboardingPending"), + finishFirstLaunchOnboarding: (createDefaultProject) => + invoke("AppFinishFirstLaunchOnboarding", { createDefaultProject }), + checkAppExists: (appName) => invoke("AppCheckAppExists", { appName }), + resolveAppPath: (appName) => invoke("AppResolveAppPath", { appName }), + storeGet: (name, key) => invoke("StorageGet", { name, key }), + storeSet: (name, key, value) => invoke("StorageSet", { name, key, value }), + storeDelete: (name, key) => invoke("StorageDelete", { name, key }), + storeClear: (name) => invoke("StorageClear", { name }), + storeKeys: (name) => invoke("StorageKeys", { name }).then(mutable), + storeLength: (name) => invoke("StorageLength", { name }), + draftGet: (key) => invoke("DraftsGet", { key }), + draftSet: (key, value) => invoke("DraftsSet", { key, value }), + draftDelete: (key) => invoke("DraftsDelete", { key }), + draftBlobPut: (data) => invoke("DraftsPutBlob", { data: new Uint8Array(data) }), + draftBlobGet: (id) => invoke("DraftsGetBlob", { id }).then((data) => (data ? toArrayBuffer(data) : null)), + + getWindowID: () => invoke("WindowGetId"), + themeReady: () => invoke("WindowThemeReady"), + onMenuCommand: (cb) => listen("MenuCommandTriggered", (event) => cb(event.id)), + onDeepLink: (cb) => listen("DeepLinksOpened", (event) => cb(mutable(event.urls))), + + openDirectoryPicker: (opts) => invoke("FilesOpenDirectoryPicker", { options: opts }).then(mutable), + openFilePicker: (opts) => invoke("FilesOpenFilePicker", { options: opts }).then(mutable), + readPickedFile: (token, path) => invoke("FilesReadPickedFile", { token, path }).then(toArrayBuffer), + releasePickedFiles: (token) => invoke("FilesReleasePickedFiles", { token }), + getPathForFile: (file) => window.electron.getPathForFile(file), + saveFilePicker: (opts) => invoke("FilesSaveFilePicker", { options: opts }), + openExternal: (url) => send("FilesOpenExternal", { url }), + openLocalFile: (url) => send("FilesOpenLocalFile", { url }), + openPath: (path, app) => invoke("FilesOpenPath", { path, application: app }).then((value) => value ?? undefined), + revealPath: (path) => invoke("FilesRevealPath", { path }), + readClipboardImage: () => + invoke("FilesReadClipboardImage").then((image) => + image ? { ...image, buffer: toArrayBuffer(image.buffer) } : null, + ), + getWindowFocused: () => invoke("WindowGetFocused"), + getWindowFullscreen: () => invoke("WindowGetFullscreen"), + onWindowFullscreenChanged: (cb) => listen("WindowFullscreenChanged", (event) => cb(event.fullscreen)), + setWindowFocus: () => invoke("WindowSetFocus"), + showWindow: () => invoke("WindowShow"), + relaunch: () => send("AppRelaunch"), + getZoomFactor: () => invoke("WindowGetZoomFactor"), + setZoomFactor: (factor) => invoke("WindowSetZoomFactor", { factor }), + getPinchZoomEnabled: () => invoke("WindowGetPinchZoomEnabled"), + setPinchZoomEnabled: (enabled) => invoke("WindowSetPinchZoomEnabled", { enabled }), + onPinchZoomEnabledChanged: (cb) => listen("WindowPinchZoomChanged", (event) => cb(event.enabled)), + onZoomFactorChanged: (cb) => listen("WindowZoomChanged", (event) => cb(event.factor)), + setTitlebar: (theme) => invoke("WindowSetTitlebar", { theme }), + runDesktopMenuAction: (action) => invoke("MenuRunAction", { action }), + setBackgroundColor: (color) => invoke("AppSetBackgroundColor", { color }), + exportDebugLogs: () => invoke("AppExportDebugLogs"), + setForceFocus: (enabled) => invoke("AppSetForceFocus", { enabled }), + recordFatalRendererError: (error) => invoke("AppRecordFatalRendererError", { error }), + setNativeTranslations: (bundle) => invoke("AppSetNativeTranslations", { value: bundle }), +} diff --git a/packages/desktop/src/renderer/desktop-app.tsx b/packages/desktop/src/renderer/desktop-app.tsx index cdbe0cd439c..f278eac3f3d 100644 --- a/packages/desktop/src/renderer/desktop-app.tsx +++ b/packages/desktop/src/renderer/desktop-app.tsx @@ -5,7 +5,6 @@ import { AppBaseProviders, AppInterface, PlatformProvider, - preloadRoute, ServerConnection, useCommand, useLanguage, @@ -15,7 +14,7 @@ import { import { useTheme } from "@opencode-ai/ui/theme/context" import type { BaseRouterProps } from "@solidjs/router" import { createEffect, createMemo, createResource, lazy, Show, Suspense } from "solid-js" -import type { ElectronAPI } from "../preload/types" +import type { ElectronAPI } from "./api-types" import { DesktopFirstLaunchOnboarding } from "./onboarding" import { createDesktopPlatform, type DesktopWindowState } from "./platform" import { bindDesktopMenu } from "./platform/menu" @@ -39,11 +38,9 @@ export function DesktopApp(props: { api: ElectronAPI; updater: UpdaterPlatform; function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; windowState: DesktopWindowState }) { const platform = createDesktopPlatform(props.api, props.windowState, props.updater) - const initialUrl = getLastActiveUrl(props.windowState.id) const [sidecar] = createResource(() => props.api.awaitInitialization()) const [defaultServer] = createResource(() => platform.getDefaultServer?.()) const [locale] = createResource(() => preloadStoredLocale(platform)) - const [route] = createResource(() => preloadRoute(initialUrl)) const router = (routerProps: BaseRouterProps) => ( ) @@ -51,7 +48,9 @@ function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; wind function ReadyApp() { const wslServers = useWslServers() const language = useLanguage() - const ready = createMemo(() => !defaultServer.loading && !sidecar.loading && !locale.loading && !route.loading) + const ready = createMemo( + () => !defaultServer.loading && !sidecar.loading && !locale.loading && !wslServers.isLoading, + ) const servers = createMemo(() => { const data = initializationData(sidecar) const list: ServerConnection.Any[] = [] @@ -81,7 +80,7 @@ function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; wind diff --git a/packages/desktop/src/renderer/env.d.ts b/packages/desktop/src/renderer/env.d.ts index 6dff3baf1c4..eca4134d9b0 100644 --- a/packages/desktop/src/renderer/env.d.ts +++ b/packages/desktop/src/renderer/env.d.ts @@ -1,8 +1,8 @@ -import type { ElectronAPI } from "../preload/types" +import type { ElectronNative } from "../preload/types" declare global { interface Window { - api: ElectronAPI + electron: ElectronNative __OPENCODE__?: { deepLinks?: string[] } diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx index 1ab0e33f8ee..d09ac286e54 100644 --- a/packages/desktop/src/renderer/index.tsx +++ b/packages/desktop/src/renderer/index.tsx @@ -3,6 +3,7 @@ import "./diagnostics" import "./styles.css" import { render } from "solid-js/web" +import { api } from "./api" import { DesktopApp } from "./desktop-app" import { startDesktopMenu } from "./platform/menu" import { startDesktopUpdater } from "./platform/updater" @@ -14,8 +15,8 @@ const root = requireRendererRoot() const version = desktopVersion() await initializeSentry(version) -const updater = startDesktopUpdater(window.api) -startDesktopMenu(window.api) -startDeepLinks(window.api) +const updater = startDesktopUpdater(api) +startDesktopMenu(api) +startDeepLinks(api) -render(() => , root) +render(() => , root) diff --git a/packages/desktop/src/renderer/ipc-client.ts b/packages/desktop/src/renderer/ipc-client.ts new file mode 100644 index 00000000000..e1e98996723 --- /dev/null +++ b/packages/desktop/src/renderer/ipc-client.ts @@ -0,0 +1,114 @@ +import { Context, Effect, Layer, ManagedRuntime, Queue, Stream } from "effect" +import { RpcClient, RpcMessage, RpcSerialization } from "effect/unstable/rpc" +import { DesktopRpcs, type DesktopRpcClient } from "../shared/ipc-rpc" +import type { DesktopEvent } from "../shared/ipc-rpc/events" +import { IpcTransportPort } from "../shared/ipc-transport" + +class DesktopClient extends Context.Service()("opencode/desktop/DesktopClient") {} + +type EventTag = DesktopEvent["_tag"] +type InvokeTag = Exclude +type InvokeArgs = Parameters +type InvokeResult = + ReturnType extends Effect.Effect ? Value : never +type EventValue = Extract + +const port = new Promise((resolve) => { + const onMessage = (event: MessageEvent) => { + if (event.source !== window || event.data !== IpcTransportPort) return + const value = event.ports[0] + if (!value) return + window.removeEventListener("message", onMessage) + resolve(value) + } + window.addEventListener("message", onMessage) +}) + +const ClientProtocolLive = Layer.unwrap(Effect.promise(() => port).pipe(Effect.map((value) => clientProtocol(value)))) +const ClientLive = Layer.effect(DesktopClient, RpcClient.make(DesktopRpcs)).pipe(Layer.provide(ClientProtocolLive)) +const runtime = ManagedRuntime.make(ClientLive) +const listeners = new Map void>>() +window.addEventListener("pagehide", () => void runtime.dispose(), { once: true }) + +runtime.runFork( + Effect.gen(function* () { + const client = yield* DesktopClient + yield* client + .DesktopEvents() + .pipe( + Stream.runForEach((event) => + Effect.sync(() => listeners.get(event._tag)?.forEach((listener) => listener(event))), + ), + ) + }), +) + +export function invoke(tag: Tag, ...payload: InvokeArgs): Promise> { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* DesktopClient + const method = client[tag] as unknown as (...args: ReadonlyArray) => Effect.Effect + return yield* method(...payload) + }), + ) as Promise> +} + +export function send(tag: Tag, ...payload: InvokeArgs) { + void invoke(tag, ...payload).catch(() => undefined) +} + +export function listen(tag: Tag, listener: (value: EventValue) => void) { + const callback = listener as (value: unknown) => void + const callbacks = listeners.get(tag) ?? new Set() + callbacks.add(callback) + listeners.set(tag, callbacks) + return () => { + callbacks.delete(callback) + if (callbacks.size === 0) listeners.delete(tag) + } +} + +function clientProtocol(value: MessagePort) { + return Layer.effect( + RpcClient.Protocol, + RpcClient.Protocol.make( + Effect.fnUntraced(function* (writeResponse, clientIds) { + const serialization = yield* RpcSerialization.RpcSerialization + const parser = serialization.makeUnsafe() + const inbound = yield* Queue.unbounded() + const onMessage = (event: MessageEvent) => { + try { + parser + .decode(event.data) + .forEach((message) => Queue.offerUnsafe(inbound, message as RpcMessage.FromServerEncoded)) + } catch { + return + } + } + value.addEventListener("message", onMessage) + value.start() + yield* Effect.addFinalizer(() => + Effect.sync(() => { + value.removeEventListener("message", onMessage) + value.close() + }), + ) + yield* Stream.fromQueue(inbound).pipe( + Stream.runForEach((message) => + Effect.forEach(clientIds, (clientId) => writeResponse(clientId, message), { discard: true }), + ), + Effect.forkScoped, + ) + return { + send: (_clientId, request) => + Effect.sync(() => { + const encoded = parser.encode(request) + if (encoded !== undefined) value.postMessage(encoded) + }), + supportsAck: true, + supportsTransferables: false, + } + }), + ), + ).pipe(Layer.provide(RpcSerialization.layerMsgPack)) +} diff --git a/packages/desktop/src/renderer/onboarding/index.tsx b/packages/desktop/src/renderer/onboarding/index.tsx index f86efce688d..c890f15ff9f 100644 --- a/packages/desktop/src/renderer/onboarding/index.tsx +++ b/packages/desktop/src/renderer/onboarding/index.tsx @@ -1,6 +1,6 @@ import { ServerConnection, useServers, useTabs } from "@opencode-ai/app/desktop" import { onMount } from "solid-js" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" export function DesktopFirstLaunchOnboarding(props: { api: ElectronAPI diff --git a/packages/desktop/src/renderer/platform/files.ts b/packages/desktop/src/renderer/platform/files.ts index 7b1962d5091..ff07ade3fc8 100644 --- a/packages/desktop/src/renderer/platform/files.ts +++ b/packages/desktop/src/renderer/platform/files.ts @@ -1,5 +1,5 @@ import type { Platform } from "@opencode-ai/app" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" type DesktopOS = Extract["os"] type DesktopFileAPI = Pick< diff --git a/packages/desktop/src/renderer/platform/index.ts b/packages/desktop/src/renderer/platform/index.ts index 3abc6c9f124..b9e95784f0c 100644 --- a/packages/desktop/src/renderer/platform/index.ts +++ b/packages/desktop/src/renderer/platform/index.ts @@ -1,5 +1,5 @@ import { ACCEPTED_FILE_EXTENSIONS, ServerConnection, type Platform, type UpdaterPlatform } from "@opencode-ai/app" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" import { setPinchZoomEnabled, webviewZoom } from "../window/zoom" import { windowFullscreen } from "../window/fullscreen" import { createDesktopFiles } from "./files" diff --git a/packages/desktop/src/renderer/platform/menu.ts b/packages/desktop/src/renderer/platform/menu.ts index 78698c64020..6256375b872 100644 --- a/packages/desktop/src/renderer/platform/menu.ts +++ b/packages/desktop/src/renderer/platform/menu.ts @@ -1,5 +1,5 @@ import type { Platform } from "@opencode-ai/app" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" import { resetZoom, zoomIn, zoomOut } from "../window/zoom" let trigger: ((id: string) => void) | null = null diff --git a/packages/desktop/src/renderer/platform/notifications.ts b/packages/desktop/src/renderer/platform/notifications.ts index b293de15db6..aecf3c528ed 100644 --- a/packages/desktop/src/renderer/platform/notifications.ts +++ b/packages/desktop/src/renderer/platform/notifications.ts @@ -1,5 +1,5 @@ import type { Platform } from "@opencode-ai/app" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" export function createDesktopNotify(api: ElectronAPI): Platform["notify"] { return async (title, description, onClick) => { diff --git a/packages/desktop/src/renderer/platform/storage.ts b/packages/desktop/src/renderer/platform/storage.ts index b3845eb018c..4e7084e7fb2 100644 --- a/packages/desktop/src/renderer/platform/storage.ts +++ b/packages/desktop/src/renderer/platform/storage.ts @@ -1,6 +1,6 @@ import { createDraftStore, type Platform } from "@opencode-ai/app" import type { AsyncStorage } from "@solid-primitives/storage" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" export function createDesktopStorage(api: ElectronAPI) { const cache = new Map() diff --git a/packages/desktop/src/renderer/platform/updater.ts b/packages/desktop/src/renderer/platform/updater.ts index 6dfb9f4a870..85ecceda2a0 100644 --- a/packages/desktop/src/renderer/platform/updater.ts +++ b/packages/desktop/src/renderer/platform/updater.ts @@ -1,6 +1,6 @@ import type { UpdaterPlatform, UpdaterState } from "@opencode-ai/app" import { createSignal } from "solid-js" -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" export function startDesktopUpdater(api: ElectronAPI): UpdaterPlatform { const [state, setState] = createSignal({ status: "disabled" }) diff --git a/packages/desktop/src/renderer/startup/deep-links.ts b/packages/desktop/src/renderer/startup/deep-links.ts index 3d33f356cdd..fff01a8413f 100644 --- a/packages/desktop/src/renderer/startup/deep-links.ts +++ b/packages/desktop/src/renderer/startup/deep-links.ts @@ -1,4 +1,4 @@ -import type { ElectronAPI } from "../../preload/types" +import type { ElectronAPI } from "../api-types" const deepLinkEvent = "opencode:deep-link" diff --git a/packages/desktop/src/renderer/startup/initialization.test.ts b/packages/desktop/src/renderer/startup/initialization.test.ts index fc4bd197e1d..2fb945ed3b5 100644 --- a/packages/desktop/src/renderer/startup/initialization.test.ts +++ b/packages/desktop/src/renderer/startup/initialization.test.ts @@ -1,5 +1,4 @@ import { describe, expect, test } from "bun:test" -import { Ipc } from "../../shared/ipc-contract" import { initializationData, initializationReady } from "./initialization" describe("desktop renderer initialization", () => { @@ -15,10 +14,8 @@ describe("desktop renderer initialization", () => { } }) - test("removes Electron's remote invocation wrapper from startup errors", () => { - const error = new Error( - `Error invoking remote method '${Ipc.app.awaitInitialization}': Error: Cannot migrate session_message projections`, - ) + test("preserves clean RPC startup errors", () => { + const error = new Error("Cannot migrate session_message projections") try { initializationData(Object.assign(() => undefined, { error })) diff --git a/packages/desktop/src/renderer/startup/initialization.ts b/packages/desktop/src/renderer/startup/initialization.ts index 16a0af1255e..92f5c34603c 100644 --- a/packages/desktop/src/renderer/startup/initialization.ts +++ b/packages/desktop/src/renderer/startup/initialization.ts @@ -5,12 +5,6 @@ export function initializationData(state: (() => A | undefined) & { error: un function markLocalServerStartup(error: unknown) { const failure = error instanceof Error ? error : new Error(String(error)) - const prefix = `Error invoking remote method '${Ipc.app.awaitInitialization}': Error: ` - if (failure.message.startsWith(prefix)) { - const previous = failure.message - failure.message = failure.message.slice(prefix.length) - if (failure.stack) failure.stack = failure.stack.replace(`Error: ${previous}`, `Error: ${failure.message}`) - } Object.defineProperty(failure, "localServerStartup", { value: true }) return failure } @@ -20,4 +14,3 @@ export function initializationReady(state: (() => A | undefined) & { error: u initializationData(state) return true } -import { Ipc } from "../../shared/ipc-contract" diff --git a/packages/desktop/src/renderer/window/fullscreen.ts b/packages/desktop/src/renderer/window/fullscreen.ts index 95d91fa2e7e..ec7e152d55b 100644 --- a/packages/desktop/src/renderer/window/fullscreen.ts +++ b/packages/desktop/src/renderer/window/fullscreen.ts @@ -1,8 +1,9 @@ import { createSignal } from "solid-js" +import { api } from "../api" const [windowFullscreen, setWindowFullscreen] = createSignal(false) -window.api.onWindowFullscreenChanged(setWindowFullscreen) -void window.api.getWindowFullscreen().then(setWindowFullscreen) +api.onWindowFullscreenChanged(setWindowFullscreen) +void api.getWindowFullscreen().then(setWindowFullscreen) export { windowFullscreen } diff --git a/packages/desktop/src/renderer/window/zoom.ts b/packages/desktop/src/renderer/window/zoom.ts index 7993bf5ee23..4babed8a86d 100644 --- a/packages/desktop/src/renderer/window/zoom.ts +++ b/packages/desktop/src/renderer/window/zoom.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT import { createSignal } from "solid-js" +import { api } from "../api" const OS_NAME = (() => { if (navigator.userAgent.includes("Mac")) return "macos" @@ -33,7 +34,7 @@ const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_Z const applyZoom = (next: number) => { requestedZoom = next - void window.api + void api .setZoomFactor(next) .then(() => { if (requestedZoom !== next) return @@ -45,16 +46,16 @@ const applyZoom = (next: number) => { }) } -window.api.onZoomFactorChanged((factor) => { +api.onZoomFactorChanged((factor) => { requestedZoom = clamp(factor) setWebviewZoom(requestedZoom) }) -void window.api.getPinchZoomEnabled().then((enabled) => { +void api.getPinchZoomEnabled().then((enabled) => { pinchZoomEnabled = enabled }) -window.api.onPinchZoomEnabledChanged((enabled) => { +api.onPinchZoomEnabledChanged((enabled) => { pinchZoomEnabled = enabled resetWheelPinch() }) @@ -62,7 +63,7 @@ window.api.onPinchZoomEnabledChanged((enabled) => { const setPinchZoomEnabled = (enabled: boolean) => { pinchZoomEnabled = enabled resetWheelPinch() - return window.api.setPinchZoomEnabled(enabled) + return api.setPinchZoomEnabled(enabled) } const resetZoom = () => applyZoom(1) diff --git a/packages/desktop/src/shared/ipc-contract.ts b/packages/desktop/src/shared/ipc-contract.ts index 729a629ea60..e10ba9e1d38 100644 --- a/packages/desktop/src/shared/ipc-contract.ts +++ b/packages/desktop/src/shared/ipc-contract.ts @@ -1,99 +1,3 @@ -import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu" -import type { DesktopNativeBundle } from "@opencode-ai/app/i18n/desktop-native" -import type { UpdaterState } from "@opencode-ai/app/updater" -import type { WslServerConfig, WslServersEvent, WslServersState } from "@opencode-ai/app/wsl/types" - -export const Ipc = { - app: { - awaitInitialization: "await-initialization", - consumeInitialDeepLinks: "consume-initial-deep-links", - deepLink: "deep-link", - getDefaultServerUrl: "get-default-server-url", - setDefaultServerUrl: "set-default-server-url", - isFirstLaunchOnboardingPending: "is-first-launch-onboarding-pending", - finishFirstLaunchOnboarding: "finish-first-launch-onboarding", - checkAppExists: "check-app-exists", - resolveAppPath: "resolve-app-path", - relaunch: "relaunch", - setBackgroundColor: "set-background-color", - exportDebugLogs: "export-debug-logs", - setForceFocus: "set-force-focus", - recordFatalRendererError: "record-fatal-renderer-error", - setNativeTranslations: "set-native-translations", - }, - storage: { - get: "store-get", - set: "store-set", - delete: "store-delete", - clear: "store-clear", - keys: "store-keys", - length: "store-length", - }, - drafts: { - get: "draft-get", - set: "draft-set", - delete: "draft-delete", - putBlob: "draft-blob-put", - getBlob: "draft-blob-get", - }, - files: { - openDirectoryPicker: "open-directory-picker", - openFilePicker: "open-file-picker", - readPickedFile: "read-picked-file", - releasePickedFiles: "release-picked-files", - saveFilePicker: "save-file-picker", - openExternal: "open-external", - openLocalFile: "open-local-file", - openPath: "open-path", - revealPath: "reveal-path", - readClipboardImage: "read-clipboard-image", - }, - window: { - getId: "get-window-id", - themeReady: "window-theme-ready", - getFocused: "get-window-focused", - getFullscreen: "get-window-fullscreen", - fullscreenChanged: "window-fullscreen-changed", - setFocus: "set-window-focus", - show: "show-window", - getZoomFactor: "get-zoom-factor", - setZoomFactor: "set-zoom-factor", - zoomFactorChanged: "zoom-factor-changed", - getPinchZoomEnabled: "get-pinch-zoom-enabled", - setPinchZoomEnabled: "set-pinch-zoom-enabled", - pinchZoomEnabledChanged: "pinch-zoom-enabled-changed", - setTitlebar: "set-titlebar", - }, - menu: { - command: "menu-command", - runAction: "run-desktop-menu-action", - }, - updater: { - subscribe: "updater-subscribe", - unsubscribe: "updater-unsubscribe", - check: "updater-check", - install: "updater-install", - state: "updater-state", - }, - wsl: { - awaitInitialization: "wsl-servers-await-initialization", - subscribe: "wsl-servers-subscribe", - unsubscribe: "wsl-servers-unsubscribe", - getState: "wsl-servers-get-state", - probeRuntime: "wsl-servers-probe-runtime", - refreshDistros: "wsl-servers-refresh-distros", - installWsl: "wsl-servers-install-wsl", - installDistro: "wsl-servers-install-distro", - probeAddable: "wsl-servers-probe-addable", - installOpencode: "wsl-servers-install-opencode", - openTerminal: "wsl-servers-open-terminal", - addServer: "wsl-servers-add", - removeServer: "wsl-servers-remove", - startServer: "wsl-servers-start", - event: "wsl-servers-event", - }, -} as const - export type ServerReadyData = { url: string username: string | null @@ -138,114 +42,3 @@ export type ClipboardImage = { width: number height: number } - -export type IpcInvoke = { - [Ipc.app.awaitInitialization]: { args: []; result: ServerReadyData } - [Ipc.app.consumeInitialDeepLinks]: { args: []; result: string[] } - [Ipc.app.getDefaultServerUrl]: { args: []; result: string | null } - [Ipc.app.setDefaultServerUrl]: { args: [url: string | null]; result: void } - [Ipc.app.isFirstLaunchOnboardingPending]: { args: []; result: boolean } - [Ipc.app.finishFirstLaunchOnboarding]: { args: [createDefaultProject: boolean]; result: string | null } - [Ipc.app.checkAppExists]: { args: [appName: string]; result: boolean } - [Ipc.app.resolveAppPath]: { args: [appName: string]; result: string | null } - [Ipc.app.setBackgroundColor]: { args: [color: string]; result: void } - [Ipc.app.exportDebugLogs]: { args: []; result: string } - [Ipc.app.setForceFocus]: { args: [enabled: boolean]; result: void } - [Ipc.app.recordFatalRendererError]: { args: [error: FatalRendererError]; result: void } - [Ipc.app.setNativeTranslations]: { args: [bundle: DesktopNativeBundle]; result: void } - - [Ipc.storage.get]: { args: [name: string, key: string]; result: string | null } - [Ipc.storage.set]: { args: [name: string, key: string, value: string]; result: void } - [Ipc.storage.delete]: { args: [name: string, key: string]; result: void } - [Ipc.storage.clear]: { args: [name: string]; result: void } - [Ipc.storage.keys]: { args: [name: string]; result: string[] } - [Ipc.storage.length]: { args: [name: string]; result: number } - - [Ipc.drafts.get]: { args: [key: string]; result: string | null } - [Ipc.drafts.set]: { args: [key: string, value: string]; result: void } - [Ipc.drafts.delete]: { args: [key: string]; result: void } - [Ipc.drafts.putBlob]: { args: [data: ArrayBuffer]; result: string } - [Ipc.drafts.getBlob]: { args: [id: string]; result: ArrayBuffer | null } - - [Ipc.files.openDirectoryPicker]: { - args: [options?: DirectoryPickerOptions] - result: string | string[] | null - } - [Ipc.files.openFilePicker]: { args: [options?: FilePickerOptions]; result: PickedFiles | null } - [Ipc.files.readPickedFile]: { args: [token: string, path: string]; result: ArrayBuffer } - [Ipc.files.releasePickedFiles]: { args: [token: string]; result: void } - [Ipc.files.saveFilePicker]: { args: [options?: SaveFilePickerOptions]; result: string | null } - [Ipc.files.openPath]: { args: [path: string, app?: string]; result: string | undefined } - [Ipc.files.revealPath]: { args: [path: string]; result: boolean } - [Ipc.files.readClipboardImage]: { args: []; result: ClipboardImage | null } - - [Ipc.window.getId]: { args: []; result: string } - [Ipc.window.themeReady]: { args: []; result: void } - [Ipc.window.getFocused]: { args: []; result: boolean } - [Ipc.window.getFullscreen]: { args: []; result: boolean } - [Ipc.window.setFocus]: { args: []; result: void } - [Ipc.window.show]: { args: []; result: void } - [Ipc.window.getZoomFactor]: { args: []; result: number } - [Ipc.window.setZoomFactor]: { args: [factor: number]; result: void } - [Ipc.window.getPinchZoomEnabled]: { args: []; result: boolean } - [Ipc.window.setPinchZoomEnabled]: { args: [enabled: boolean]; result: void } - [Ipc.window.setTitlebar]: { args: [theme: TitlebarTheme]; result: void } - [Ipc.menu.runAction]: { args: [action: DesktopMenuAction]; result: void } - - [Ipc.updater.subscribe]: { args: []; result: void } - [Ipc.updater.unsubscribe]: { args: []; result: void } - [Ipc.updater.check]: { args: []; result: UpdaterState } - [Ipc.updater.install]: { args: []; result: void } - - [Ipc.wsl.awaitInitialization]: { args: []; result: void } - [Ipc.wsl.subscribe]: { args: []; result: void } - [Ipc.wsl.unsubscribe]: { args: []; result: void } - [Ipc.wsl.getState]: { args: []; result: WslServersState } - [Ipc.wsl.probeRuntime]: { args: []; result: void } - [Ipc.wsl.refreshDistros]: { args: []; result: void } - [Ipc.wsl.installWsl]: { args: []; result: void } - [Ipc.wsl.installDistro]: { args: [name: string]; result: void } - [Ipc.wsl.probeAddable]: { args: [distros: string[]]; result: void } - [Ipc.wsl.installOpencode]: { args: [name: string]; result: void } - [Ipc.wsl.openTerminal]: { args: [name: string]; result: void } - [Ipc.wsl.addServer]: { args: [distro: string]; result: WslServerConfig } - [Ipc.wsl.removeServer]: { args: [id: string]; result: void } - [Ipc.wsl.startServer]: { args: [id: string]; result: void } -} - -export type IpcSend = { - [Ipc.app.relaunch]: [] - [Ipc.files.openExternal]: [url: string] - [Ipc.files.openLocalFile]: [url: string] -} - -export type IpcEvent = { - [Ipc.app.deepLink]: [urls: string[]] - [Ipc.menu.command]: [id: string] - [Ipc.updater.state]: [state: UpdaterState] - [Ipc.wsl.event]: [event: WslServersEvent] - [Ipc.window.fullscreenChanged]: [fullscreen: boolean] - [Ipc.window.pinchZoomEnabledChanged]: [enabled: boolean] - [Ipc.window.zoomFactorChanged]: [factor: number] -} - -export type IpcInvokeArgs = IpcInvoke[Channel]["args"] -export type IpcInvokeResult = IpcInvoke[Channel]["result"] -export type IpcInvokeMethod = ( - ...args: IpcInvokeArgs -) => Promise> -export type IpcSendMethod = (...args: IpcSend[Channel]) => void -export type IpcEventListener = (...args: IpcEvent[Channel]) => void -export type IpcEventSubscription = (listener: IpcEventListener) => () => void - -type IpcEventSender = { - send(channel: Channel, ...args: IpcEvent[Channel]): void -} - -export function sendIpcEvent( - sender: IpcEventSender, - channel: Channel, - ...args: IpcEvent[Channel] -) { - sender.send(channel, ...args) -} diff --git a/packages/desktop/src/shared/ipc-rpc.ts b/packages/desktop/src/shared/ipc-rpc.ts new file mode 100644 index 00000000000..235fed1bc1e --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc.ts @@ -0,0 +1,21 @@ +import { RpcClient, RpcClientError } from "effect/unstable/rpc" +import { AppRpcs } from "./ipc-rpc/app" +import { EventRpcs } from "./ipc-rpc/events" +import { FileRpcs } from "./ipc-rpc/files" +import { MenuRpcs } from "./ipc-rpc/menu" +import { StorageRpcs } from "./ipc-rpc/storage" +import { UpdaterRpcs } from "./ipc-rpc/updater" +import { WindowRpcs } from "./ipc-rpc/window" +import { WslRpcs } from "./ipc-rpc/wsl" + +export { AppRpcs } from "./ipc-rpc/app" +export { EventRpcs } from "./ipc-rpc/events" +export { FileRpcs } from "./ipc-rpc/files" +export { MenuRpcs } from "./ipc-rpc/menu" +export { StorageRpcs } from "./ipc-rpc/storage" +export { UpdaterRpcs } from "./ipc-rpc/updater" +export { WindowRpcs } from "./ipc-rpc/window" +export { WslRpcs } from "./ipc-rpc/wsl" + +export const DesktopRpcs = AppRpcs.merge(StorageRpcs, FileRpcs, WindowRpcs, MenuRpcs, UpdaterRpcs, WslRpcs, EventRpcs) +export type DesktopRpcClient = RpcClient.FromGroup diff --git a/packages/desktop/src/shared/ipc-rpc/app.ts b/packages/desktop/src/shared/ipc-rpc/app.ts new file mode 100644 index 00000000000..74dffd08326 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/app.ts @@ -0,0 +1,72 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +const ServerReadyData = Schema.Struct({ + url: Schema.String, + username: Schema.NullOr(Schema.String), + password: Schema.NullOr(Schema.String), +}) + +export const AppAwaitInitialization = Rpc.make("AppAwaitInitialization", { success: ServerReadyData }) +export const AppConsumeInitialDeepLinks = Rpc.make("AppConsumeInitialDeepLinks", { + success: Schema.Array(Schema.String), +}) +export const AppGetDefaultServerUrl = Rpc.make("AppGetDefaultServerUrl", { + success: Schema.NullOr(Schema.String), +}) +export const AppSetDefaultServerUrl = Rpc.make("AppSetDefaultServerUrl", { + payload: { url: Schema.NullOr(Schema.String) }, +}) +export const AppIsFirstLaunchOnboardingPending = Rpc.make("AppIsFirstLaunchOnboardingPending", { + success: Schema.Boolean, +}) +export const AppFinishFirstLaunchOnboarding = Rpc.make("AppFinishFirstLaunchOnboarding", { + payload: { createDefaultProject: Schema.Boolean }, + success: Schema.NullOr(Schema.String), +}) +export const AppCheckAppExists = Rpc.make("AppCheckAppExists", { + payload: { appName: Schema.String }, + success: Schema.Boolean, +}) +export const AppResolveAppPath = Rpc.make("AppResolveAppPath", { + payload: { appName: Schema.String }, + success: Schema.NullOr(Schema.String), +}) +export const AppSetBackgroundColor = Rpc.make("AppSetBackgroundColor", { + payload: { color: Schema.String }, +}) +export const AppExportDebugLogs = Rpc.make("AppExportDebugLogs", { success: Schema.String }) +export const AppSetForceFocus = Rpc.make("AppSetForceFocus", { + payload: { enabled: Schema.Boolean }, +}) +export const AppRecordFatalRendererError = Rpc.make("AppRecordFatalRendererError", { + payload: { + error: Schema.Struct({ + error: Schema.String, + url: Schema.String, + version: Schema.optionalKey(Schema.String), + platform: Schema.String, + os: Schema.optionalKey(Schema.String), + }), + }, +}) +export const AppSetNativeTranslations = Rpc.make("AppSetNativeTranslations", { + payload: { value: Schema.Unknown }, +}) +export const AppRelaunch = Rpc.make("AppRelaunch") +export const AppRpcs = RpcGroup.make( + AppAwaitInitialization, + AppConsumeInitialDeepLinks, + AppGetDefaultServerUrl, + AppSetDefaultServerUrl, + AppIsFirstLaunchOnboardingPending, + AppFinishFirstLaunchOnboarding, + AppCheckAppExists, + AppResolveAppPath, + AppSetBackgroundColor, + AppExportDebugLogs, + AppSetForceFocus, + AppRecordFatalRendererError, + AppSetNativeTranslations, + AppRelaunch, +) diff --git a/packages/desktop/src/shared/ipc-rpc/events.ts b/packages/desktop/src/shared/ipc-rpc/events.ts new file mode 100644 index 00000000000..ef3f74d0162 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/events.ts @@ -0,0 +1,46 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" +import { UpdaterStateSchema } from "./updater" +import { WslServersEventSchema } from "./wsl" + +export class DeepLinksOpened extends Schema.TaggedClass()("DeepLinksOpened", { + urls: Schema.Array(Schema.String), +}) {} + +export class MenuCommandTriggered extends Schema.TaggedClass()("MenuCommandTriggered", { + id: Schema.String, +}) {} + +export class UpdaterStateChanged extends Schema.TaggedClass()("UpdaterStateChanged", { + state: UpdaterStateSchema, +}) {} + +export class WslServersChanged extends Schema.TaggedClass()("WslServersChanged", { + event: WslServersEventSchema, +}) {} + +export class WindowFullscreenChanged extends Schema.TaggedClass()("WindowFullscreenChanged", { + fullscreen: Schema.Boolean, +}) {} + +export class WindowPinchZoomChanged extends Schema.TaggedClass()("WindowPinchZoomChanged", { + enabled: Schema.Boolean, +}) {} + +export class WindowZoomChanged extends Schema.TaggedClass()("WindowZoomChanged", { + factor: Schema.Number, +}) {} + +export const DesktopEvent = Schema.Union([ + DeepLinksOpened, + MenuCommandTriggered, + UpdaterStateChanged, + WslServersChanged, + WindowFullscreenChanged, + WindowPinchZoomChanged, + WindowZoomChanged, +]) +export type DesktopEvent = Schema.Schema.Type + +export const DesktopEvents = Rpc.make("DesktopEvents", { success: DesktopEvent, stream: true }) +export const EventRpcs = RpcGroup.make(DesktopEvents) diff --git a/packages/desktop/src/shared/ipc-rpc/files.ts b/packages/desktop/src/shared/ipc-rpc/files.ts new file mode 100644 index 00000000000..cfb9b3685df --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/files.ts @@ -0,0 +1,71 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +const OptionalString = Schema.optionalKey(Schema.String) +const PickerOptions = Schema.Struct({ + multiple: Schema.optionalKey(Schema.Boolean), + title: OptionalString, + defaultPath: OptionalString, +}) +const FilePickerOptions = Schema.Struct({ + multiple: Schema.optionalKey(Schema.Boolean), + title: OptionalString, + defaultPath: OptionalString, + extensions: Schema.optionalKey(Schema.Array(Schema.String)), +}) +const SavePickerOptions = Schema.Struct({ title: OptionalString, defaultPath: OptionalString }) +const PickedFiles = Schema.Struct({ + token: Schema.String, + files: Schema.Array(Schema.Struct({ path: Schema.String, name: Schema.String, size: Schema.Number })), +}) +const ClipboardImage = Schema.Struct({ buffer: Schema.Uint8Array, width: Schema.Number, height: Schema.Number }) + +export const FilesOpenDirectoryPicker = Rpc.make("FilesOpenDirectoryPicker", { + payload: { options: Schema.optionalKey(PickerOptions) }, + success: Schema.NullOr(Schema.Union([Schema.String, Schema.Array(Schema.String)])), +}) +export const FilesOpenFilePicker = Rpc.make("FilesOpenFilePicker", { + payload: { options: Schema.optionalKey(FilePickerOptions) }, + success: Schema.NullOr(PickedFiles), +}) +export const FilesReadPickedFile = Rpc.make("FilesReadPickedFile", { + payload: { token: Schema.String, path: Schema.String }, + success: Schema.Uint8Array, +}) +export const FilesReleasePickedFiles = Rpc.make("FilesReleasePickedFiles", { + payload: { token: Schema.String }, +}) +export const FilesSaveFilePicker = Rpc.make("FilesSaveFilePicker", { + payload: { options: Schema.optionalKey(SavePickerOptions) }, + success: Schema.NullOr(Schema.String), +}) +export const FilesOpenExternal = Rpc.make("FilesOpenExternal", { + payload: { url: Schema.String }, +}) +export const FilesOpenLocalFile = Rpc.make("FilesOpenLocalFile", { + payload: { url: Schema.String }, +}) +export const FilesOpenPath = Rpc.make("FilesOpenPath", { + payload: { path: Schema.String, application: Schema.optionalKey(Schema.String) }, + success: Schema.NullOr(Schema.String), +}) +export const FilesRevealPath = Rpc.make("FilesRevealPath", { + payload: { path: Schema.String }, + success: Schema.Boolean, +}) +export const FilesReadClipboardImage = Rpc.make("FilesReadClipboardImage", { + success: Schema.NullOr(ClipboardImage), +}) + +export const FileRpcs = RpcGroup.make( + FilesOpenDirectoryPicker, + FilesOpenFilePicker, + FilesReadPickedFile, + FilesReleasePickedFiles, + FilesSaveFilePicker, + FilesOpenExternal, + FilesOpenLocalFile, + FilesOpenPath, + FilesRevealPath, + FilesReadClipboardImage, +) diff --git a/packages/desktop/src/shared/ipc-rpc/menu.ts b/packages/desktop/src/shared/ipc-rpc/menu.ts new file mode 100644 index 00000000000..c20c0c2ece5 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/menu.ts @@ -0,0 +1,29 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +const DesktopMenuAction = Schema.Literals([ + "app.checkForUpdates", + "app.relaunch", + "edit.undo", + "edit.redo", + "edit.cut", + "edit.copy", + "edit.paste", + "edit.delete", + "edit.selectAll", + "view.reload", + "view.toggleDevTools", + "view.resetZoom", + "view.zoomIn", + "view.zoomOut", + "view.toggleFullscreen", + "window.new", + "window.close", + "window.minimize", + "window.toggleMaximize", +]) + +export const MenuRunAction = Rpc.make("MenuRunAction", { + payload: { action: DesktopMenuAction }, +}) +export const MenuRpcs = RpcGroup.make(MenuRunAction) diff --git a/packages/desktop/src/shared/ipc-rpc/storage.ts b/packages/desktop/src/shared/ipc-rpc/storage.ts new file mode 100644 index 00000000000..42443a30632 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/storage.ts @@ -0,0 +1,52 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +export const StorageGet = Rpc.make("StorageGet", { + payload: { name: Schema.String, key: Schema.String }, + success: Schema.NullOr(Schema.String), +}) +export const StorageSet = Rpc.make("StorageSet", { + payload: { name: Schema.String, key: Schema.String, value: Schema.String }, +}) +export const StorageDelete = Rpc.make("StorageDelete", { + payload: { name: Schema.String, key: Schema.String }, +}) +export const StorageClear = Rpc.make("StorageClear", { payload: { name: Schema.String } }) +export const StorageKeys = Rpc.make("StorageKeys", { + payload: { name: Schema.String }, + success: Schema.Array(Schema.String), +}) +export const StorageLength = Rpc.make("StorageLength", { + payload: { name: Schema.String }, + success: Schema.Number, +}) +export const DraftsGet = Rpc.make("DraftsGet", { + payload: { key: Schema.String }, + success: Schema.NullOr(Schema.String), +}) +export const DraftsSet = Rpc.make("DraftsSet", { + payload: { key: Schema.String, value: Schema.String }, +}) +export const DraftsDelete = Rpc.make("DraftsDelete", { payload: { key: Schema.String } }) +export const DraftsPutBlob = Rpc.make("DraftsPutBlob", { + payload: { data: Schema.Uint8Array }, + success: Schema.String, +}) +export const DraftsGetBlob = Rpc.make("DraftsGetBlob", { + payload: { id: Schema.String }, + success: Schema.NullOr(Schema.Uint8Array), +}) + +export const StorageRpcs = RpcGroup.make( + StorageGet, + StorageSet, + StorageDelete, + StorageClear, + StorageKeys, + StorageLength, + DraftsGet, + DraftsSet, + DraftsDelete, + DraftsPutBlob, + DraftsGetBlob, +) diff --git a/packages/desktop/src/shared/ipc-rpc/updater.ts b/packages/desktop/src/shared/ipc-rpc/updater.ts new file mode 100644 index 00000000000..6c25ac305e9 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/updater.ts @@ -0,0 +1,19 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +export const UpdaterStateSchema = Schema.Union([ + Schema.Struct({ status: Schema.Literal("disabled") }), + Schema.Struct({ status: Schema.Literal("idle") }), + Schema.Struct({ status: Schema.Literal("checking") }), + Schema.Struct({ status: Schema.Literal("downloading"), version: Schema.String }), + Schema.Struct({ status: Schema.Literal("ready"), version: Schema.String }), + Schema.Struct({ status: Schema.Literal("up-to-date") }), + Schema.Struct({ status: Schema.Literal("installing"), version: Schema.String }), + Schema.Struct({ status: Schema.Literal("error"), message: Schema.String }), +]) + +export const UpdaterSubscribe = Rpc.make("UpdaterSubscribe") +export const UpdaterUnsubscribe = Rpc.make("UpdaterUnsubscribe") +export const UpdaterCheck = Rpc.make("UpdaterCheck", { success: UpdaterStateSchema }) +export const UpdaterInstall = Rpc.make("UpdaterInstall") +export const UpdaterRpcs = RpcGroup.make(UpdaterSubscribe, UpdaterUnsubscribe, UpdaterCheck, UpdaterInstall) diff --git a/packages/desktop/src/shared/ipc-rpc/window.ts b/packages/desktop/src/shared/ipc-rpc/window.ts new file mode 100644 index 00000000000..7971d0b62d6 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/window.ts @@ -0,0 +1,40 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +export const WindowGetId = Rpc.make("WindowGetId", { success: Schema.String }) +export const WindowThemeReady = Rpc.make("WindowThemeReady") +export const WindowGetFocused = Rpc.make("WindowGetFocused", { success: Schema.Boolean }) +export const WindowGetFullscreen = Rpc.make("WindowGetFullscreen", { success: Schema.Boolean }) +export const WindowSetFocus = Rpc.make("WindowSetFocus") +export const WindowShow = Rpc.make("WindowShow") +export const WindowGetZoomFactor = Rpc.make("WindowGetZoomFactor", { success: Schema.Number }) +export const WindowSetZoomFactor = Rpc.make("WindowSetZoomFactor", { + payload: { factor: Schema.Number }, +}) +export const WindowGetPinchZoomEnabled = Rpc.make("WindowGetPinchZoomEnabled", { + success: Schema.Boolean, +}) +export const WindowSetPinchZoomEnabled = Rpc.make("WindowSetPinchZoomEnabled", { + payload: { enabled: Schema.Boolean }, +}) +export const WindowSetTitlebar = Rpc.make("WindowSetTitlebar", { + payload: { + theme: Schema.Struct({ + mode: Schema.Literals(["light", "dark"]), + scheme: Schema.optionalKey(Schema.Literals(["system", "light", "dark"])), + }), + }, +}) +export const WindowRpcs = RpcGroup.make( + WindowGetId, + WindowThemeReady, + WindowGetFocused, + WindowGetFullscreen, + WindowSetFocus, + WindowShow, + WindowGetZoomFactor, + WindowSetZoomFactor, + WindowGetPinchZoomEnabled, + WindowSetPinchZoomEnabled, + WindowSetTitlebar, +) diff --git a/packages/desktop/src/shared/ipc-rpc/wsl.ts b/packages/desktop/src/shared/ipc-rpc/wsl.ts new file mode 100644 index 00000000000..c4e90bc8bd1 --- /dev/null +++ b/packages/desktop/src/shared/ipc-rpc/wsl.ts @@ -0,0 +1,109 @@ +import { Schema } from "effect" +import { Rpc, RpcGroup } from "effect/unstable/rpc" + +const WslServerConfig = Schema.Struct({ id: Schema.String, distro: Schema.String }) +const WslServerRuntime = Schema.Union([ + Schema.Struct({ kind: Schema.Literal("starting") }), + Schema.Struct({ + kind: Schema.Literal("ready"), + url: Schema.String, + username: Schema.NullOr(Schema.String), + password: Schema.NullOr(Schema.String), + }), + Schema.Struct({ kind: Schema.Literal("failed"), message: Schema.String }), + Schema.Struct({ kind: Schema.Literal("stopped") }), +]) +const WslJob = Schema.Union([ + Schema.Struct({ kind: Schema.Literal("runtime"), startedAt: Schema.Number }), + Schema.Struct({ kind: Schema.Literal("distros"), startedAt: Schema.Number }), + Schema.Struct({ kind: Schema.Literal("install-wsl"), startedAt: Schema.Number }), + Schema.Struct({ kind: Schema.Literal("install-distro"), distro: Schema.String, startedAt: Schema.Number }), + Schema.Struct({ + kind: Schema.Literal("probe-addable"), + distros: Schema.Array(Schema.String), + startedAt: Schema.Number, + }), + Schema.Struct({ kind: Schema.Literal("install-opencode"), distro: Schema.String, startedAt: Schema.Number }), +]) +const WslServersState = Schema.Struct({ + runtime: Schema.NullOr( + Schema.Struct({ + available: Schema.Boolean, + version: Schema.NullOr(Schema.String), + error: Schema.NullOr(Schema.String), + }), + ), + installed: Schema.Array( + Schema.Struct({ name: Schema.String, version: Schema.NullOr(Schema.Number), isDefault: Schema.Boolean }), + ), + online: Schema.Array(Schema.Struct({ name: Schema.String, label: Schema.String })), + distroProbes: Schema.Record( + Schema.String, + Schema.Struct({ + name: Schema.String, + canExecute: Schema.Boolean, + hasBash: Schema.Boolean, + hasCurl: Schema.Boolean, + error: Schema.NullOr(Schema.String), + }), + ), + opencodeChecks: Schema.Record( + Schema.String, + Schema.Struct({ + distro: Schema.String, + resolvedPath: Schema.NullOr(Schema.String), + version: Schema.NullOr(Schema.String), + expectedVersion: Schema.NullOr(Schema.String), + matchesDesktop: Schema.NullOr(Schema.Boolean), + error: Schema.NullOr(Schema.String), + }), + ), + pendingRestart: Schema.Boolean, + servers: Schema.Array(Schema.Struct({ config: WslServerConfig, runtime: WslServerRuntime })), + job: Schema.NullOr(WslJob), +}) +export const WslServersEventSchema = Schema.Struct({ type: Schema.Literal("state"), state: WslServersState }) + +export const WslSubscribe = Rpc.make("WslSubscribe") +export const WslUnsubscribe = Rpc.make("WslUnsubscribe") +export const WslGetState = Rpc.make("WslGetState", { success: WslServersState }) +export const WslProbeRuntime = Rpc.make("WslProbeRuntime") +export const WslRefreshDistros = Rpc.make("WslRefreshDistros") +export const WslInstallWsl = Rpc.make("WslInstallWsl") +export const WslInstallDistro = Rpc.make("WslInstallDistro", { + payload: { name: Schema.String }, +}) +export const WslProbeAddable = Rpc.make("WslProbeAddable", { + payload: { distros: Schema.Array(Schema.String) }, +}) +export const WslInstallOpencode = Rpc.make("WslInstallOpencode", { + payload: { name: Schema.String }, +}) +export const WslOpenTerminal = Rpc.make("WslOpenTerminal", { + payload: { name: Schema.String }, +}) +export const WslAddServer = Rpc.make("WslAddServer", { + payload: { distro: Schema.String }, + success: WslServerConfig, +}) +export const WslRemoveServer = Rpc.make("WslRemoveServer", { + payload: { id: Schema.String }, +}) +export const WslStartServer = Rpc.make("WslStartServer", { + payload: { id: Schema.String }, +}) +export const WslRpcs = RpcGroup.make( + WslSubscribe, + WslUnsubscribe, + WslGetState, + WslProbeRuntime, + WslRefreshDistros, + WslInstallWsl, + WslInstallDistro, + WslProbeAddable, + WslInstallOpencode, + WslOpenTerminal, + WslAddServer, + WslRemoveServer, + WslStartServer, +) diff --git a/packages/desktop/src/shared/ipc-transport.ts b/packages/desktop/src/shared/ipc-transport.ts new file mode 100644 index 00000000000..2efbd527a7b --- /dev/null +++ b/packages/desktop/src/shared/ipc-transport.ts @@ -0,0 +1 @@ +export const IpcTransportPort = "desktop-rpc-port"