commit 1f01c0c4547bdc12013298db8495fd6ddf017f85
Author: BordedDev <>
Date:   Thu Mar 6 00:36:52 2025 +0100

    Add base bot setup
     - supports ping, llama and image/comfyUI
     - comes with 3 pre-setup comfyUI templates

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c924dd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,132 @@
+/.idea/
+/.vscode/
+/node_modules
+.env
+*.orig
+*.pyc
+*.swp
+*~
+.fuse_hidden*
+.directory
+.Trash-*
+.nfs*
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+.idea/**/aws.xml
+.idea/**/contentModel.xml
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+.idea/**/gradle.xml
+.idea/**/libraries
+cmake-build-*/
+.idea/**/mongoSettings.xml
+*.iws
+out/
+.idea_modules/
+atlassian-ide-plugin.xml
+.idea/replstate.xml
+.idea/sonarlint/
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+.idea/httpRequests
+.idea/caches/build_file_checksums.ser
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+pids
+*.pid
+*.seed
+*.pid.lock
+lib-cov
+coverage
+*.lcov
+.nyc_output
+.grunt
+bower_components
+.lock-wscript
+build/Release
+node_modules/
+jspm_packages/
+web_modules/
+*.tsbuildinfo
+.npm
+.eslintcache
+.stylelintcache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+.node_repl_history
+*.tgz
+.yarn-integrity
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+.cache
+.parcel-cache
+.next
+out
+.nuxt
+dist
+.cache/
+.vuepress/dist
+.temp
+.docusaurus
+.serverless/
+.fusebox/
+.dynamodb/
+.tern-port
+.vscode-test
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+.svelte-kit/
+package
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+*.stackdump
+[Dd]esktop.ini
+$RECYCLE.BIN/
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+*.lnk
+.DS_Store
+.AppleDouble
+.LSOverride
+Icon
+._*
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/deno.json b/deno.json
new file mode 100644
index 0000000..fdcc99e
--- /dev/null
+++ b/deno.json
@@ -0,0 +1,24 @@
+{
+  "tasks": {
+    "dev:snek-llama-bot": "deno run --allow-env --allow-read --allow-sys --allow-ffi --allow-net=snek.molodetz.nl:443,127.0.0.1:8188 --allow-run=\"deno\" src/ws-snek-llama-bot.ts",
+    "dev:snek-img-bot": "deno run --allow-env --allow-read --allow-sys --allow-ffi --allow-net=snek.molodetz.nl:443,127.0.0.1:8188 --allow-run=\"deno\" src/ws-snek-image-bot.ts"
+  },
+  "imports": {
+    "@eta-dev/eta": "jsr:@eta-dev/eta@^3.5.0",
+    "@logtape/logtape": "jsr:@logtape/logtape@^0.8.1",
+    "@std/assert": "jsr:@std/assert@1",
+    "@std/cli": "jsr:@std/cli@^1.0.12",
+    "@std/collections": "jsr:@std/collections@^1.0.10",
+    "@std/dotenv": "jsr:@std/dotenv@^0.225.3",
+    "@std/path": "jsr:@std/path@^1.0.8",
+    "@types/node": "npm:@types/node@^22.13.9",
+    "jmespath": "npm:jmespath@^0.16.0",
+    "lodash-es": "npm:lodash-es@^4.17.21",
+    "node-llama-cpp": "npm:node-llama-cpp@^3.6.0",
+    "octokit": "npm:octokit@^4.1.0"
+  },
+  "fmt": {
+    "semiColons": false
+  },
+  "nodeModulesDir": "auto"
+}
diff --git a/deno.lock b/deno.lock
new file mode 100644
index 0000000..fff14f9
--- /dev/null
+++ b/deno.lock
@@ -0,0 +1,1259 @@
+{
+  "version": "4",
+  "specifiers": {
+    "jsr:@eta-dev/eta@*": "3.5.0",
+    "jsr:@eta-dev/eta@^3.5.0": "3.5.0",
+    "jsr:@logtape/logtape@~0.8.1": "0.8.1",
+    "jsr:@std/assert@1": "1.0.11",
+    "jsr:@std/cli@^1.0.12": "1.0.12",
+    "jsr:@std/collections@^1.0.10": "1.0.10",
+    "jsr:@std/dotenv@~0.225.3": "0.225.3",
+    "jsr:@std/internal@^1.0.5": "1.0.5",
+    "jsr:@std/path@*": "1.0.8",
+    "jsr:@std/path@^1.0.8": "1.0.8",
+    "npm:@types/node@^22.13.9": "22.13.9",
+    "npm:jmespath@*": "0.16.0",
+    "npm:jmespath@0.16": "0.16.0",
+    "npm:lodash-es@*": "4.17.21",
+    "npm:lodash-es@^4.17.21": "4.17.21",
+    "npm:node-llama-cpp@*": "3.6.0",
+    "npm:node-llama-cpp@^3.6.0": "3.6.0",
+    "npm:octokit@*": "4.1.0_@octokit+core@6.1.3",
+    "npm:octokit@^4.1.0": "4.1.0_@octokit+core@6.1.3"
+  },
+  "jsr": {
+    "@eta-dev/eta@3.5.0": {
+      "integrity": "6b70827efc14c7cbf08498ac7a922ecab003641caf3852a6cb5b1b12ee58fb37"
+    },
+    "@logtape/logtape@0.8.1": {
+      "integrity": "deb3b276d4be0a697e94f6a79ddb309f993e1870166a6d43d8eca86cd00871a8"
+    },
+    "@std/assert@1.0.11": {
+      "integrity": "2461ef3c368fe88bc60e186e7744a93112f16fd110022e113a0849e94d1c83c1",
+      "dependencies": [
+        "jsr:@std/internal"
+      ]
+    },
+    "@std/cli@1.0.12": {
+      "integrity": "e5cfb7814d189da174ecd7a34fbbd63f3513e24a1b307feb2fcd5da47a070d90"
+    },
+    "@std/collections@1.0.10": {
+      "integrity": "903af106a3d92970d74e20f7ebff77d9658af9bef4403f1dc42a7801c0575899"
+    },
+    "@std/dotenv@0.225.3": {
+      "integrity": "a95e5b812c27b0854c52acbae215856d9cce9d4bbf774d938c51d212711e8d4a"
+    },
+    "@std/internal@1.0.5": {
+      "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba"
+    },
+    "@std/path@1.0.8": {
+      "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
+    }
+  },
+  "npm": {
+    "@huggingface/jinja@0.3.3": {
+      "integrity": "sha512-vQQr2JyWvVFba3Lj9es4q9vCl1sAc74fdgnEMoX8qHrXtswap9ge9uO3ONDzQB0cQ0PUyaKY2N6HaVbTBvSXvw=="
+    },
+    "@kwsites/file-exists@1.1.1": {
+      "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
+      "dependencies": [
+        "debug"
+      ]
+    },
+    "@kwsites/promise-deferred@1.1.1": {
+      "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="
+    },
+    "@node-llama-cpp/linux-arm64@3.6.0": {
+      "integrity": "sha512-hE/hqxtr5DQyY1DohwOcY742NQZtEFag8H/FQP2Y7fnlNQYhiOe45PcAJDiqmEUMmlCGVvHZaCWbaNVoTMYdWg=="
+    },
+    "@node-llama-cpp/linux-armv7l@3.6.0": {
+      "integrity": "sha512-aRyDVf8szfJJWHnNWG56Ir3LtfXxj9vwLXbXy4XwfHlMTuBHWhmrRXyB8f3A/aJ8h6u48wMVxqxdmwnXigSKWg=="
+    },
+    "@node-llama-cpp/linux-x64-cuda@3.6.0": {
+      "integrity": "sha512-lS9F+aX2cGV1I/pAuCNeQm9bGELNmnvKqbF4k4ZjNk64ZT2sE74o2S/uN6GvMJETG+rgQiKRuKb1l/yIm0LOfA=="
+    },
+    "@node-llama-cpp/linux-x64-vulkan@3.6.0": {
+      "integrity": "sha512-1Wc6e1YJRpjllD6MRfwYPxE7z8qvmaYrEFyVPzTe9sghKXUswpBmmb0mM/yOzwT/mUBygSwOEBvTkp3nG+pWhg=="
+    },
+    "@node-llama-cpp/linux-x64@3.6.0": {
+      "integrity": "sha512-lUzTTY7AwRz5j/f6rss6fPc2u3YNOmo4k8Zap38kzy9H6rL+U2nlanN+4STs5j/7gcx5f/VHRnPrYDl5OPcmTw=="
+    },
+    "@node-llama-cpp/mac-arm64-metal@3.6.0": {
+      "integrity": "sha512-bOtZkJ6dCWHnZ1SP8EJ+LOdIFKo/7clb0ck+IwD/Bn/2ePzObVBsq30IxpRnUXx8pZ54+CzmTQuS2NOMHXS0PQ=="
+    },
+    "@node-llama-cpp/mac-x64@3.6.0": {
+      "integrity": "sha512-xjyEAsOXQ6i3VuXoQYB5llYuNz0sP9YnrDzAJ8sqovXXYkSyXPRyTCF5/PaAFc6QMkpsFIw3fSbavJeSzR5IGw=="
+    },
+    "@node-llama-cpp/win-arm64@3.6.0": {
+      "integrity": "sha512-o4gEUBVMZ1R3Oy1f642UA1vJtnVLAJq2W+diPUxJVqXs9KYDOf7+JuxVcTEzSj6+wBsN3ZRtt36Xst41Jwp6FQ=="
+    },
+    "@node-llama-cpp/win-x64-cuda@3.6.0": {
+      "integrity": "sha512-vxNrz4BwMNgmfbRxALdTnb7RlJnO6p5uXlZP8fxpaD0zyBllenURTTzEo3Wobpa98af5DWEY1AueH9RFixvscA=="
+    },
+    "@node-llama-cpp/win-x64-vulkan@3.6.0": {
+      "integrity": "sha512-2XhzVQaRw5QxMqtg+517W+tn0fgDqvo12I0/wVpaBctwIaX+yOcj+njGlVUbMBFzhR9VM9wo5N2bjfRYI6y+PA=="
+    },
+    "@node-llama-cpp/win-x64@3.6.0": {
+      "integrity": "sha512-JDJoDeBkJhvFlINwi7tyTuOjSTJoBF6yyf7o89iMZ2xniyo6BzhI2d/79PGLkXht/1+sGNoCyzbuz3cBgP06Fg=="
+    },
+    "@octokit/app@15.1.2_@octokit+core@6.1.3": {
+      "integrity": "sha512-6aKmKvqnJKoVK+kx0mLlBMKmQYoziPw4Rd/PWr0j65QVQlrDXlu6hGU8fmTXt7tNkf/DsubdIaTT4fkoWzCh5g==",
+      "dependencies": [
+        "@octokit/auth-app",
+        "@octokit/auth-unauthenticated",
+        "@octokit/core",
+        "@octokit/oauth-app",
+        "@octokit/plugin-paginate-rest",
+        "@octokit/types",
+        "@octokit/webhooks"
+      ]
+    },
+    "@octokit/auth-app@7.1.4": {
+      "integrity": "sha512-5F+3l/maq9JfWQ4bV28jT2G/K8eu9OJ317yzXPTGe4Kw+lKDhFaS4dQ3Ltmb6xImKxfCQdqDqMXODhc9YLipLw==",
+      "dependencies": [
+        "@octokit/auth-oauth-app",
+        "@octokit/auth-oauth-user",
+        "@octokit/request",
+        "@octokit/request-error",
+        "@octokit/types",
+        "toad-cache",
+        "universal-github-app-jwt",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/auth-oauth-app@8.1.2": {
+      "integrity": "sha512-3woNZgq5/S6RS+9ZTq+JdymxVr7E0s4EYxF20ugQvgX3pomdPUL5r/XdTY9wALoBM2eHVy4ettr5fKpatyTyHw==",
+      "dependencies": [
+        "@octokit/auth-oauth-device",
+        "@octokit/auth-oauth-user",
+        "@octokit/request",
+        "@octokit/types",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/auth-oauth-device@7.1.2": {
+      "integrity": "sha512-gTOIzDeV36OhVfxCl69FmvJix7tJIiU6dlxuzLVAzle7fYfO8UDyddr9B+o4CFQVaMBLMGZ9ak2CWMYcGeZnPw==",
+      "dependencies": [
+        "@octokit/oauth-methods",
+        "@octokit/request",
+        "@octokit/types",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/auth-oauth-user@5.1.2": {
+      "integrity": "sha512-PgVDDPJgZYb3qSEXK4moksA23tfn68zwSAsQKZ1uH6IV9IaNEYx35OXXI80STQaLYnmEE86AgU0tC1YkM4WjsA==",
+      "dependencies": [
+        "@octokit/auth-oauth-device",
+        "@octokit/oauth-methods",
+        "@octokit/request",
+        "@octokit/types",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/auth-token@5.1.2": {
+      "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw=="
+    },
+    "@octokit/auth-unauthenticated@6.1.1": {
+      "integrity": "sha512-bGXqdN6RhSFHvpPq46SL8sN+F3odQ6oMNLWc875IgoqcC3qus+fOL2th6Tkl94wvdSTy8/OeHzWy/lZebmnhog==",
+      "dependencies": [
+        "@octokit/request-error",
+        "@octokit/types"
+      ]
+    },
+    "@octokit/core@6.1.3": {
+      "integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==",
+      "dependencies": [
+        "@octokit/auth-token",
+        "@octokit/graphql",
+        "@octokit/request",
+        "@octokit/request-error",
+        "@octokit/types",
+        "before-after-hook",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/endpoint@10.1.2": {
+      "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==",
+      "dependencies": [
+        "@octokit/types",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/graphql@8.2.0": {
+      "integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==",
+      "dependencies": [
+        "@octokit/request",
+        "@octokit/types",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/oauth-app@7.1.5": {
+      "integrity": "sha512-/Y2MiwWDlGUK4blKKfjJiwjzu/FzwKTTTfTZAAQ0QbdBIDEGJPWhOFH6muSN86zaa4tNheB4YS3oWIR2e4ydzA==",
+      "dependencies": [
+        "@octokit/auth-oauth-app",
+        "@octokit/auth-oauth-user",
+        "@octokit/auth-unauthenticated",
+        "@octokit/core",
+        "@octokit/oauth-authorization-url",
+        "@octokit/oauth-methods",
+        "@types/aws-lambda",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/oauth-authorization-url@7.1.1": {
+      "integrity": "sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA=="
+    },
+    "@octokit/oauth-methods@5.1.3": {
+      "integrity": "sha512-M+bDBi5H8FnH0xhCTg0m9hvcnppdDnxUqbZyOkxlLblKpLAR+eT2nbDPvJDp0eLrvJWA1I8OX0KHf/sBMQARRA==",
+      "dependencies": [
+        "@octokit/oauth-authorization-url",
+        "@octokit/request",
+        "@octokit/request-error",
+        "@octokit/types"
+      ]
+    },
+    "@octokit/openapi-types@23.0.1": {
+      "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g=="
+    },
+    "@octokit/openapi-webhooks-types@8.5.1": {
+      "integrity": "sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ=="
+    },
+    "@octokit/plugin-paginate-graphql@5.2.4_@octokit+core@6.1.3": {
+      "integrity": "sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==",
+      "dependencies": [
+        "@octokit/core"
+      ]
+    },
+    "@octokit/plugin-paginate-rest@11.4.0_@octokit+core@6.1.3": {
+      "integrity": "sha512-ttpGck5AYWkwMkMazNCZMqxKqIq1fJBNxBfsFwwfyYKTf914jKkLF0POMS3YkPBwp5g1c2Y4L79gDz01GhSr1g==",
+      "dependencies": [
+        "@octokit/core",
+        "@octokit/types"
+      ]
+    },
+    "@octokit/plugin-rest-endpoint-methods@13.3.0_@octokit+core@6.1.3": {
+      "integrity": "sha512-LUm44shlmkp/6VC+qQgHl3W5vzUP99ZM54zH6BuqkJK4DqfFLhegANd+fM4YRLapTvPm4049iG7F3haANKMYvQ==",
+      "dependencies": [
+        "@octokit/core",
+        "@octokit/types"
+      ]
+    },
+    "@octokit/plugin-retry@7.1.3_@octokit+core@6.1.3": {
+      "integrity": "sha512-8nKOXvYWnzv89gSyIvgFHmCBAxfQAOPRlkacUHL9r5oWtp5Whxl8Skb2n3ACZd+X6cYijD6uvmrQuPH/UCL5zQ==",
+      "dependencies": [
+        "@octokit/core",
+        "@octokit/request-error",
+        "@octokit/types",
+        "bottleneck"
+      ]
+    },
+    "@octokit/plugin-throttling@9.4.0_@octokit+core@6.1.3": {
+      "integrity": "sha512-IOlXxXhZA4Z3m0EEYtrrACkuHiArHLZ3CvqWwOez/pURNqRuwfoFlTPbN5Muf28pzFuztxPyiUiNwz8KctdZaQ==",
+      "dependencies": [
+        "@octokit/core",
+        "@octokit/types",
+        "bottleneck"
+      ]
+    },
+    "@octokit/request-error@6.1.6": {
+      "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==",
+      "dependencies": [
+        "@octokit/types"
+      ]
+    },
+    "@octokit/request@9.2.0": {
+      "integrity": "sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==",
+      "dependencies": [
+        "@octokit/endpoint",
+        "@octokit/request-error",
+        "@octokit/types",
+        "fast-content-type-parse",
+        "universal-user-agent"
+      ]
+    },
+    "@octokit/types@13.8.0": {
+      "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==",
+      "dependencies": [
+        "@octokit/openapi-types"
+      ]
+    },
+    "@octokit/webhooks-methods@5.1.0": {
+      "integrity": "sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ=="
+    },
+    "@octokit/webhooks@13.5.0": {
+      "integrity": "sha512-uSO/TCCfi9vaZHOBsGWsRNBXYYKtLnSDbHI+std0M80AaEd7AnVfLqvk+9V3GP1faPcOx06ADx+h8UWwvemIGw==",
+      "dependencies": [
+        "@octokit/openapi-webhooks-types",
+        "@octokit/request-error",
+        "@octokit/webhooks-methods"
+      ]
+    },
+    "@reflink/reflink-darwin-arm64@0.1.19": {
+      "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA=="
+    },
+    "@reflink/reflink-darwin-x64@0.1.19": {
+      "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA=="
+    },
+    "@reflink/reflink-linux-arm64-gnu@0.1.19": {
+      "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg=="
+    },
+    "@reflink/reflink-linux-arm64-musl@0.1.19": {
+      "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA=="
+    },
+    "@reflink/reflink-linux-x64-gnu@0.1.19": {
+      "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw=="
+    },
+    "@reflink/reflink-linux-x64-musl@0.1.19": {
+      "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ=="
+    },
+    "@reflink/reflink-win32-arm64-msvc@0.1.19": {
+      "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ=="
+    },
+    "@reflink/reflink-win32-x64-msvc@0.1.19": {
+      "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w=="
+    },
+    "@reflink/reflink@0.1.19": {
+      "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==",
+      "dependencies": [
+        "@reflink/reflink-darwin-arm64",
+        "@reflink/reflink-darwin-x64",
+        "@reflink/reflink-linux-arm64-gnu",
+        "@reflink/reflink-linux-arm64-musl",
+        "@reflink/reflink-linux-x64-gnu",
+        "@reflink/reflink-linux-x64-musl",
+        "@reflink/reflink-win32-arm64-msvc",
+        "@reflink/reflink-win32-x64-msvc"
+      ]
+    },
+    "@tinyhttp/content-disposition@2.2.2": {
+      "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g=="
+    },
+    "@types/aws-lambda@8.10.147": {
+      "integrity": "sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew=="
+    },
+    "@types/node@22.13.9": {
+      "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
+      "dependencies": [
+        "undici-types"
+      ]
+    },
+    "ansi-escapes@6.2.1": {
+      "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig=="
+    },
+    "ansi-regex@5.0.1": {
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+    },
+    "ansi-regex@6.1.0": {
+      "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
+    },
+    "ansi-styles@4.3.0": {
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dependencies": [
+        "color-convert"
+      ]
+    },
+    "ansi-styles@6.2.1": {
+      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+    },
+    "aproba@2.0.0": {
+      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+    },
+    "are-we-there-yet@3.0.1": {
+      "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+      "dependencies": [
+        "delegates",
+        "readable-stream"
+      ]
+    },
+    "async-retry@1.3.3": {
+      "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+      "dependencies": [
+        "retry@0.13.1"
+      ]
+    },
+    "asynckit@0.4.0": {
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios@1.8.1": {
+      "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
+      "dependencies": [
+        "follow-redirects",
+        "form-data",
+        "proxy-from-env"
+      ]
+    },
+    "before-after-hook@3.0.2": {
+      "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="
+    },
+    "bottleneck@2.19.5": {
+      "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
+    },
+    "bytes@3.1.2": {
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+    },
+    "call-bind-apply-helpers@1.0.2": {
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dependencies": [
+        "es-errors",
+        "function-bind"
+      ]
+    },
+    "chalk@5.4.1": {
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="
+    },
+    "chmodrp@1.0.2": {
+      "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w=="
+    },
+    "chownr@2.0.0": {
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+    },
+    "ci-info@4.1.0": {
+      "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A=="
+    },
+    "cli-cursor@5.0.0": {
+      "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+      "dependencies": [
+        "restore-cursor"
+      ]
+    },
+    "cli-spinners@2.9.2": {
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="
+    },
+    "cliui@8.0.1": {
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dependencies": [
+        "string-width@4.2.3",
+        "strip-ansi@6.0.1",
+        "wrap-ansi"
+      ]
+    },
+    "cmake-js@7.3.0": {
+      "integrity": "sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==",
+      "dependencies": [
+        "axios",
+        "debug",
+        "fs-extra",
+        "lodash.isplainobject",
+        "memory-stream",
+        "node-api-headers",
+        "npmlog",
+        "rc",
+        "semver",
+        "tar",
+        "url-join",
+        "which@2.0.2",
+        "yargs"
+      ]
+    },
+    "color-convert@2.0.1": {
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": [
+        "color-name"
+      ]
+    },
+    "color-name@1.1.4": {
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "color-support@1.1.3": {
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+    },
+    "combined-stream@1.0.8": {
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": [
+        "delayed-stream"
+      ]
+    },
+    "commander@10.0.1": {
+      "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
+    },
+    "console-control-strings@1.1.0": {
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+    },
+    "cross-env@7.0.3": {
+      "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+      "dependencies": [
+        "cross-spawn"
+      ]
+    },
+    "cross-spawn@7.0.6": {
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dependencies": [
+        "path-key",
+        "shebang-command",
+        "which@2.0.2"
+      ]
+    },
+    "debug@4.4.0": {
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dependencies": [
+        "ms"
+      ]
+    },
+    "deep-extend@0.6.0": {
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+    },
+    "delayed-stream@1.0.0": {
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
+    "delegates@1.0.0": {
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+    },
+    "dunder-proto@1.0.1": {
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dependencies": [
+        "call-bind-apply-helpers",
+        "es-errors",
+        "gopd"
+      ]
+    },
+    "emoji-regex@10.4.0": {
+      "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
+    },
+    "emoji-regex@8.0.0": {
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "env-var@7.5.0": {
+      "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA=="
+    },
+    "es-define-property@1.0.1": {
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
+    },
+    "es-errors@1.3.0": {
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+    },
+    "es-object-atoms@1.1.1": {
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dependencies": [
+        "es-errors"
+      ]
+    },
+    "es-set-tostringtag@2.1.0": {
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "dependencies": [
+        "es-errors",
+        "get-intrinsic",
+        "has-tostringtag",
+        "hasown"
+      ]
+    },
+    "escalade@3.2.0": {
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
+    },
+    "eventemitter3@5.0.1": {
+      "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
+    },
+    "fast-content-type-parse@2.0.1": {
+      "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
+    },
+    "filename-reserved-regex@3.0.0": {
+      "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw=="
+    },
+    "filenamify@6.0.0": {
+      "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==",
+      "dependencies": [
+        "filename-reserved-regex"
+      ]
+    },
+    "follow-redirects@1.15.9": {
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
+    },
+    "form-data@4.0.2": {
+      "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+      "dependencies": [
+        "asynckit",
+        "combined-stream",
+        "es-set-tostringtag",
+        "mime-types"
+      ]
+    },
+    "fs-extra@11.3.0": {
+      "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
+      "dependencies": [
+        "graceful-fs",
+        "jsonfile",
+        "universalify"
+      ]
+    },
+    "fs-minipass@2.1.0": {
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dependencies": [
+        "minipass@3.3.6"
+      ]
+    },
+    "function-bind@1.1.2": {
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+    },
+    "gauge@4.0.4": {
+      "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+      "dependencies": [
+        "aproba",
+        "color-support",
+        "console-control-strings",
+        "has-unicode",
+        "signal-exit@3.0.7",
+        "string-width@4.2.3",
+        "strip-ansi@6.0.1",
+        "wide-align"
+      ]
+    },
+    "get-caller-file@2.0.5": {
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+    },
+    "get-east-asian-width@1.3.0": {
+      "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="
+    },
+    "get-intrinsic@1.3.0": {
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dependencies": [
+        "call-bind-apply-helpers",
+        "es-define-property",
+        "es-errors",
+        "es-object-atoms",
+        "function-bind",
+        "get-proto",
+        "gopd",
+        "has-symbols",
+        "hasown",
+        "math-intrinsics"
+      ]
+    },
+    "get-proto@1.0.1": {
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dependencies": [
+        "dunder-proto",
+        "es-object-atoms"
+      ]
+    },
+    "gopd@1.2.0": {
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
+    },
+    "graceful-fs@4.2.11": {
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+    },
+    "has-symbols@1.1.0": {
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
+    },
+    "has-tostringtag@1.0.2": {
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dependencies": [
+        "has-symbols"
+      ]
+    },
+    "has-unicode@2.0.1": {
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+    },
+    "hasown@2.0.2": {
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "dependencies": [
+        "function-bind"
+      ]
+    },
+    "ignore@7.0.3": {
+      "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA=="
+    },
+    "inherits@2.0.4": {
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini@1.3.8": {
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+    },
+    "ipull@3.9.2": {
+      "integrity": "sha512-YbCDsqcf0ytc3b8304ygBlvRtKJTvyygkQX2xcmPkih6vdVKbRw13pDdtSR+vEqLql3owyuPj9m6iT6IfwFaCg==",
+      "dependencies": [
+        "@reflink/reflink",
+        "@tinyhttp/content-disposition",
+        "async-retry",
+        "chalk",
+        "ci-info",
+        "cli-spinners",
+        "commander",
+        "eventemitter3",
+        "filenamify",
+        "fs-extra",
+        "is-unicode-supported@2.1.0",
+        "lifecycle-utils@1.7.3",
+        "lodash.debounce",
+        "lowdb",
+        "pretty-bytes",
+        "pretty-ms@8.0.0",
+        "sleep-promise",
+        "slice-ansi",
+        "stdout-update",
+        "strip-ansi@7.1.0"
+      ]
+    },
+    "is-fullwidth-code-point@3.0.0": {
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+    },
+    "is-fullwidth-code-point@5.0.0": {
+      "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+      "dependencies": [
+        "get-east-asian-width"
+      ]
+    },
+    "is-interactive@2.0.0": {
+      "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="
+    },
+    "is-unicode-supported@1.3.0": {
+      "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="
+    },
+    "is-unicode-supported@2.1.0": {
+      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="
+    },
+    "isexe@2.0.0": {
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+    },
+    "isexe@3.1.1": {
+      "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="
+    },
+    "jmespath@0.16.0": {
+      "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="
+    },
+    "jsonfile@6.1.0": {
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dependencies": [
+        "graceful-fs",
+        "universalify"
+      ]
+    },
+    "lifecycle-utils@1.7.3": {
+      "integrity": "sha512-T7zs7J6/sgsqwVyG34Sfo5LTQmlPmmqaUe3yBhdF8nq24RtR/HtbkNZRhNbr9BEaKySdSgH+P9H5U9X+p0WjXw=="
+    },
+    "lifecycle-utils@2.0.0": {
+      "integrity": "sha512-KIkV6NeD2n0jZnO+fdIGKI5Or7alyhb6UTFzeaqf6EnE5y3pdK821+kd7yOMBUL/sPYhHU5ny74J0QKslLikGw=="
+    },
+    "lodash-es@4.17.21": {
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+    },
+    "lodash.debounce@4.0.8": {
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+    },
+    "lodash.isplainobject@4.0.6": {
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+    },
+    "log-symbols@6.0.0": {
+      "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
+      "dependencies": [
+        "chalk",
+        "is-unicode-supported@1.3.0"
+      ]
+    },
+    "log-symbols@7.0.0": {
+      "integrity": "sha512-zrc91EDk2M+2AXo/9BTvK91pqb7qrPg2nX/Hy+u8a5qQlbaOflCKO+6SqgZ+M+xUFxGdKTgwnGiL96b1W3ikRA==",
+      "dependencies": [
+        "is-unicode-supported@2.1.0",
+        "yoctocolors"
+      ]
+    },
+    "lowdb@7.0.1": {
+      "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==",
+      "dependencies": [
+        "steno"
+      ]
+    },
+    "math-intrinsics@1.1.0": {
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
+    },
+    "memory-stream@1.0.0": {
+      "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==",
+      "dependencies": [
+        "readable-stream"
+      ]
+    },
+    "mime-db@1.52.0": {
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types@2.1.35": {
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": [
+        "mime-db"
+      ]
+    },
+    "mimic-function@5.0.1": {
+      "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="
+    },
+    "minimist@1.2.8": {
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+    },
+    "minipass@3.3.6": {
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dependencies": [
+        "yallist"
+      ]
+    },
+    "minipass@5.0.0": {
+      "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="
+    },
+    "minizlib@2.1.2": {
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dependencies": [
+        "minipass@3.3.6",
+        "yallist"
+      ]
+    },
+    "mkdirp@1.0.4": {
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+    },
+    "ms@2.1.3": {
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+    },
+    "nanoid@5.1.2": {
+      "integrity": "sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g=="
+    },
+    "node-addon-api@8.3.1": {
+      "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA=="
+    },
+    "node-api-headers@1.5.0": {
+      "integrity": "sha512-Yi/FgnN8IU/Cd6KeLxyHkylBUvDTsSScT0Tna2zTrz8klmc8qF2ppj6Q1LHsmOueJWhigQwR4cO2p0XBGW5IaQ=="
+    },
+    "node-llama-cpp@3.6.0": {
+      "integrity": "sha512-SzjsZLuG2pQPPkgMniTgK4sCcslA6ion5L55L8qeGnIb0cAhzVDbJ0Lxl5NhuTMm8KkxVZXF2yTihyulPMSLhw==",
+      "dependencies": [
+        "@huggingface/jinja",
+        "@node-llama-cpp/linux-arm64",
+        "@node-llama-cpp/linux-armv7l",
+        "@node-llama-cpp/linux-x64",
+        "@node-llama-cpp/linux-x64-cuda",
+        "@node-llama-cpp/linux-x64-vulkan",
+        "@node-llama-cpp/mac-arm64-metal",
+        "@node-llama-cpp/mac-x64",
+        "@node-llama-cpp/win-arm64",
+        "@node-llama-cpp/win-x64",
+        "@node-llama-cpp/win-x64-cuda",
+        "@node-llama-cpp/win-x64-vulkan",
+        "async-retry",
+        "bytes",
+        "chalk",
+        "chmodrp",
+        "cmake-js",
+        "cross-env",
+        "cross-spawn",
+        "env-var",
+        "filenamify",
+        "fs-extra",
+        "ignore",
+        "ipull",
+        "is-unicode-supported@2.1.0",
+        "lifecycle-utils@2.0.0",
+        "log-symbols@7.0.0",
+        "nanoid",
+        "node-addon-api",
+        "octokit",
+        "ora",
+        "pretty-ms@9.2.0",
+        "proper-lockfile",
+        "semver",
+        "simple-git",
+        "slice-ansi",
+        "stdout-update",
+        "strip-ansi@7.1.0",
+        "validate-npm-package-name",
+        "which@5.0.0",
+        "yargs"
+      ]
+    },
+    "npmlog@6.0.2": {
+      "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+      "dependencies": [
+        "are-we-there-yet",
+        "console-control-strings",
+        "gauge",
+        "set-blocking"
+      ]
+    },
+    "octokit@4.1.0_@octokit+core@6.1.3": {
+      "integrity": "sha512-/UrQAOSvkc+lUUWKNzy4ByAgYU9KpFzZQt8DnC962YmQuDiZb1SNJ90YukCCK5aMzKqqCA+z1kkAlmzYvdYKag==",
+      "dependencies": [
+        "@octokit/app",
+        "@octokit/core",
+        "@octokit/oauth-app",
+        "@octokit/plugin-paginate-graphql",
+        "@octokit/plugin-paginate-rest",
+        "@octokit/plugin-rest-endpoint-methods",
+        "@octokit/plugin-retry",
+        "@octokit/plugin-throttling",
+        "@octokit/request-error",
+        "@octokit/types"
+      ]
+    },
+    "onetime@7.0.0": {
+      "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+      "dependencies": [
+        "mimic-function"
+      ]
+    },
+    "ora@8.2.0": {
+      "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==",
+      "dependencies": [
+        "chalk",
+        "cli-cursor",
+        "cli-spinners",
+        "is-interactive",
+        "is-unicode-supported@2.1.0",
+        "log-symbols@6.0.0",
+        "stdin-discarder",
+        "string-width@7.2.0",
+        "strip-ansi@7.1.0"
+      ]
+    },
+    "parse-ms@3.0.0": {
+      "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw=="
+    },
+    "parse-ms@4.0.0": {
+      "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="
+    },
+    "path-key@3.1.1": {
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
+    },
+    "pretty-bytes@6.1.1": {
+      "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="
+    },
+    "pretty-ms@8.0.0": {
+      "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==",
+      "dependencies": [
+        "parse-ms@3.0.0"
+      ]
+    },
+    "pretty-ms@9.2.0": {
+      "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==",
+      "dependencies": [
+        "parse-ms@4.0.0"
+      ]
+    },
+    "proper-lockfile@4.1.2": {
+      "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+      "dependencies": [
+        "graceful-fs",
+        "retry@0.12.0",
+        "signal-exit@3.0.7"
+      ]
+    },
+    "proxy-from-env@1.1.0": {
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "rc@1.2.8": {
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+      "dependencies": [
+        "deep-extend",
+        "ini",
+        "minimist",
+        "strip-json-comments"
+      ]
+    },
+    "readable-stream@3.6.2": {
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": [
+        "inherits",
+        "string_decoder",
+        "util-deprecate"
+      ]
+    },
+    "require-directory@2.1.1": {
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+    },
+    "restore-cursor@5.1.0": {
+      "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+      "dependencies": [
+        "onetime",
+        "signal-exit@4.1.0"
+      ]
+    },
+    "retry@0.12.0": {
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="
+    },
+    "retry@0.13.1": {
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
+    },
+    "safe-buffer@5.2.1": {
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+    },
+    "semver@7.7.1": {
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="
+    },
+    "set-blocking@2.0.0": {
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+    },
+    "shebang-command@2.0.0": {
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dependencies": [
+        "shebang-regex"
+      ]
+    },
+    "shebang-regex@3.0.0": {
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
+    },
+    "signal-exit@3.0.7": {
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+    },
+    "signal-exit@4.1.0": {
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
+    },
+    "simple-git@3.27.0": {
+      "integrity": "sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==",
+      "dependencies": [
+        "@kwsites/file-exists",
+        "@kwsites/promise-deferred",
+        "debug"
+      ]
+    },
+    "sleep-promise@9.1.0": {
+      "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA=="
+    },
+    "slice-ansi@7.1.0": {
+      "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+      "dependencies": [
+        "ansi-styles@6.2.1",
+        "is-fullwidth-code-point@5.0.0"
+      ]
+    },
+    "stdin-discarder@0.2.2": {
+      "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="
+    },
+    "stdout-update@4.0.1": {
+      "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==",
+      "dependencies": [
+        "ansi-escapes",
+        "ansi-styles@6.2.1",
+        "string-width@7.2.0",
+        "strip-ansi@7.1.0"
+      ]
+    },
+    "steno@4.0.2": {
+      "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A=="
+    },
+    "string-width@4.2.3": {
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dependencies": [
+        "emoji-regex@8.0.0",
+        "is-fullwidth-code-point@3.0.0",
+        "strip-ansi@6.0.1"
+      ]
+    },
+    "string-width@7.2.0": {
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dependencies": [
+        "emoji-regex@10.4.0",
+        "get-east-asian-width",
+        "strip-ansi@7.1.0"
+      ]
+    },
+    "string_decoder@1.3.0": {
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dependencies": [
+        "safe-buffer"
+      ]
+    },
+    "strip-ansi@6.0.1": {
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dependencies": [
+        "ansi-regex@5.0.1"
+      ]
+    },
+    "strip-ansi@7.1.0": {
+      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+      "dependencies": [
+        "ansi-regex@6.1.0"
+      ]
+    },
+    "strip-json-comments@2.0.1": {
+      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+    },
+    "tar@6.2.1": {
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+      "dependencies": [
+        "chownr",
+        "fs-minipass",
+        "minipass@5.0.0",
+        "minizlib",
+        "mkdirp",
+        "yallist"
+      ]
+    },
+    "toad-cache@3.7.0": {
+      "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw=="
+    },
+    "undici-types@6.20.0": {
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
+    },
+    "universal-github-app-jwt@2.2.0": {
+      "integrity": "sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ=="
+    },
+    "universal-user-agent@7.0.2": {
+      "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
+    },
+    "universalify@2.0.1": {
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="
+    },
+    "url-join@4.0.1": {
+      "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
+    },
+    "util-deprecate@1.0.2": {
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "validate-npm-package-name@6.0.0": {
+      "integrity": "sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg=="
+    },
+    "which@2.0.2": {
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dependencies": [
+        "isexe@2.0.0"
+      ]
+    },
+    "which@5.0.0": {
+      "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+      "dependencies": [
+        "isexe@3.1.1"
+      ]
+    },
+    "wide-align@1.1.5": {
+      "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+      "dependencies": [
+        "string-width@4.2.3"
+      ]
+    },
+    "wrap-ansi@7.0.0": {
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dependencies": [
+        "ansi-styles@4.3.0",
+        "string-width@4.2.3",
+        "strip-ansi@6.0.1"
+      ]
+    },
+    "y18n@5.0.8": {
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+    },
+    "yallist@4.0.0": {
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    },
+    "yargs-parser@21.1.1": {
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+    },
+    "yargs@17.7.2": {
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dependencies": [
+        "cliui",
+        "escalade",
+        "get-caller-file",
+        "require-directory",
+        "string-width@4.2.3",
+        "y18n",
+        "yargs-parser"
+      ]
+    },
+    "yoctocolors@2.1.1": {
+      "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="
+    }
+  },
+  "redirects": {
+    "https://esm.sh/@octokit/app@^15.1.2?target=denonext": "https://esm.sh/@octokit/app@15.1.2?target=denonext",
+    "https://esm.sh/@octokit/auth-app@^7.1.4?target=denonext": "https://esm.sh/@octokit/auth-app@7.1.4?target=denonext",
+    "https://esm.sh/@octokit/auth-oauth-app@^8.1.2?target=denonext": "https://esm.sh/@octokit/auth-oauth-app@8.1.2?target=denonext",
+    "https://esm.sh/@octokit/auth-oauth-device@^7.1.2?target=denonext": "https://esm.sh/@octokit/auth-oauth-device@7.1.2?target=denonext",
+    "https://esm.sh/@octokit/auth-oauth-user@^5.1.2?target=denonext": "https://esm.sh/@octokit/auth-oauth-user@5.1.2?target=denonext",
+    "https://esm.sh/@octokit/auth-token@^5.0.0?target=denonext": "https://esm.sh/@octokit/auth-token@5.1.2?target=denonext",
+    "https://esm.sh/@octokit/auth-unauthenticated@^6.1.1?target=denonext": "https://esm.sh/@octokit/auth-unauthenticated@6.1.1?target=denonext",
+    "https://esm.sh/@octokit/core@^6.1.3?target=denonext": "https://esm.sh/@octokit/core@6.1.3?target=denonext",
+    "https://esm.sh/@octokit/endpoint@^10.0.0?target=denonext": "https://esm.sh/@octokit/endpoint@10.1.2?target=denonext",
+    "https://esm.sh/@octokit/graphql@^8.1.2?target=denonext": "https://esm.sh/@octokit/graphql@8.2.0?target=denonext",
+    "https://esm.sh/@octokit/oauth-app@^7.1.4?target=denonext": "https://esm.sh/@octokit/oauth-app@7.1.5?target=denonext",
+    "https://esm.sh/@octokit/oauth-app@^7.1.5?target=denonext": "https://esm.sh/@octokit/oauth-app@7.1.5?target=denonext",
+    "https://esm.sh/@octokit/oauth-authorization-url@^7.0.0?target=denonext": "https://esm.sh/@octokit/oauth-authorization-url@7.1.1?target=denonext",
+    "https://esm.sh/@octokit/oauth-methods@^5.1.2?target=denonext": "https://esm.sh/@octokit/oauth-methods@5.1.3?target=denonext",
+    "https://esm.sh/@octokit/oauth-methods@^5.1.3?target=denonext": "https://esm.sh/@octokit/oauth-methods@5.1.3?target=denonext",
+    "https://esm.sh/@octokit/plugin-paginate-graphql@^5.2.4?target=denonext": "https://esm.sh/@octokit/plugin-paginate-graphql@5.2.4?target=denonext",
+    "https://esm.sh/@octokit/plugin-paginate-rest@^11.3.6?target=denonext": "https://esm.sh/@octokit/plugin-paginate-rest@11.4.0?target=denonext",
+    "https://esm.sh/@octokit/plugin-paginate-rest@^11.4.0?target=denonext": "https://esm.sh/@octokit/plugin-paginate-rest@11.4.0?target=denonext",
+    "https://esm.sh/@octokit/plugin-rest-endpoint-methods@^13.3.0?target=denonext": "https://esm.sh/@octokit/plugin-rest-endpoint-methods@13.3.0?target=denonext",
+    "https://esm.sh/@octokit/plugin-retry@^7.1.3?target=denonext": "https://esm.sh/@octokit/plugin-retry@7.1.3?target=denonext",
+    "https://esm.sh/@octokit/plugin-throttling@^9.4.0?target=denonext": "https://esm.sh/@octokit/plugin-throttling@9.4.0?target=denonext",
+    "https://esm.sh/@octokit/request-error@^6.0.1?target=denonext": "https://esm.sh/@octokit/request-error@6.1.6?target=denonext",
+    "https://esm.sh/@octokit/request-error@^6.1.6?target=denonext": "https://esm.sh/@octokit/request-error@6.1.6?target=denonext",
+    "https://esm.sh/@octokit/request@^9.1.4?target=denonext": "https://esm.sh/@octokit/request@9.2.0?target=denonext",
+    "https://esm.sh/@octokit/webhooks-methods@^5.0.0?target=denonext": "https://esm.sh/@octokit/webhooks-methods@5.1.0?target=denonext",
+    "https://esm.sh/@octokit/webhooks@^13.4.2?target=denonext": "https://esm.sh/@octokit/webhooks@13.5.0?target=denonext",
+    "https://esm.sh/before-after-hook@^3.0.2?target=denonext": "https://esm.sh/before-after-hook@3.0.2?target=denonext",
+    "https://esm.sh/bottleneck@^2.15.3/light?target=denonext": "https://esm.sh/bottleneck@2.19.5/light?target=denonext",
+    "https://esm.sh/fast-content-type-parse@^2.0.0?target=denonext": "https://esm.sh/fast-content-type-parse@2.0.1?target=denonext",
+    "https://esm.sh/octokit?dts": "https://esm.sh/octokit@4.1.0?dts",
+    "https://esm.sh/toad-cache@^3.7.0?target=denonext": "https://esm.sh/toad-cache@3.7.0?target=denonext",
+    "https://esm.sh/universal-github-app-jwt@^2.2.0?target=denonext": "https://esm.sh/universal-github-app-jwt@2.2.0?target=denonext",
+    "https://esm.sh/universal-user-agent@^7.0.0?target=denonext": "https://esm.sh/universal-user-agent@7.0.2?target=denonext",
+    "https://esm.sh/universal-user-agent@^7.0.2?target=denonext": "https://esm.sh/universal-user-agent@7.0.2?target=denonext"
+  },
+  "remote": {
+    "https://esm.sh/@octokit/app@15.1.2/denonext/app.mjs": "21c4fdfecfc4540b17c21b3c0cf1e0a5c7eb55bab034978af33d7dd7a06ab4ab",
+    "https://esm.sh/@octokit/app@15.1.2?target=denonext": "51ad31b108532bc255a43647574518580d5e95d5ad7a968df7bb477b71b59e79",
+    "https://esm.sh/@octokit/auth-app@7.1.4/denonext/auth-app.mjs": "c2c8bc96d4760e5a0af7535c1aa669ebc8b490764b575d8113632515f11760c6",
+    "https://esm.sh/@octokit/auth-app@7.1.4?target=denonext": "a4632ee3c6af2cfde6e6249c13d5d7706575649ac570389f6117bd37eab0b523",
+    "https://esm.sh/@octokit/auth-oauth-app@8.1.2/denonext/auth-oauth-app.mjs": "1803a9d024ec1d9b53c9fdc5d386d05990ee6b6715d2ad79619f8452fd9d5d13",
+    "https://esm.sh/@octokit/auth-oauth-app@8.1.2?target=denonext": "8c48ca11c1b9e2d08c56555fa61b13cb5050366b3e0412ceb19a1e314f355015",
+    "https://esm.sh/@octokit/auth-oauth-device@7.1.2/denonext/auth-oauth-device.mjs": "24ac890f2a933a5532ea37f9e94b1185c24dfd163339300fce36a132f2ccd6d3",
+    "https://esm.sh/@octokit/auth-oauth-device@7.1.2?target=denonext": "f2d03bf3a4208a0cabd8e4b85c98d633f857678e811bf6de4429d1d9439da0eb",
+    "https://esm.sh/@octokit/auth-oauth-user@5.1.2/denonext/auth-oauth-user.mjs": "4f1ce58465e50c7f79d58794976da1853f9785bf5434b19d81a50c400a9ed858",
+    "https://esm.sh/@octokit/auth-oauth-user@5.1.2?target=denonext": "54d806f7a2b19086b880d27cbbf863985c9f9f26914c031a99ad849a85cdbcbe",
+    "https://esm.sh/@octokit/auth-token@5.1.2/denonext/auth-token.mjs": "360feb2cb940bfa2e20ba6e5b7c366b207e7b8cb3a2ff983f3d33b19087cdc50",
+    "https://esm.sh/@octokit/auth-token@5.1.2?target=denonext": "0e5dc846d0c00da36580f08e0d4115526daf6256acf07fabedc06716dfbf72c9",
+    "https://esm.sh/@octokit/auth-unauthenticated@6.1.1/denonext/auth-unauthenticated.mjs": "1cd86628013b6444922993cd740ed0a327dd220ac465595ccad05de8fb2f80f8",
+    "https://esm.sh/@octokit/auth-unauthenticated@6.1.1?target=denonext": "a00a18f165e48e1d98e6d5e4266aee4a0bde051b7342c54ebd838d2547538ae4",
+    "https://esm.sh/@octokit/core@6.1.3/denonext/core.mjs": "27711bac5522d962bca6513035a3ab2e55c001f49e050175bb67bbedd4049535",
+    "https://esm.sh/@octokit/core@6.1.3?target=denonext": "48e4022a4e8ae6df3eec7b9028bfc452b113f1746c81737861b06d44c6791f03",
+    "https://esm.sh/@octokit/endpoint@10.1.2/denonext/endpoint.mjs": "10aa9cb55330d89ef4392f710c7f1132b30d3018d1a548af90d50837ad8d8640",
+    "https://esm.sh/@octokit/endpoint@10.1.2?target=denonext": "e44433015f71eb9ebf694f7e66880a96505b503e0d6f172c63267259cf7810e7",
+    "https://esm.sh/@octokit/graphql@8.2.0/denonext/graphql.mjs": "a26719a9e0adfc72773caaca5857a67ddf54b7824efd0f4585c4a14bba66a566",
+    "https://esm.sh/@octokit/graphql@8.2.0?target=denonext": "f1f248a1461a1c8a230e0d45dd6979a21205c29941ee790ef9d779da05259305",
+    "https://esm.sh/@octokit/oauth-app@7.1.5/denonext/oauth-app.mjs": "946907b1ce34610a973305985d4861e2377b83cb463dcd1724ffb917af8188c9",
+    "https://esm.sh/@octokit/oauth-app@7.1.5?target=denonext": "373b5044d98bccffa8aa0dc180c9f013a5e6ae95eba0b4481feb7204458d4c1b",
+    "https://esm.sh/@octokit/oauth-authorization-url@7.1.1/denonext/oauth-authorization-url.mjs": "209de15074485c12e6bffaed0f33d857c789df4a577b19ddbd78caa9646b77dc",
+    "https://esm.sh/@octokit/oauth-authorization-url@7.1.1?target=denonext": "fdaed10ede57c5e5a3ae67b487331336916d5401d366bed1425515e104b95de1",
+    "https://esm.sh/@octokit/oauth-methods@5.1.3/denonext/oauth-methods.mjs": "9043d6ed33f54207cab9966eb6f984f87e62c5c4ed3e019aa3b28d02ea738eac",
+    "https://esm.sh/@octokit/oauth-methods@5.1.3?target=denonext": "d185bf52b6cd01f4d13fcf25ca7bce03e8534b06835c0ad6783366c6676311a6",
+    "https://esm.sh/@octokit/plugin-paginate-graphql@5.2.4/denonext/plugin-paginate-graphql.mjs": "a2b0869cb5eb1e14516d1d2ba227e2b968afe05ed9b3895f005ac196e7b5c5a1",
+    "https://esm.sh/@octokit/plugin-paginate-graphql@5.2.4?target=denonext": "1512bade9daddb1ffe8fc46693db5dd3df429d51264e6848e3de3ea0cd16bc61",
+    "https://esm.sh/@octokit/plugin-paginate-rest@11.4.0/denonext/plugin-paginate-rest.mjs": "3bb02e7f514dee857103ceb9d922621bed80115bfe0fb1d7197a7a7cd44cfa6f",
+    "https://esm.sh/@octokit/plugin-paginate-rest@11.4.0?target=denonext": "b4e0d949bdd6a34257cddde0262ca67e82a158aba5bbfeb00f54e6939770c17b",
+    "https://esm.sh/@octokit/plugin-rest-endpoint-methods@13.3.0/denonext/plugin-rest-endpoint-methods.mjs": "8254da50e1460596143fb2d49314f97fc0b9bae60d0259594c3a6b98bca5c266",
+    "https://esm.sh/@octokit/plugin-rest-endpoint-methods@13.3.0?target=denonext": "b54fb93130dea1a56de1d434e6c900793ab05281c0086129cbd60411a07039bd",
+    "https://esm.sh/@octokit/plugin-retry@7.1.3/denonext/plugin-retry.mjs": "b55a731984d30e256309ef0ad6eca64d34ea4cc61c747f711b7c45b0e4e1b086",
+    "https://esm.sh/@octokit/plugin-retry@7.1.3?target=denonext": "5f0e94e59f4a593ef911c8c10796a73d9ef5289505c129a647f90fdf3405d833",
+    "https://esm.sh/@octokit/plugin-throttling@9.4.0/denonext/plugin-throttling.mjs": "50c4a67edc2bba79d81f5ea8966175a3acffbffe3403b5815da172e0c12ab686",
+    "https://esm.sh/@octokit/plugin-throttling@9.4.0?target=denonext": "d40a7eb26c4897bb17e4cddd341a25f85ea0607798d5ee3e48014693f07e98b5",
+    "https://esm.sh/@octokit/request-error@6.1.6/denonext/request-error.mjs": "a3bb74761fbdc7b1fbc406f0a2ace10f82f64706ffc9596a90badef765414c5e",
+    "https://esm.sh/@octokit/request-error@6.1.6?target=denonext": "bfebd513aea83d3df72e2f0b70f9e2dcd7aafed463c095f7388ca3dd10621e7d",
+    "https://esm.sh/@octokit/request@9.2.0/denonext/request.mjs": "662411deed3a6c54a960c802b24795bc8ce65649e0a941ba848fa6f68e7d0070",
+    "https://esm.sh/@octokit/request@9.2.0?target=denonext": "550f4054c2e0eba7174dd21aa9117e6934f3bb3d3b3c43f6d4a1471cef3a04d9",
+    "https://esm.sh/@octokit/webhooks-methods@5.1.0/denonext/webhooks-methods.mjs": "fdaefa3e104afcf645fb3a35a1bb557c31e7d069342f1518df72fd5662c28932",
+    "https://esm.sh/@octokit/webhooks-methods@5.1.0?target=denonext": "5a77eef071b9134be9a6ed9358b553195aae275b36e7b7e7c146f0b90d1baa92",
+    "https://esm.sh/@octokit/webhooks@13.5.0/denonext/webhooks.mjs": "b94ff89a92ac22f1cd0307314d976fa5a34a0a731d7e79bf8962201fb3ecd013",
+    "https://esm.sh/@octokit/webhooks@13.5.0?target=denonext": "6e3bc47e2034b2421d1c5e4cf652aa0a330c887aa69c6bf704923eda42722ae7",
+    "https://esm.sh/before-after-hook@3.0.2/denonext/before-after-hook.mjs": "1a862839e15e4816de946c11023cbec3cb1d276e3a86073adfaac9dca6871aca",
+    "https://esm.sh/before-after-hook@3.0.2?target=denonext": "923592fc40b4609eac335800f88c890d81c5f5f12e0b22fd8fd0646313c634f7",
+    "https://esm.sh/bottleneck@2.19.5/denonext/light.mjs": "943b72d08b33e678a4f64bf2ce9a0468f9356ee65092af9c893fa25d3be6518b",
+    "https://esm.sh/bottleneck@2.19.5/light?target=denonext": "c53ee9007926e5a49e291e0a0f2e1113735d65271a33eb609ac9ad309def4878",
+    "https://esm.sh/fast-content-type-parse@2.0.1/denonext/fast-content-type-parse.mjs": "436d0ff6ce4508efcb9023892d25cc9d7eb321e9321a0d3aaf870300dc2e8578",
+    "https://esm.sh/fast-content-type-parse@2.0.1?target=denonext": "7170ebd0186887d73afc67050a28e5350171e5e842e82a10309e829eabdf138a",
+    "https://esm.sh/octokit@4.1.0/denonext/octokit.mjs": "0e522b2239c7f7d673022e81dd55b2dcb6ce42b905f1cfc82a5dfbbd2c24af28",
+    "https://esm.sh/octokit@4.1.0?dts": "094de1c868d2aaa8be65d7135d3feeee8006a5e2d76683f5bfcc32243557cdfc",
+    "https://esm.sh/toad-cache@3.7.0/denonext/toad-cache.mjs": "63b78525dcc99d8f736de244e46b306b2b5f6c72db75bf1d230230d46a06ab37",
+    "https://esm.sh/toad-cache@3.7.0?target=denonext": "e17c2f2729b3268a6ecb412b975002ebaa66894c2e5e0a657a38d1176a1f02d4",
+    "https://esm.sh/universal-github-app-jwt@2.2.0/denonext/universal-github-app-jwt.mjs": "c7cb04f796b43f163b1bcaf2b85c742d11ae724ae5ea82dbe517617c33bd0467",
+    "https://esm.sh/universal-github-app-jwt@2.2.0?target=denonext": "75d402511284eb488f2f2c0f39cc5122ec91c3de1a18eebe1360f2935a0e577a",
+    "https://esm.sh/universal-user-agent@7.0.2/denonext/universal-user-agent.mjs": "c5370728870841e1061776d12f5776929bb04945b0f8d5e4251207eab57d35ea",
+    "https://esm.sh/universal-user-agent@7.0.2?target=denonext": "1c7589a3ed835be8bf13e6c78c7d507af65b7945b92edbf3ad25428e60e072f4"
+  },
+  "workspace": {
+    "dependencies": [
+      "jsr:@eta-dev/eta@^3.5.0",
+      "jsr:@logtape/logtape@~0.8.1",
+      "jsr:@std/assert@1",
+      "jsr:@std/cli@^1.0.12",
+      "jsr:@std/collections@^1.0.10",
+      "jsr:@std/dotenv@~0.225.3",
+      "jsr:@std/path@^1.0.8",
+      "npm:@types/node@^22.13.9",
+      "npm:jmespath@0.16",
+      "npm:lodash-es@^4.17.21",
+      "npm:node-llama-cpp@^3.6.0",
+      "npm:octokit@^4.1.0"
+    ]
+  }
+}
diff --git a/src/msg-handlers/base-handler.ts b/src/msg-handlers/base-handler.ts
new file mode 100644
index 0000000..e4fa0b1
--- /dev/null
+++ b/src/msg-handlers/base-handler.ts
@@ -0,0 +1,41 @@
+import { EventEmitter } from "node:events"
+import {Bot, Message, User} from "../snek/snek-socket.ts"
+import { trim } from "npm:lodash-es"
+
+export abstract class BaseHandler extends EventEmitter {
+  prefix = "msg-handler"
+  user: User|null = null
+
+  constructor(prefix: string) {
+    super()
+    this.prefix = prefix.toLowerCase()
+  }
+
+  async bind(bot: Bot) {
+    this.user = await bot.user
+    bot.on("message", async (message) => {
+      try {
+        if (await this.isMatch(message)) {
+          try {
+            await this.handleMessage(message, bot)
+          } catch (e) {
+            message.reply("An error occurred while handling your message")
+            console.error("Error handling message", e)
+          }
+        }
+      } catch (e) {
+        console.error("Error checking message", e)
+      }
+    })
+  }
+
+  async isMatch(message: Message): Promise<boolean> {
+    return message.userUID !== this.user?.uid &&
+      trim(message?.message, ' `').toLowerCase().startsWith(this.prefix)
+  }
+
+  abstract handleMessage(
+    message: Message,
+    bot: Bot,
+  ): Promise<void> | void
+}
diff --git a/src/msg-handlers/img-gen-handler.ts b/src/msg-handlers/img-gen-handler.ts
new file mode 100644
index 0000000..03b07b8
--- /dev/null
+++ b/src/msg-handlers/img-gen-handler.ts
@@ -0,0 +1,187 @@
+import {Bot, Message} from "../snek/snek-socket.ts"
+import {BaseHandler} from "./base-handler.ts"
+import {ImgGen} from "../util/img-gen.ts"
+import {Eta} from "jsr:@eta-dev/eta"
+import { getLogger } from "@logtape/logtape"
+import * as path from "node:path"
+import {randomUUID} from "node:crypto";
+
+const TEMPLATES = new Eta({ views: path.join(Deno.cwd(), "templates/img-gen") })
+
+const FORMATTING_WRAPPER = /^\s*`(?:``\w+)?(.*?)(?:``)?`\s*$/gs
+
+const logger = getLogger(["img-gen-handler"])
+
+const parsePrompt = (prompt: string): Record<string, unknown> => {
+  try {
+    return JSON.parse(prompt)
+  } catch (e) {
+    prompt = prompt.replace(FORMATTING_WRAPPER, "$1")
+    return JSON.parse(prompt)
+  }
+}
+
+export class ImgGenHandler extends BaseHandler {
+  #imageGenerator = new ImgGen()
+  #activeTemplate = "flux-dev.eta"
+  #templateVariables: Record<string, unknown> = {
+    steps: 20,
+    seed: 0,
+    cfg: 2,
+    sampler: "euler",
+    negativePrompt: "",
+    batchSize: 1,
+    width: 1024,
+    height: 1024,
+  }
+
+  #subCommands = {
+    "prompt": this.prompt.bind(this),
+    "template": this.template.bind(this),
+    "variables": this.variables.bind(this),
+  } as Record<string, (command: string, message: Message, bot: Bot) => void>
+
+  constructor() {
+    super("/img-gen")
+  }
+
+  override async handleMessage(message: Message, bot: Bot) {
+    const newMessage = message.message.substring(this.prefix.length).trim()
+    if (!newMessage) {
+      message.reply(
+        "No command given, try one of: " +
+          Object.keys(this.#subCommands).join(", "),
+      )
+      return
+    }
+
+    const [[_, command, rest]] = newMessage.matchAll(/^(\w+)\s*(.*)$/gs)
+
+    if (command in this.#subCommands) {
+      this.#subCommands[command]?.(rest.trim(), message, bot)
+      return
+    }
+
+    message.reply(
+      "Invalid command, assuming prompt. Otherwise these are the correct ones: " +
+        Object.keys(this.#subCommands).join(", "),
+    )
+
+    await this.prompt(newMessage, message, bot)
+  }
+
+  async prompt(command: string, message: Message, bot: Bot) {
+    if (!command) {
+      message.reply("No prompt given")
+      return
+    }
+    try {
+      logger.info("Generating image", { command, variables: this.#templateVariables, message })
+
+      const prompt = parsePrompt(
+        await TEMPLATES.renderAsync(this.#activeTemplate, {
+          ...this.#templateVariables,
+          randomSeed: (
+            min: number = 0,
+            max: number = Number.MAX_SAFE_INTEGER,
+          ) => Math.floor(Math.random() * (max - min + 1)) + min,
+          prompt: command.replaceAll("\n", " "),
+        }),
+      )
+
+      message.reply("image generated called")
+
+      const promptResults = await this.#imageGenerator.dispatchPrompt(
+        prompt,
+      )
+
+      const blob = promptResults.filter((result) => result instanceof Blob)
+
+      if (blob.length === 0) {
+        console.log("Prompt Results: ", promptResults)
+        message.reply("Failed to generate image")
+        return
+      }
+
+      const files = await Promise.all(
+        blob.map(async (blob) =>
+          new File([(await blob.arrayBuffer()).slice(8)], `${randomUUID()}.png`)
+        ),
+      )
+
+      await bot.uploadFiles(message.channelUID, ...files)
+    } catch (e) {
+      console.error(e)
+      message.reply(`Failed to generate image: ${e.message}`)
+    }
+  }
+
+  async template(command: string, message: Message, bot: Bot) {
+    if (!command) {
+      const templatePath = TEMPLATES.config.views
+      if (templatePath) {
+        const templateOptions = await Deno.readDir(templatePath)
+        let templateList =
+          `Current template: ${this.#activeTemplate}\n\nTemplates:\n`
+        for await (const template of templateOptions) {
+          templateList += ` - ${template.name}\n`
+        }
+        message.reply(templateList)
+      } else {
+        message.reply(`Current template: ${this.#activeTemplate}`)
+        return
+      }
+      return
+    }
+
+    const [[_, template, rest]] = command.matchAll(/^(\S+)\s*(.*)$/gs)
+
+    if (!template) {
+      message.reply("No template given")
+      return
+    }
+
+    if (template.startsWith("@")) {
+      TEMPLATES.loadTemplate(template, rest, { async: true })
+      this.#activeTemplate = template
+      message.reply(`Template set to ${template}`)
+    } else {
+      try {
+        Deno.readFileSync(TEMPLATES.resolvePath(template))
+        this.#activeTemplate = template
+        message.reply(`Template set to ${template}`)
+      } catch (e) {
+        message.reply(`Failed to load template: ${e.message}`)
+        return
+      }
+    }
+  }
+
+  variables(command: string, message: Message, bot: Bot) {
+    if (!command) {
+      let currentVariables = "Current Variables:\n\n```json\n"
+      currentVariables += JSON.stringify(this.#templateVariables, null, 2)
+      currentVariables += "\n```"
+      message.reply(currentVariables)
+
+      return
+    }
+
+    const [[_, variable, value]] = command.matchAll(/^(\S+)\s*(.*)$/gs)
+
+    if (!variable) {
+      message.reply("No variable given")
+      return
+    }
+
+    if (!value) {
+      message.reply(
+        `Variable ${variable} = ${this.#templateVariables[variable]}`,
+      )
+      return
+    }
+
+    this.#templateVariables[variable] = value
+    message.reply(`Variable set: ${variable} = ${value}`)
+  }
+}
diff --git a/src/msg-handlers/llama-handler.ts b/src/msg-handlers/llama-handler.ts
new file mode 100644
index 0000000..7223df5
--- /dev/null
+++ b/src/msg-handlers/llama-handler.ts
@@ -0,0 +1,212 @@
+import {BaseHandler} from "./base-handler.ts"
+import {Bot, Message} from "../snek/snek-socket.ts"
+import {trim, trimStart} from "npm:lodash-es"
+import {
+    ChatSessionModelFunctions,
+    ChatWrapper,
+    GeneralChatWrapper,
+    getLlama,
+    LLamaChatPromptOptions,
+    LlamaChatSession,
+    LlamaContext,
+    LlamaModel,
+    resolveChatWrapper,
+    Token,
+} from "npm:node-llama-cpp"
+import {getLogger} from "@logtape/logtape"
+import {deepMerge} from "@std/collections/deep-merge"
+
+const llama = await getLlama()
+
+const textEncoder = new TextEncoder()
+
+function printSync(input: string | Uint8Array, to = Deno.stdout) {
+  let bytesWritten = 0
+  const bytes = typeof input === "string" ? textEncoder.encode(input) : input
+  while (bytesWritten < bytes.length) {
+    bytesWritten += to.writeSync(bytes.subarray(bytesWritten))
+  }
+}
+const logger = getLogger(["llama-gen-handler"])
+
+const optionsGenerator = <
+  const Functions extends ChatSessionModelFunctions | undefined = undefined,
+    LLamaOptions = LLamaChatPromptOptions<Functions>
+>(
+  model: LlamaModel,
+  debugOutput: boolean = true,
+  defaultTimeout = 5 * 60 * 1000,
+  options?: LLamaOptions,
+): LLamaChatPromptOptions<Functions> => {
+  const manager = AbortSignal.timeout(defaultTimeout)
+
+  const defaultOptions: LLamaChatPromptOptions<Functions> = {
+    repeatPenalty: {
+      lastTokens: 24,
+      penalty: 1.12,
+      penalizeNewLine: true,
+      frequencyPenalty: 0.02,
+      presencePenalty: 0.02,
+      punishTokensFilter: (tokens: Token[]) => {
+        return tokens.filter((token) => {
+          const text = model.detokenize([token])
+
+          // allow the model to repeat tokens
+          // that contain the word "better"
+          return !text.toLowerCase().includes("@")
+           // TODO: Exclude usernames
+        })
+      },
+    },
+    temperature: 0.6,
+
+    signal: manager,
+    stopOnAbortSignal: true,
+  }
+
+  if (debugOutput) {
+    defaultOptions.onResponseChunk = (chunk) => {
+      const isThoughtSegment = chunk.type === "segment" &&
+        chunk.segmentType === "thought"
+
+      if (
+        chunk.type === "segment" && chunk.segmentStartTime != null
+      ) {
+        printSync(` [segment start: ${chunk.segmentType}] `)
+      }
+
+      printSync(chunk.text)
+
+      if (chunk.type === "segment" && chunk.segmentEndTime != null) {
+        printSync(` [segment end: ${chunk.segmentType}] `)
+      }
+    }
+  }
+
+  return deepMerge(defaultOptions, options)
+}
+
+export class LLamaHandler extends BaseHandler {
+  joinMode = false
+  debugLogResponses = true
+
+  systemPrompt: string
+
+  #activeModel: string
+
+  #model: LlamaModel | null = null
+  #context: LlamaContext | null = null
+
+  #chatWrapper: ChatWrapper | null = null
+
+  #session: LlamaChatSession | null = null
+
+  constructor(
+    activeModel: string,
+    systemPrompt: string = "You are an AI chatbot.",
+  ) {
+    super("")
+    this.#activeModel = activeModel
+    this.systemPrompt = systemPrompt
+  }
+
+  async calculateSystemPrompt(): Promise<string> {
+    return this.systemPrompt
+  }
+
+  override async bind(bot: Bot): Promise<void> {
+    await super.bind(bot)
+    this.prefix = "@" + this.user?.username.toLowerCase()
+
+    this.#model = await llama.loadModel({
+      modelPath: this.#activeModel,
+      defaultContextFlashAttention: true,
+    })
+
+    this.#context = await this.#model.createContext({
+      flashAttention: true,
+    })
+
+    logger.info("Model loaded", {
+      batchSize: this.#context.batchSize,
+      contextSize: this.#context.contextSize,
+    })
+    console.log("Model loaded", {
+      batchSize: this.#context.batchSize,
+      contextSize: this.#context.contextSize,
+    })
+
+    this.#chatWrapper = //new Llama3ChatWrapper()
+      resolveChatWrapper({
+        bosString: this.#model
+          .tokens
+          .bosString,
+        filename: this.#model
+          .filename,
+        fileInfo: this.#model
+          .fileInfo,
+        tokenizer: this.#model
+          .tokenizer,
+      }) ?? new GeneralChatWrapper()
+
+    this.#session = new LlamaChatSession({
+      contextSequence: this.#context.getSequence(),
+      chatWrapper: this.#chatWrapper,
+      systemPrompt: await this.calculateSystemPrompt(),
+    })
+
+    const channels = await bot.channels
+
+    const channel = channels.find((v) => v.tag === "public") || channels[0]
+
+    if (channel) {
+      await bot.sendMessage(
+        channel.uid,
+        await this.#session.prompt(
+          "Welcome to chat, greet everyone\n",
+          optionsGenerator(this.#model, this.debugLogResponses),
+        ),
+      )
+      this.#session.resetChatHistory()
+    }
+  }
+
+  override async isMatch(message: Message): Promise<boolean> {
+    return message.userUID !== this.user?.uid && (this.joinMode ||
+      trim(message?.message, " `").toLowerCase().includes(this.prefix))
+  }
+
+  override async handleMessage(message: Message, bot: Bot): Promise<void> {
+    const session = this.#session
+    const user = this.user
+    if (!session || !user) {
+      return
+    }
+
+    let response = await session.prompt(
+      `@${message.username}: ${message.message}`,
+      optionsGenerator(this.#model!, this.debugLogResponses),
+    )
+
+    response = response.replace(/<think>.*?<\/think>/gs, "")
+    let lwResponse = response.toLowerCase()
+
+    if (lwResponse.startsWith("ai")) {
+      response = response.substring(2).trim()
+      lwResponse = response.toLowerCase()
+    }
+
+    if (lwResponse.startsWith(`@${user.username.toLowerCase()}`)) {
+      response = response.substring(user.username.length + 1).trim()
+      lwResponse = response.toLowerCase()
+    }
+
+    if (lwResponse.startsWith(`@${message.username.toLowerCase()}`)) {
+      response = response.substring(message.username.length + 1).trim()
+      lwResponse = response.toLowerCase()
+    }
+
+    response = trimStart(response, ":").trim()
+    bot.send("send_message", message.channelUID, response)
+  }
+}
diff --git a/src/msg-handlers/ping-handler.ts b/src/msg-handlers/ping-handler.ts
new file mode 100644
index 0000000..1ce3914
--- /dev/null
+++ b/src/msg-handlers/ping-handler.ts
@@ -0,0 +1,12 @@
+import { BaseHandler } from "./base-handler.ts"
+import { Bot, Message } from "../snek/snek-socket.ts"
+
+export class PingHandler extends BaseHandler {
+  constructor() {
+    super("ping")
+  }
+
+  override handleMessage(message: Message, bot: Bot): void {
+    message.reply("pong")
+  }
+}
diff --git a/src/snek/snek-socket.ts b/src/snek/snek-socket.ts
new file mode 100644
index 0000000..7d51e27
--- /dev/null
+++ b/src/snek/snek-socket.ts
@@ -0,0 +1,295 @@
+import {EventEmitter} from "node:events"
+import {debounce} from "npm:lodash-es"
+import {getLogger} from "@logtape/logtape"
+
+const logger = getLogger(["ws-socket"])
+
+export const BASE_URL = "https://snek.molodetz.nl/login.json"
+
+const baseRequest = async (
+  action: string,
+  username: string,
+  password: string,
+) => {
+  const loginPayloadRed = await fetch(BASE_URL)
+  console.log(loginPayloadRed)
+  const basePayload = await loginPayloadRed.json()
+
+  basePayload.fields.username.value = username
+  basePayload.fields.password.value = password
+
+  return await fetch(BASE_URL, {
+    method: "POST",
+    headers: {
+      "Content-Type": "application/json",
+    },
+    body: JSON.stringify({
+      action,
+      form: basePayload,
+    }),
+  })
+}
+
+export const login = async (username: string, password: string) => {
+  const loginResult = await baseRequest("submit", username, password)
+
+  const loginPayload = await loginResult.json()
+  if (!("redirect_url" in loginPayload)) {
+    return null
+  }
+  const cookies = loginResult.headers.getSetCookie()
+  const authCookie =
+    cookies.filter((v) => v.includes("AIOHTTP_SESSION")).flatMap((v) => {
+      try {
+        return v.matchAll(
+          /(AIOHTTP_SESSION="?.*?"?)(?:;|^)/g,
+        )?.next()?.value?.[1]
+      } catch (e) {
+        return undefined
+      }
+    })[0]
+
+  return authCookie || null
+}
+
+export const validate = async (username: string, password: string) => {
+  return await baseRequest("validate", username, password)
+}
+
+export interface User {
+  color: string
+  created_at: string
+  email?: string | null
+  last_ping: string | null
+  nick: string
+  uid: string
+  updated_at: string
+  username: string
+}
+
+export interface Channel {
+  name: string
+  uid: string
+  is_moderator: boolean
+  is_read_only: boolean
+  tag: string
+}
+
+export class Message {
+  message: string
+  html: string
+  userUID: string
+  color: string
+  channelUID: string
+  createdAt: string
+  updatedAt: string | null
+  username: string
+  uid: string
+  userNick: string
+
+  bot: Bot
+
+  constructor(data: any, bot: Bot) {
+    this.message = data.message
+    this.html = data.html
+    this.userUID = data.user_uid
+    this.color = data.color
+    this.channelUID = data.channel_uid
+    this.createdAt = data.created_at
+    this.updatedAt = data.updated_at
+    this.username = data.username
+    this.uid = data.uid
+    this.userNick = data.user_nick
+    this.bot = bot
+  }
+
+  reply(message: string) {
+    return this.bot.send("send_message", this.channelUID, message)
+  }
+}
+
+export class ConnectionClosedError extends Error {
+  constructor(code: number, reason: string) {
+    super(`Connection closed with code ${code}: ${reason}`)
+  }
+}
+
+export class Bot extends EventEmitter<
+  { message: Message[]; close: { code: number; reason: string }[] }
+> {
+  readonly #username: string
+  readonly #password: string
+  readonly #url: string | URL
+  #ws: WebSocket | null = null
+
+  #processingMessages = new Map<
+    string,
+    {
+      data: PromiseWithResolvers<unknown>
+      when: Date
+      req: {
+        name: string
+        args: unknown[]
+        callId: string
+      }
+    }
+  >()
+
+  autoReconnect = true
+
+  get channels() {
+    return this.send<Channel[]>("get_channels")
+  }
+
+  get user() {
+    return this.send<User>("get_user", null)
+  }
+  get messages() {
+    return this.send("get_messages", null)
+  }
+
+  #authCookie: string | null = null
+
+  get authCookie() {
+    return this.#authCookie
+      ? Promise.resolve(this.#authCookie)
+      : login(this.#username, this.#password).then((
+        cookie,
+      ) => (this.#authCookie = cookie))
+  }
+
+  constructor(
+    username: string,
+    password: string,
+    url: string | URL = "wss://snek.molodetz.nl/rpc.ws",
+  ) {
+    super()
+    this.#username = username
+    this.#password = password
+    this.#url = url
+  }
+
+  send<T>(name: string, ...args: unknown[]) {
+    const ws = this.#ws
+    if (ws && ws.readyState === ws.OPEN) {
+      const callId = Math.random().toString(36).slice(2)
+      logger.debug("Sending message", { name, args, callId })
+      const res = Promise.withResolvers<T>()
+      this.#processingMessages.set(callId, {
+        data: res as PromiseWithResolvers<unknown>,
+        when: new Date(),
+        req: { name, args, callId },
+      })
+      ws.send(JSON.stringify({
+        method: name,
+        args: args,
+        callId,
+      }))
+
+      return res.promise.catch((e: unknown): typeof res.promise => {
+        logger.error("Error sending message, retrying", {
+          name,
+          args,
+          callId,
+          e,
+        })
+
+        return new Promise((resolve, reject) => {
+          setTimeout(() => {
+            resolve(this.connect().then(() => this.send(name, ...args)))
+          }, 1000)
+        })
+      })
+    }
+    return Promise.reject(new Error("Connection not open"))
+  }
+
+  receive(data: string) {
+    const parsedData = JSON.parse(data)
+    const callId = parsedData.callId
+
+    const message = this.#processingMessages.get(callId)
+    if (message) {
+      logger.debug("Resolving message", {
+        data: parsedData.data,
+        callId,
+        raw: parsedData,
+      })
+      message.data.resolve(parsedData.data)
+      this.#processingMessages.delete(callId)
+    } else {
+      logger.debug("Emitting message", parsedData)
+      this.emit("message", new Message(parsedData, this))
+    }
+  }
+
+  connect = debounce(
+    function (this: Bot) {
+      if (this.#ws) {
+        return this.send("get_user", null)
+      }
+      logger.debug("Connecting to", { url: this.#url })
+      const connectedPromise = Promise.withResolvers<unknown>()
+
+      this.#ws = new WebSocket(this.#url)
+      this.#ws.onopen = () => {
+        logger.debug("Connected")
+        connectedPromise.resolve(
+          this.send("login", this.#username, this.#password),
+        )
+      }
+      this.#ws.onmessage = (event) => {
+        logger.debug("Received message", { data: event.data })
+        this.receive(event.data)
+      }
+
+      this.#ws.onclose = (event) => {
+        logger.warn("Connection closed", {
+          code: event.code,
+          reason: event.reason,
+        })
+
+        this.#ws = null
+        this.emit("close", { code: event.code, reason: event.reason })
+        this.#processingMessages.forEach((message) => {
+          message.data.reject(
+            new ConnectionClosedError(event.code, event.reason),
+          )
+        })
+        this.#processingMessages.clear()
+
+        if (this.autoReconnect) {
+          this.connect()
+        }
+      }
+
+      this.#ws.onerror = (event) => {
+        logger.error("Connection error", { event })
+      }
+
+      return connectedPromise.promise
+    },
+    100,
+    { leading: true, trailing: false },
+  )
+
+  sendMessage(channelUID: string, message: string) {
+    return this.send("send_message", channelUID, message)
+  }
+
+  async uploadFiles(channelUID: string, ...files: File[]) {
+    const imageForm = new FormData()
+    imageForm.append("channel_uid", channelUID)
+    files.forEach((file) => {
+      imageForm.append("files[]", file)
+    })
+
+    return await fetch("https://snek.molodetz.nl/drive.bin", {
+      method: "POST",
+      body: imageForm,
+      headers: {
+        "Cookie": await this.authCookie,
+      },
+    })
+  }
+}
diff --git a/src/util/img-gen.ts b/src/util/img-gen.ts
new file mode 100644
index 0000000..bf454ab
--- /dev/null
+++ b/src/util/img-gen.ts
@@ -0,0 +1,133 @@
+import {randomUUID} from "node:crypto"
+import {getLogger} from "@logtape/logtape"
+
+const logger = getLogger(["img-gen"])
+export class ImgGen {
+  #host: string
+  #clientId = randomUUID()
+  #ws: WebSocket
+
+  #promptQueue = new Map<
+    string,
+    { promise: PromiseWithResolvers<unknown[]>; msgs: unknown[] }
+  >()
+
+  constructor(host: string = "127.0.0.1:8188") {
+    this.#host = host
+    this.#initiateWebSocket()
+  }
+
+  async queuePrompt(prompt: string) {
+    const response = await fetch(`http://${this.#host}/prompt`, {
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify({ prompt, client_id: this.#clientId }),
+    })
+    return await response.json()
+  }
+
+  async getImage(filename: string, subfolder: string, folderType: string) {
+    const response = await fetch(
+      `http://${this.#host}/view?${
+        new URLSearchParams({ filename, subfolder, type: folderType })
+          .toString()
+      }`,
+    )
+    return await response.arrayBuffer()
+  }
+
+  async getHistory(promptId: string) {
+    const response = await fetch(`http://${this.#host}/history/${promptId}`)
+    return await response.json()
+  }
+
+  #initiateWebSocket() {
+    if (
+      !this.#ws || this.#ws.readyState === this.#ws.CLOSED ||
+      this.#ws.readyState === this.#ws.CLOSING
+    ) {
+      const res = Promise.withResolvers<void>()
+      this.#ws = new WebSocket(
+        `ws://${this.#host}/ws?clientId=${this.#clientId}`,
+      )
+      let lastExportId: string | null = null
+      this.#ws.addEventListener("message", (event) => {
+        if (typeof event.data === "string") {
+          console.log("Received message", event, event.data)
+          const data = JSON.parse(event.data)
+          const res = this.#promptQueue.get(data.data.prompt_id)
+          if (res) {
+            res.msgs.push(data)
+            if (data.type === "execution_success") {
+              res.promise.resolve(res.msgs)
+              this.#promptQueue.delete(data.data.prompt_id)
+            } else if (data.type === "executing") {
+              if (data.data.node === "save_image_websocket_node") {
+                lastExportId = data.data.prompt_id
+              }
+            }
+          }
+        } else {
+          console.error(
+            "Received img message",
+            event,
+            "assuming is part of",
+            lastExportId,
+          )
+          if (lastExportId) {
+            const res = this.#promptQueue.get(lastExportId)
+            if (res) {
+              res.msgs.push(event.data)
+            }
+          }
+        }
+      })
+      this.#ws.addEventListener("open", () => {
+        res.resolve()
+      }, { once: true })
+
+      this.#ws.addEventListener("close", () => {
+        logger.error("WebSocket closed")
+
+        for (const [_, { promise }] of this.#promptQueue) {
+          promise.reject(new Error("WebSocket closed"))
+        }
+
+        this.#promptQueue.clear()
+
+        this.#ws = null
+        this.#initiateWebSocket()
+      })
+
+      this.#ws.addEventListener("error", (e) => {
+        logger.error("WebSocket error", {e})
+      })
+
+      return res.promise
+    } else if (this.#ws.readyState === this.#ws.CONNECTING) {
+      return new Promise<void>((resolve) => {
+        this.#ws.addEventListener("open", () => {
+          resolve()
+        }, { once: true })
+      })
+    }
+
+    return Promise.resolve()
+  }
+
+  async dispatchPrompt(prompt: Record<string, unknown> | string) {
+    const res = Promise.withResolvers<unknown[]>()
+
+    await this.#initiateWebSocket()
+
+    if (this.#ws.readyState !== this.#ws.OPEN) {
+      return Promise.reject(new Error("WebSocket not open"))
+    }
+
+    const promptData = await this.queuePrompt(prompt as string)
+    console.log("Prompt data", promptData)
+    this.#promptQueue.set(promptData.prompt_id, { promise: res, msgs: [] })
+
+    return res.promise
+  }
+}
diff --git a/src/util/logging.ts b/src/util/logging.ts
new file mode 100644
index 0000000..5e06077
--- /dev/null
+++ b/src/util/logging.ts
@@ -0,0 +1,25 @@
+import {configure, getConsoleSink} from "@logtape/logtape";
+
+await configure({
+  sinks: {
+    console: getConsoleSink(
+      {
+        formatter: (logEvent) => {
+          const { timestamp, level, category, message, properties } = logEvent
+          return `${timestamp} [${level.toUpperCase()}] [${category}] ${message} ${
+            properties && Object.keys(properties).length
+              ? JSON.stringify(properties)
+              : ""
+          }`
+        },
+      },
+    ),
+  },
+  loggers: [
+    { category: "ai-app", lowestLevel: "debug", sinks: ["console"] },
+    { category: "img-gen", lowestLevel: "debug", sinks: ["console"] },
+    { category: "img-gen-handler", lowestLevel: "debug", sinks: ["console"] },
+    { category: "llama-gen-handler", lowestLevel: "debug", sinks: ["console"] },
+    { category: "ws-socket", lowestLevel: "debug", sinks: ["console"] },
+  ],
+})
diff --git a/src/ws-snek-image-bot.ts b/src/ws-snek-image-bot.ts
new file mode 100644
index 0000000..af2e83e
--- /dev/null
+++ b/src/ws-snek-image-bot.ts
@@ -0,0 +1,19 @@
+import "@std/dotenv/load"
+import "./util/logging.ts"
+import {Bot} from "./snek/snek-socket.ts"
+import {PingHandler} from "./msg-handlers/ping-handler.ts";
+import {ImgGenHandler} from "./msg-handlers/img-gen-handler.ts";
+
+const bot = new Bot(
+  Deno.env.get("SNEK_USERNAME")!,
+  Deno.env.get("SNEK_PASSWORD")!,
+)
+
+await bot.connect()
+
+const user = await bot.user
+
+console.log("We are user: ", user, await bot.authCookie)
+
+new PingHandler().bind(bot)
+new ImgGenHandler().bind(bot)
diff --git a/src/ws-snek-llama-bot.ts b/src/ws-snek-llama-bot.ts
new file mode 100644
index 0000000..78b170c
--- /dev/null
+++ b/src/ws-snek-llama-bot.ts
@@ -0,0 +1,24 @@
+import "@std/dotenv/load"
+import "./util/logging.ts"
+import {Bot} from "./snek/snek-socket.ts"
+import {PingHandler} from "./msg-handlers/ping-handler.ts";
+import {LLamaHandler} from "./msg-handlers/llama-handler.ts";
+
+const bot = new Bot(
+  Deno.env.get("SNEK_USERNAME")!,
+  Deno.env.get("SNEK_PASSWORD")!,
+)
+
+await bot.connect()
+
+const user = await bot.user
+
+console.log("We are user: ", user, await bot.authCookie)
+
+await Promise.all([
+  new PingHandler().bind(bot),
+  new LLamaHandler(
+    Deno.env.get("SNEK_LLAMA_MODEL")!,
+    Deno.env.get("SNEK_LLAMA_SYSTEM_PROMPT")!,
+  ).bind(bot),
+])
diff --git a/templates/img-gen/SD3.5-large-turbo.eta b/templates/img-gen/SD3.5-large-turbo.eta
new file mode 100644
index 0000000..123822f
--- /dev/null
+++ b/templates/img-gen/SD3.5-large-turbo.eta
@@ -0,0 +1,199 @@
+{
+  "4": {
+    "inputs": {
+      "ckpt_name": "stableDiffusion35_largeTurbo.safetensors"
+    },
+    "class_type": "CheckpointLoaderSimple",
+    "_meta": {
+      "title": "Load Checkpoint"
+    }
+  },
+  "6": {
+    "inputs": {
+      "text": "<%= it.prompt %>",
+      "clip": [
+        "11",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "8": {
+    "inputs": {
+      "samples": [
+        "294",
+        0
+      ],
+      "vae": [
+        "4",
+        2
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "11": {
+    "inputs": {
+      "clip_name1": "clip_g.safetensors",
+      "clip_name2": "clip_l.safetensors",
+      "clip_name3": "t5xxl_fp16.safetensors"
+    },
+    "class_type": "TripleCLIPLoader",
+    "_meta": {
+      "title": "TripleCLIPLoader"
+    }
+  },
+  "13": {
+    "inputs": {
+      "shift": 3,
+      "model": [
+        "4",
+        0
+      ]
+    },
+    "class_type": "ModelSamplingSD3",
+    "_meta": {
+      "title": "ModelSamplingSD3"
+    }
+  },
+  "67": {
+    "inputs": {
+      "conditioning": [
+        "71",
+        0
+      ]
+    },
+    "class_type": "ConditioningZeroOut",
+    "_meta": {
+      "title": "ConditioningZeroOut"
+    }
+  },
+  "68": {
+    "inputs": {
+      "start": 0.1,
+      "end": 1,
+      "conditioning": [
+        "67",
+        0
+      ]
+    },
+    "class_type": "ConditioningSetTimestepRange",
+    "_meta": {
+      "title": "ConditioningSetTimestepRange"
+    }
+  },
+  "69": {
+    "inputs": {
+      "conditioning_1": [
+        "68",
+        0
+      ],
+      "conditioning_2": [
+        "70",
+        0
+      ]
+    },
+    "class_type": "ConditioningCombine",
+    "_meta": {
+      "title": "Conditioning (Combine)"
+    }
+  },
+  "70": {
+    "inputs": {
+      "start": 0,
+      "end": 0.1,
+      "conditioning": [
+        "71",
+        0
+      ]
+    },
+    "class_type": "ConditioningSetTimestepRange",
+    "_meta": {
+      "title": "ConditioningSetTimestepRange"
+    }
+  },
+  "71": {
+    "inputs": {
+      "text": "<%= it.negativePrompt ||'' %>",
+      "clip": [
+        "11",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "135": {
+    "inputs": {
+      "width": <%= it.width || 1024 %>,
+      "height": <%= it.height || 1024 %>,
+      "batch_size": <%= it.batchSize || 1 %>
+    },
+    "class_type": "EmptySD3LatentImage",
+    "_meta": {
+      "title": "EmptySD3LatentImage"
+    }
+  },
+  "294": {
+    "inputs": {
+      "seed": <%= it.seed || it.randomSeed() %>,
+      "steps":  <%= it.steps || 2 %>,
+      "cfg": <%= it.cfg || 1 %>,
+      "sampler_name": "<%= it.sampler || "euler" %>",
+      "scheduler": "beta",
+      "denoise": 1,
+      "model": [
+        "13",
+        0
+      ],
+      "positive": [
+        "6",
+        0
+      ],
+      "negative": [
+        "69",
+        0
+      ],
+      "latent_image": [
+        "135",
+        0
+      ]
+    },
+    "class_type": "KSampler",
+    "_meta": {
+      "title": "KSampler"
+    }
+  },
+  "301": {
+    "inputs": {
+      "filename_prefix": "ComfyUI",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImage",
+    "_meta": {
+      "title": "Save Image"
+    }
+  },
+  "save_image_websocket_node": {
+    "inputs": {
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImageWebsocket",
+    "_meta": {
+      "title": "SaveImageWebsocket"
+    }
+  }
+}
\ No newline at end of file
diff --git a/templates/img-gen/SD3.5-large.eta b/templates/img-gen/SD3.5-large.eta
new file mode 100644
index 0000000..27c21c5
--- /dev/null
+++ b/templates/img-gen/SD3.5-large.eta
@@ -0,0 +1,199 @@
+{
+  "4": {
+    "inputs": {
+      "ckpt_name": "stableDiffusion35_large.safetensors"
+    },
+    "class_type": "CheckpointLoaderSimple",
+    "_meta": {
+      "title": "Load Checkpoint"
+    }
+  },
+  "6": {
+    "inputs": {
+      "text": "<%= it.prompt %>",
+      "clip": [
+        "11",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "8": {
+    "inputs": {
+      "samples": [
+        "294",
+        0
+      ],
+      "vae": [
+        "4",
+        2
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "11": {
+    "inputs": {
+      "clip_name1": "clip_g.safetensors",
+      "clip_name2": "clip_l.safetensors",
+      "clip_name3": "t5xxl_fp16.safetensors"
+    },
+    "class_type": "TripleCLIPLoader",
+    "_meta": {
+      "title": "TripleCLIPLoader"
+    }
+  },
+  "13": {
+    "inputs": {
+      "shift": 3,
+      "model": [
+        "4",
+        0
+      ]
+    },
+    "class_type": "ModelSamplingSD3",
+    "_meta": {
+      "title": "ModelSamplingSD3"
+    }
+  },
+  "67": {
+    "inputs": {
+      "conditioning": [
+        "71",
+        0
+      ]
+    },
+    "class_type": "ConditioningZeroOut",
+    "_meta": {
+      "title": "ConditioningZeroOut"
+    }
+  },
+  "68": {
+    "inputs": {
+      "start": 0.1,
+      "end": 1,
+      "conditioning": [
+        "67",
+        0
+      ]
+    },
+    "class_type": "ConditioningSetTimestepRange",
+    "_meta": {
+      "title": "ConditioningSetTimestepRange"
+    }
+  },
+  "69": {
+    "inputs": {
+      "conditioning_1": [
+        "68",
+        0
+      ],
+      "conditioning_2": [
+        "70",
+        0
+      ]
+    },
+    "class_type": "ConditioningCombine",
+    "_meta": {
+      "title": "Conditioning (Combine)"
+    }
+  },
+  "70": {
+    "inputs": {
+      "start": 0,
+      "end": 0.1,
+      "conditioning": [
+        "71",
+        0
+      ]
+    },
+    "class_type": "ConditioningSetTimestepRange",
+    "_meta": {
+      "title": "ConditioningSetTimestepRange"
+    }
+  },
+  "71": {
+    "inputs": {
+      "text": "<%= it.negativePrompt ||'' %>",
+      "clip": [
+        "11",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "135": {
+    "inputs": {
+      "width": <%= it.width || 1024 %>,
+      "height": <%= it.height || 1024 %>,
+      "batch_size": <%= it.batchSize || 1 %>
+    },
+    "class_type": "EmptySD3LatentImage",
+    "_meta": {
+      "title": "EmptySD3LatentImage"
+    }
+  },
+  "294": {
+    "inputs": {
+      "seed": <%= it.seed || it.randomSeed() %>,
+      "steps":  <%= it.steps || 2 %>,
+      "cfg": <%= it.cfg || 1 %>,
+      "sampler_name": "<%= it.sampler || "euler" %>",
+      "scheduler": "beta",
+      "denoise": 1,
+      "model": [
+        "13",
+        0
+      ],
+      "positive": [
+        "6",
+        0
+      ],
+      "negative": [
+        "69",
+        0
+      ],
+      "latent_image": [
+        "135",
+        0
+      ]
+    },
+    "class_type": "KSampler",
+    "_meta": {
+      "title": "KSampler"
+    }
+  },
+  "301": {
+    "inputs": {
+      "filename_prefix": "ComfyUI",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImage",
+    "_meta": {
+      "title": "Save Image"
+    }
+  },
+  "save_image_websocket_node": {
+    "inputs": {
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImageWebsocket",
+    "_meta": {
+      "title": "SaveImageWebsocket"
+    }
+  }
+}
\ No newline at end of file
diff --git a/templates/img-gen/flux-dev.eta b/templates/img-gen/flux-dev.eta
new file mode 100644
index 0000000..5cb7547
--- /dev/null
+++ b/templates/img-gen/flux-dev.eta
@@ -0,0 +1,204 @@
+{
+  "5": {
+    "inputs": {
+      "width": <%= it.width || 1024 %>,
+      "height": <%= it.height || 1024 %>,
+      "batch_size": <%= it.batchSize || 1 %>
+    },
+    "class_type": "EmptyLatentImage",
+    "_meta": {
+      "title": "Empty Latent Image"
+    }
+  },
+  "6": {
+    "inputs": {
+      "text": "<%= it.prompt ||'' %>",
+      "clip": [
+        "11",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "8": {
+    "inputs": {
+      "samples": [
+        "13",
+        0
+      ],
+      "vae": [
+        "10",
+        0
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "9": {
+    "inputs": {
+      "filename_prefix": "MarkuryFLUX",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImage",
+    "_meta": {
+      "title": "Save Image"
+    }
+  },
+  "10": {
+    "inputs": {
+      "vae_name": "ae.safetensors"
+    },
+    "class_type": "VAELoader",
+    "_meta": {
+      "title": "Load VAE"
+    }
+  },
+  "11": {
+    "inputs": {
+      "clip_name1": "t5xxl_fp16.safetensors",
+      "clip_name2": "clip_l.safetensors",
+      "type": "flux",
+      "device": "default"
+    },
+    "class_type": "DualCLIPLoader",
+    "_meta": {
+      "title": "DualCLIPLoader"
+    }
+  },
+  "12": {
+    "inputs": {
+      "unet_name": "flux_dev.safetensors",
+      "weight_dtype": "fp8_e4m3fn"
+    },
+    "class_type": "UNETLoader",
+    "_meta": {
+      "title": "Load Diffusion Model"
+    }
+  },
+  "13": {
+    "inputs": {
+      "noise": [
+        "25",
+        0
+      ],
+      "guider": [
+        "22",
+        0
+      ],
+      "sampler": [
+        "16",
+        0
+      ],
+      "sigmas": [
+        "17",
+        0
+      ],
+      "latent_image": [
+        "5",
+        0
+      ]
+    },
+    "class_type": "SamplerCustomAdvanced",
+    "_meta": {
+      "title": "SamplerCustomAdvanced"
+    }
+  },
+  "16": {
+    "inputs": {
+      "sampler_name": "<%= it.sampler || "euler" %>"
+    },
+    "class_type": "KSamplerSelect",
+    "_meta": {
+      "title": "KSamplerSelect"
+    }
+  },
+  "17": {
+    "inputs": {
+      "scheduler": "beta",
+      "steps": <%= it.steps || 30 %>,
+      "denoise": 1,
+      "model": [
+        "61",
+        0
+      ]
+    },
+    "class_type": "BasicScheduler",
+    "_meta": {
+      "title": "BasicScheduler"
+    }
+  },
+  "22": {
+    "inputs": {
+      "model": [
+        "61",
+        0
+      ],
+      "conditioning": [
+        "60",
+        0
+      ]
+    },
+    "class_type": "BasicGuider",
+    "_meta": {
+      "title": "BasicGuider"
+    }
+  },
+  "25": {
+    "inputs": {
+      "noise_seed": <%= it.seed || it.randomSeed() %>
+    },
+    "class_type": "RandomNoise",
+    "_meta": {
+      "title": "RandomNoise"
+    }
+  },
+  "60": {
+    "inputs": {
+      "guidance": 4,
+      "conditioning": [
+        "6",
+        0
+      ]
+    },
+    "class_type": "FluxGuidance",
+    "_meta": {
+      "title": "FluxGuidance"
+    }
+  },
+  "61": {
+    "inputs": {
+      "max_shift": 1.15,
+      "base_shift": 0.5,
+      "width": <%= it.width || 1024 %>,
+      "height": <%= it.height || 1024 %>,
+      "model": [
+        "12",
+        0
+      ]
+    },
+    "class_type": "ModelSamplingFlux",
+    "_meta": {
+      "title": "ModelSamplingFlux"
+    }
+  },
+  "save_image_websocket_node": {
+    "inputs": {
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImageWebsocket",
+    "_meta": {
+      "title": "SaveImageWebsocket"
+    }
+  }
+}
\ No newline at end of file
diff --git a/templates/img-gen/mochi.eta b/templates/img-gen/mochi.eta
new file mode 100644
index 0000000..e9ccb31
--- /dev/null
+++ b/templates/img-gen/mochi.eta
@@ -0,0 +1,149 @@
+{
+  "3": {
+    "inputs": {
+      "seed": <%= it.seed || it.randomSeed() %>,
+      "steps": <%= it.steps || 30 %>,
+      "cfg": <%= it.cfg || 4.5 %>,
+      "sampler_name": "<%= it.sampler || "euler" %>",
+      "scheduler": "beta",
+      "denoise": 1,
+      "model": [
+        "37",
+        0
+      ],
+      "positive": [
+        "6",
+        0
+      ],
+      "negative": [
+        "7",
+        0
+      ],
+      "latent_image": [
+        "21",
+        0
+      ]
+    },
+    "class_type": "KSampler",
+    "_meta": {
+      "title": "KSampler"
+    }
+  },
+  "6": {
+    "inputs": {
+      "text": "<%= it.prompt %>",
+      "clip": [
+        "38",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "7": {
+    "inputs": {
+      "text": "<%= it.negativePrompt || '' %>",
+      "clip": [
+        "38",
+        0
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "8": {
+    "inputs": {
+      "samples": [
+        "3",
+        0
+      ],
+      "vae": [
+        "39",
+        0
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "21": {
+    "inputs": {
+      "width": <%= it.width || 848 %>,
+      "height": <%= it.height || 480 %>,
+      "length": <%= it.frames || 37 %>,
+      "batch_size": <%= it.batchSize || 1 %>
+    },
+    "class_type": "EmptyMochiLatentVideo",
+    "_meta": {
+      "title": "EmptyMochiLatentVideo"
+    }
+  },
+  "28": {
+    "inputs": {
+      "filename_prefix": "ComfyUI",
+      "fps": 24,
+      "lossless": false,
+      "quality": 80,
+      "method": "default",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveAnimatedWEBP",
+    "_meta": {
+      "title": "SaveAnimatedWEBP"
+    }
+  },
+  "37": {
+    "inputs": {
+      "unet_name": "mochi_preview_bf16.safetensors",
+      "weight_dtype": "default"
+    },
+    "class_type": "UNETLoader",
+    "_meta": {
+      "title": "Load Diffusion Model"
+    }
+  },
+  "38": {
+    "inputs": {
+      "clip_name": "t5xxl_fp16.safetensors",
+      "type": "mochi",
+      "device": "default"
+    },
+    "class_type": "CLIPLoader",
+    "_meta": {
+      "title": "Load CLIP"
+    }
+  },
+  "39": {
+    "inputs": {
+      "vae_name": "mochi_vae.safetensors"
+    },
+    "class_type": "VAELoader",
+    "_meta": {
+      "title": "Load VAE"
+    }
+  },
+  "save_image_websocket_node": {
+    "inputs": {
+      "fps": 24,
+      "lossless": false,
+      "quality": 80,
+      "method": "default",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveAnimatedWEBPWebsocket",
+    "_meta": {
+      "title": "SaveAnimatedWEBPWebsocket"
+    }
+  }
+}
\ No newline at end of file